欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【Java学习笔记】不要把一个非临时可变对象添加入集合中!

程序员文章站 2022-07-15 16:19:04
...

 今天再读ArrayList的源码是意外发现了一个令人惊讶的问题:

public E set(int index, E element) 
{
    rangeCheck(index);
    E oldValue = elementData(index);
    elementData[index] = element;//注意这句!
    return oldValue;
}

 注意第三句,它只是简单的把element指向的对象赋给了elementData[index]这意味着如果外界的element更改了,那么ArrayList也会更改!
 为了证明这一点,我做了个实验:

class Demo implements Comparable<Demo>
{
    int i = 0;
    @Override
    public String toString()
    {
        // TODO Auto-generated method stub
        return String.valueOf(i);
    }
    @Override
    public int compareTo(Demo o)
    {
        // TODO Auto-generated method stub
        return i - o.i;
    }
}
Collection<Demo> collection = null;

List<Demo> list = new LinkedList<>();
Set<Demo> set = new HashSet<>();
Queue<Demo> queue = new ArrayDeque<>();
Demo demo = new Demo();

collection = queue;

collection.add(demo);

System.out.println(collection);
demo.i = 5;//更改
System.out.println(collection);

[0]
[5]

 事实证明确实变了。
 所以这就告诉我们,如果我们要使用一个可变对象的集合时,千万不能把之后还要使用的对象加入集合中。换而言之:

A a = new A();//A是一个可变类
list.add(a);//NO!!!
a.modify();

 那么为什么平时我们没有意识到这有什么问题呢?这是因为我们平时用的类大多都是不可变类,例如String。实际上String中的方法根本就不会改变自身:

String s = new String("abc");
List<String> list = new LinkedList<>();

list.add(s);

System.out.println(collection);//[abc]
s.toUpperCase();//不改变s
System.out.println(s);//还是abc
System.out.println(collection);//还是[abc]

 然而如果你用StringBuffer代替String就会发现:

StringBuffer sBuffer = new StringBuffer("abc");
List<StringBuffer> list = new LinkedList<>();
list.add(sBuffer);

System.out.println(collection);//[abc]
sBuffer.append("def");
System.out.println(collection);//[abcdef]
相关标签: java 集合