7

我想感受一下copyOf()番石榴方法的‘魔力’ guava-libraries

我用一个小应用程序来检查它。

这是文档

JDK 提供了Collections.unmodifiableXXX方法,但在我们看来,这些可以是

  • 笨拙且冗长;在你想制作防御性副本的任何地方使用都不愉快
  • 不安全:只有在没有人持有对原始集合的引用时,返回的集合才是真正不可变的

所以,我尝试建立一个模型,其中"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

有类似问题的链接:

答案说copyOf

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

2 回答 2

20
  1. ImmutableList不会神奇地使元素不可变;它是无法修改的列表,而不是它包含的元素。
  2. ImmutableList.copyOf除非它正在复制一个已经ImmutableList. 如果您ImmutableList.copyOf(list)为同一个不可变列表调用两次,您将获得两个不同的副本。
于 2013-05-27T01:45:29.940 回答
5

首先感谢大家的回答。我想出了一个让我满意的例子。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)数据项的引用来更改数据)

于 2013-05-27T13:06:22.600 回答