【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]
上一篇: 验证必填项