contains、remove方法解析
在开始这两个方法之前先浅谈一下迭代器
注意:以下讲解的遍历方式/迭代方式,是所有Collection通用的一种方式,在Map集合中不能使用。在所有的Collection以及子类中使用。
example:
// 创建集合对象
Collection c = new ArrayList();
// 添加元素
c.add("abc");
c.add("def");
c.add(100);
c.add(new Object());
// 对集合Collection进行遍历/迭代
// 第一步: 获取集合对象的迭代器对象 Iterator
Iterator it = c.iterator();
// 第二步: 通过以上获取的迭代器对象开始迭代/遍历集合
while(it.hasNext()){
Object obj = it.next();
System.out.println(obj);
}
运行结果:
abc
def
100
java.lang.Object@1b6d3586
迭代器原理:
迭代器可以理解为原集合的一个快照(照片),记录的是原集合某一个时间的状态(内容)。当利用迭代器去遍历元素时,它会先在它自身(迭代器)去查,自身查到以后再映射到集合上面去找,进而找到元素,相当于进行了两次查找(我的理解是迭代器只是存储了某一个时间集合中各元素的地址,利用迭代器去遍历元素时相当于先遍历出元素地址,再利用地址去映射(查找)到元素,相当于两次查找),利用迭代器删除元素也是同样的道理(进行两次删除)!迭代器的底层源码十分复杂,感兴趣的可以自己去看看!
1. contains方法
boolean contains(Object o)判断集合中是否包含某个对象o,如果包含返回true,如果不包含返回false。contains方法是用来判断集合中是否包含某个元素的方法,它的底层是调用了equals方法进行比对:equals方法返回true,就表示包含个某元素,否则表示不包含这个元素。
example1:
// 创建集合对象
Collection c = new ArrayList();
// 向集合中存储元素
String s1 = new String("abc");
c.add(s1);
String s2 = new String("def");
c.add(s2);
// 集合中元素的个数
System.out.println("元素的个数:"+c.size());
// 新建对的对象String
String x = new String("abc");
// c集合中是否包含x?结果猜测一下是true还是false?
System.out.println(c.contains(x));
运行结果:
元素的个数:2
true
但如果代码是这样:
// 创建集合对象
Collection c = new ArrayList();
// 创建用户对象
User u1 = new User("jack");
User u2 = new User("jack");
// 加入集合
c.add(u1);
System.out.println(c.contains(u2));
}
}
class User{
private String name;
public User(){};
public User(String name){
this.name = name;
}
}
运行结果:
false
这是怎么回事嘞?在example1中String对象中,输出结果为true,而在此处的例子中输出结果却为false。这是因为在User类中没有重写equals方法,故此时会调object类的equals方法,而object的equals方法是用==比较的内存地址,两个对象的内存地址肯定不相同,故输出结果为false!在上述User类中重写equals方法:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
// 不在比较内存地址了,而是比较内容
return Objects.equals(name, user.name);
}
则输出结果为:
true
总而言之:重写equals方法是java程序员的基本素养
2. remove方法
其实remove方法的原理同contains方法的原理一样,也是需要重写equals方法!
example1:
// 创建集合
Collection c = new ArrayList();
Iterator it = c.iterator();
// 添加元素
c.add(1);
c.add(2);
c.add(3);
while(it.hasNext()){
// 编写代码时next() 方法返回值类型必须是Object。
Object obj = it.next();
System.out.println(obj);
}
运行结果:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.lxz.collection.CollectionTest06.main(CollectionTest06.java:36)
由example1的代码我们可以知道,是先获取了迭代器,然后再添加了元素,相当于集合中的元素发生了变化!此时就会报一个异常ConcurrentModificationException.迭代器可以理解为一个快照(照片),只是记录了某一个时间上的集合,故此例子中获取的迭代器实际上是没有任何元素的!一定要***注意***: 集合结构只要发生改变,迭代器必须重新获取,否则会出现.ConcurrentModificationException的异常
example2:
Collection c2 = new ArrayList();
c2.add("abc");
c2.add("def");
c2.add("xyz");
Iterator it2 = c2.iterator();
while(it2.hasNext()){
Object o = it2.next();
c2.remove(o);
System.out.println(o);
}
System.out.println(c2.size());
运行结果:
abc
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.lxz.collection.CollectionTest06.main(CollectionTest06.java:45)
example2通过迭代器去遍历出每一个元素,然后再调用集合自身的remove方法去删除集合中的元素!这样会报ConcurrentModificationException异常,因为元素删除之后,没有更新迭代器(迭代器不知道集合变化了),导致迭代器的快照与原集合状态不同!故当集合的状态发生变化以后需要去重新获取迭代器!
example3:
Collection c2 = new ArrayList();
c2.add("abc");
c2.add("def");
c2.add("xyz");
Iterator it2 = c2.iterator();
while(it2.hasNext()){
Object o = it2.next();
it2.remove(o);
System.out.println(o);
}
System.out.println(c2.size());
运行结果:
abc
def
xyz
0
使用迭代器的remove方法去删除元素时,会自动更新迭代器,并且更新集合(删除集合中的元素)!不会导致迭代器的快照与原集合状态不同!也就是说,集合的remove方法删除,只会删除集合本身中的元素。而使用迭代器的remove方法删除元素时,会将迭代器和原集合中的元素都删除!