Java:禁止在外部类修改ArrayList

5
我在我的Java项目的一个类中使用了ArrayList。该类跟踪列表是否已更改,并提供公共方法来添加和删除元素,这些方法会自动将变量设置为“已更改”。
到目前为止,列表是公共的,因为我希望我的列表可以从任何地方公开读取。但是,我只想让拥有该列表的类能够修改它。因此,不允许从外部类进行修改。这是否可能?如果可能,怎么做?
通常,为了进行访问控制,您可能会使用getter和setter方法。但即使使用getter方法并将列表设置为私有,另一个类仍然可以执行getList().remove(element),从而在外部修改列表,而该类并没有注意到列表已被修改。

仅通过公共函数提供所有所需功能,完全封装内部数据结构。因此,请编写自己的删除函数。 - SBI
5个回答

3
将您的ArrayList字段设置为私有,但让getter返回Collections.unmodifiableList(list),这将提供一个不可修改的视图。此举可以使外部代码将其视为正常的List,并使用for each循环等操作,但会禁用修改操作。此外,unmodifiableList在常数时间内返回一个视图。这是它被设计的完美情况。Javadoc如下所示:

该方法允许模块向用户提供对内部列表的“只读”访问权限。返回列表上的查询操作“通过”到指定的列表,并且尝试修改返回的列表(无论是直接还是通过其迭代器)都会导致UnsupportedOperationException。


2
使您的列表私有化并添加getter方法:
public List getList(){
    return new ArrayList(yourPrivateList);
}

1
你可以将 ArrayList 成员变量设为私有的,然后定义一个 getter 方法,接收一个索引 i 并返回 ArrayList 中第 i 个元素。
public class Test
{
    private List<String> list = new ArrayList<String>();

    public getString (int i)
    {
        // you might want to add some validation of i here
        return list.get(i);
    }

}

getString 允许您的类的用户访问列表中的任何元素,而不会修改它。

如果您想允许用户在不修改列表的情况下迭代列表,则可以添加一个 getSize() 方法来返回列表的大小(这将允许用户使用常规 for 循环迭代列表),或者您的类可以实现 Iterable (不支持 remove 操作)。


1
我认为在这里最好的选择是将您的List保持私有,并添加一个getter方法来返回List的副本,但不包括List本身。例如:
public class EncapsulationTest {

    private List<Object> privateList = new ArrayList<Object>();

    // Your constructors and methods to track list
    // modification here ...

    public List<Object> getList() {
        // Maybe you need a null check here
        return new ArrayList<Object>(privateList);
    }

    public void addElement(Object newElement) {
        this.privateList.add(newElement);
        // Set your 'changed' variable to true
    }

    public void removeElement(Object element) {
        this.privateList.remove(element);
        // Set your 'changed' variable to true
    }
}

如果你这样做,仍然可以阅读List完全副本,但无法修改List本身。实际上,你可以修改返回的List,但由于它是一个不同的对象,所以更改不会影响你的对象的List
我希望这有所帮助。

Thrustmaster在他的回答中提到返回克隆是昂贵的。我正在使用列表进行启发式,其中运行时最为重要,对这些(大型)列表的迭代频繁发生。 - CGFoX

1
您有几个选择:
  1. 返回 ArrayList 的 getter 方法可以在返回对象之前进行克隆。这样,即使外部实体修改了对象,它们最终会修改克隆而不是原始对象。注意:克隆操作可能很昂贵。我建议使用以下选项。
  2. 使用 Collections.unmodifiableList(..)查看此处的文档。
  3. 或者像其他答案建议的那样,编写自己的访问和迭代方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接