简体   繁体   English

为什么要使集合不可修改?

[英]Why make a collection unmodifiable?

I know how to make a collection unmodifiable in java but I dont understand the need for such a method to exist. 我知道如何在Java中使集合不可修改,但我不了解存在这种方法的必要性。 Can someone please explain this with an example scenario where I would want to make my collection unmodifiable? 有人可以用我想使我的收藏集无法修改的示例场景来对此进行解释吗?

Thanks in advance 提前致谢

The most efficient way to share private data outside of a class is to simply return it. 在类外共享私有数据的最有效方法是简单地返回它。 But then something outside of the class can change the data that the class depends on. 但是,类之外的某些东西可以更改类所依赖的数据。 Another option is to copy the data before you share. 另一种选择是在共享之前复制数据。 This takes time and memory to do. 这需要时间和记忆。 Unmodifiable collections will often wrap the data and simply present it without allowing an outside class to edit it. 不可修改的集合通常会包装数据并简单地将其呈现,而不允许外部类对其进行编辑。 This is faster than making a copy. 这比制作副本要快。 An outside class can optionally make a modifiable copy if it needs. 外部类可以根据需要选择制作可修改的副本。

An unmodifiable collection is basically read-only which is exactly what you want in case you need to publish such collection to client code and you don't want the client code to modify the collection. 不可修改的集合基本上是只读的,如果您需要此类集合发布到客户端代码并且您不希望客户端代码修改该集合,这正是您想要的。

It also promotes immutability which is generally a good thing since you won't have to care about the state of the collection for the rest of the execution of your program. 它还会促进不变性,这通常是一件好事,因为在程序的其余执行过程中,您不必关心集合的状态。 See item 15 of Effective Java (2nd Edition) : Minimize mutability , and to quote Joshua Bloch : 请参阅有效Java(第二版)的第15项: 最小化可变性 ,并引用Joshua Bloch的观点:

Immutable objects are simple. 不变的对象很简单。 An immutable object can be in exactly one state, the state in which it was created. 一个不可变的对象可以恰好处于一种状态,即创建该对象的状态。

Note that an unmodifiable collection will not make the contained objects immutable. 请注意,不可修改的集合不会使所包含的对象不可变。 This is a property each of the contained objects needs to make sure of, if it is required of course. 如果需要的话,这是每个包含的对象都需要确保的属性。

Take a look at this scenario. 看一下这种情况。 There is an application that creates 2 users, and then wants to notify them about something. 有一个应用程序可以创建2个用户,然后将其通知给他们。 But only users with name different from Peter should get the notification. 但是只有名称与Peter不同的用户才可以收到通知。

So we have to User.class: 因此我们必须使用User.class:

public class User {
    private String name;
    private Integer id;

    public User(final Integer id, final String name) {
        this.id = id;
        this.name = name;
    }
    public String getName() {
        return name;
    }

    public Integer getId() {
        return id;
    }
}

The users are stored in special holder class (containing map): 用户存储在特殊的持有人类别(包含地图)中:

public class UsersHolder {
    private static Map<Integer, User> usersMap = new HashMap<Integer, User>();

    public static void addUser(final User user) {
        usersMap.put(user.getId(), user);
    }

    public static Map<Integer, User> getUsersMap() {
        return usersMap;
        //return Collections.unmodifiableMap(usersMap);
    }
}

Then we have the UsersCreator that creates those users and stores them in a map: 然后,我们有创建这些用户并将其存储在地图中的UsersCreator:

public class UsersCreator {
    public static void createUsers() {
        UsersHolder.addUser(new User(1, "Peter"));
        System.out.println("Created user " + UsersHolder.getUsersMap().get(1).getName());
        UsersHolder.addUser(new User(2, "Paul"));
        System.out.println("Created user " + UsersHolder.getUsersMap().get(2).getName());
    }

    public static void main(String[] args) {
        UsersCreator.createUsers();
        System.out.println("Number of users before notification: " + UsersHolder.getUsersMap().size());
        new UsersNotificator().notifyAllUsersButPeters(UsersHolder.getUsersMap());
        System.out.println("Number of users after notification: " + UsersHolder.getUsersMap().size());
    }
}

And the notificator that notifies all but Peters: 通知除彼得斯以外的所有人的通知者:

public class UsersNotificator {
    public void notifyAllUsersButPeters(final Map<Integer, User> map) {
        //we don't need peters, so we'll remove them from the list;
        Iterator<Entry<Integer, User>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            if (iterator.next().getValue().getName().equals("Peter")) {
                iterator.remove();
            }
        }
        //now we can notify all from the list;
        notifyUsers(UsersHolder.getUsersMap());
    }

    private void notifyUsers(Map<Integer, User> map) {
        for (final User user : map.values())
        System.out.println("notifyingUsers: " + user.getName());
    }
}

Now - the notificator was presented with a map and it may modify it, which it does. 现在-向通知者提供了一张地图,它可能会对其进行修改。 It doesn't know that it shouldn't modify it as it's global usersMap. 它不知道它不应该修改它,因为它是全局用户地图。 In effect it removes all users with name Peter. 实际上,它将删除名称为Peter的所有用户。 It does it for it's own purposes, but the results will be visible for every other class using UsersHolder. 它是出于自己的目的进行的,但是使用UsersHolder的所有其他类的结果都是可见的。

The result is as follows: 结果如下:

Created user Peter
Created user Paul
Number of users before notification: 2
notifyingUsers: Paul
Number of users after notification: 1

When returning unmodifiableMap in UsersHolder the removal will not be possible. 当在UsersHolder中返回unmodifiableableMap时,将无法删除。 The only way would be to create new map with users to notify, so our usersHolder is safe. 唯一的方法是与用户一起创建新地图以进行通知,因此我们的usersHolder是安全的。

This example is a bit big, sorry for that, i failed to think of/create somehting shorter. 这个例子有点大,对此我感到抱歉,我没想到/创造的更短。

Unmodifiable map helps to keep your classes Immutable which is safe(as presented in the example) especially in multithreaded enviroment. 不可修改的映射有助于使您的类保持不变,这是安全的(如示例中所示),尤其是在多线程环境中。

There are many situations in which you do not want your collection to be modifiable. 在很多情况下,您都不希望您的收藏集可以修改。 Whenever you know that the collection is initialized with exactly the content it should contain at all times, it can provide security to make it unmodifiable. 每当您知道集合始终使用其应包含的确切内容初始化时,它就可以提供安全性以使其不可修改。

The (rather long) example provided by another user is a good example of where it often causes problems. 另一个用户提供的(相当长的)示例是一个经常导致问题的好示例。 Whenever you traverse a collection, there is a risk you change the collection if you forget to do it on a copy. 每当您遍历一个集合时,如果忘记在副本上进行更改,都有可能更改集合。 Making the collection unmodifiable catches and prevents this easy to make mistake. 使收集成为无法修改的捕获,并防止此错误易于出错。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM