Guava:copyOf()方法的ImmutableList魔术

时间:2013-05-27 01:21:08

标签: java collections guava

我想感受番石榴copyOf()方法guava-libraries的'神奇力量'。

我用它来检查它的小应用程序。

以下是documentation

  

JDK提供Collections.unmodifiableXXX方法,但我们认为这些方法可以

     
      
  • 笨拙而冗长;在你想制作防御性副本的任何地方使用都是不愉快的。
  •   
  • unsafe:如果没有人拥有对原始集合的引用,则返回的集合只是真正不可变的
  •   

所以,我尝试建立一个"someone holds a reference to the original collection"的模型。因此,使用集合的副本我不应该担心改变副本的价值。但魔法到目前为止还不起作用(有两次尝试:1。copyOf(collection),2。copyOf(iterator)):

import com.google.common.collect.ImmutableList;

import java.util.LinkedList;
import java.util.List;

class MyObject {    
    String name;    
    public MyObject(String name) {this.name = name;}    
    @Override
    public String toString() {
      return name;
    }    
}

public class ListUnsafe {

    List<MyObject> list = new LinkedList<MyObject>();    
    {
        list.add(new MyObject("a"));
        list.add(new MyObject("b"));
        list.add(new MyObject("c"));
    }

    public List<MyObject> getList() {
        return ImmutableList.copyOf(list);
    }

    public List<MyObject> getCopyIterator() {
        return ImmutableList.copyOf(list.iterator());
    }

    public static void main(String[] args) {

        ListUnsafe obj = new ListUnsafe();
        {
           MyObject ref = obj.list.get(0);

           List<MyObject> myList =  obj.getList();

           MyObject copyObj = myList.get(0);
           copyObj.name = "new";

           System.out.println("ref: " + ref);
        }

        obj = new ListUnsafe();
        {
            MyObject ref = obj.list.get(0);

            List<MyObject> myList =  obj.getCopyIterator();

            MyObject copyObj = myList.iterator().next();

            copyObj.name = "new";

            System.out.println("ref: " + ref);

        }

    }

}

输出:

ref: new
ref: new

这意味着我们更改了原始数据。我们不想要的。

问题

为什么不复制?
它与unmodifiableXXX的区别如何?

类似问题有link

答案是关于copyOf

  • (来自源代码)copyOf(Collection)实例不会创建临时ArrayListcopyOf(Iterable)copyOf(Iterator)这样做)。

2 个答案:

答案 0 :(得分:17)

  1. ImmutableList并没有神奇地使元素不可变;它是无法修改的列表,而不是它包含的元素。
  2. ImmutableList.copyOf制作副本,除非它复制已经 ImmutableList的列表。如果您为同一个不可变列表调用ImmutableList.copyOf(list)两次,则会获得两个不同的副本。

答案 1 :(得分:4)

首先感谢所有答案。我找到了一个满足我的榜样。这表明ImmutableSet.copyOf(..)和JDK的Collections.unmodifiableSet(..);

之间存在差异

是的:does shallow copy(否则很奇怪,因为它很神奇)。

class Person {
    public Person(String name) {this.name = name;}
    public Person(String name, Person relation) {this(name);this.relation = relation;}

    String name;
    Person relation;
}

public class ImmutableExample {
    public static void main(String[] args) {

        Person bob = new Person("bob");
        Person chris = new Person("chris", bob);
        Person nullPerson = null; // NULL!

        final Set<Person> originalSet = new LinkedHashSet<Person>(Arrays.asList(
                bob,
                chris
                // nullPerson // NULL !  <- if we use null then we can not convert it to ImmutableSet
               ));

        Set<Person> googleSet = ImmutableSet.copyOf(originalSet);

        Set<Person> javaSet = Collections.unmodifiableSet(originalSet);

        // is it SAFE to delete someone from original collection?
        originalSet.remove(chris);

        // google
        for (Person person : googleSet) System.out.println(person.name); // Chris is still here! And this is good! Stay with us, Chris!

        // java standard
        for (Person person : javaSet) System.out.println(person.name); // Where is Chris ??

        //newSet.add(new Person("newGuy"));  // UnsupportedOperationException

    }
}
  

(原始问题的目的,虽然是提供客户   绝对安全的阅读清单。但这只能通过实现   手动克隆列表中的所有对象[如果它们不是不可变的]。   提供更好的东西(就不变性而言)   并发安全性)比CopyOnWriteArrayList。我的意思是:   它提供了安全iterator,但如果是,则提供数据本身   在返回列表中是可变的 - 客户端仍然可以使用对返回(通过getter)数据项的引用来更改数据)