java 集合removeAll() 方法


问题

通过removeAll方法移除list中和list1一样的元素,定义如下函数:

 1 package com.study;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 class User {
 7 
 8     private int id;
 9     private String name;
10 
11     public User() {
12     }
13 
14     public User(int id, String name) {
15         super();
16         this.id = id;
17         this.name = name;
18     }
19 
20     public int getId() {
21         return id;
22     }
23 
24     public void setId(int id) {
25         this.id = id;
26     }
27 
28     public String getName() {
29         return name;
30     }
31 
32     public void setName(String name) {
33         this.name = name;
34     }
35 }
36 
37 public class TestRemoveAll {
38     public static void main(String[] args) {
39         List list = new ArrayList();
40         list.add(new User(1, "1"));
41         list.add(new User(2, "2"));
42         list.add(new User(3, "3"));
43         list.add(new User(4, "4"));
44         List list1 = new ArrayList();
45         list1.add(new User(1, "1"));
46         list1.add(new User(2, "2"));
47         list1.add(new User(3, "3"));
48         list1.add(new User(5, "5"));
49         list.removeAll(list1);
50         System.out.println("list remove list1 以后的大小为 " + list.size());
51     }
52 }

结果为:

 显然,这不是想要的结果。那么,为什么会出现这种结果呢?查看jdk源码:

## AbstractCollection ##

 1  /**
 2      * {@inheritDoc}
 3      *
 4      * 

This implementation iterates over this collection, checking each 5 * element returned by the iterator in turn to see if it's contained 6 * in the specified collection. If it's so contained, it's removed from 7 * this collection with the iterator's remove method. 8 * 9 *

Note that this implementation will throw an 10 * UnsupportedOperationException if the iterator returned by the 11 * iterator method does not implement the remove method 12 * and this collection contains one or more elements in common with the 13 * specified collection. 14 * 15 * @throws UnsupportedOperationException {@inheritDoc} 16 * @throws ClassCastException {@inheritDoc} 17 * @throws NullPointerException {@inheritDoc} 18 * 19 * @see #remove(Object) 20 * @see #contains(Object) 21 */ 22 public boolean removeAll(Collection<?> c) { 23 Objects.requireNonNull(c); 24 boolean modified = false; 25 Iterator<?> it = iterator(); 26 while (it.hasNext()) { 27 if (c.contains(it.next())) { 28 it.remove(); 29 modified = true; 30 } 31 } 32 return modified; 33 }

上边removeAll方法是通过迭代器去判断c集合中是否包含,包含即调用迭代器的remove方法。在判断包含的时候(c.contains(Object obj)),这里contains方法的源码如下:

 1 /**
 2      * {@inheritDoc}
 3      *
 4      * 

This implementation iterates over the elements in the collection, 5 * checking each element in turn for equality with the specified element. 6 * 7 * @throws ClassCastException {@inheritDoc} 8 * @throws NullPointerException {@inheritDoc} 9 */ 10 public boolean contains(Object o) { 11 Iterator it = iterator(); 12 if (o==null) { 13 while (it.hasNext()) 14 if (it.next()==null) 15 return true; 16 } else { 17 while (it.hasNext()) 18 if (o.equals(it.next())) 19 return true; 20 } 21 return false; 22 }

这里当对象o不为空时,迭代判断用到了Object的equals方法,而Object的equals方法指的是两个对象的引用是否相等,所以才形成了最初的问题。如果我们要判断两个对象的内容相等,这里就需要重写equals方法。

代码如下:

 1 package com.study;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 import java.util.Objects;
 6 
 7 class User {
 8 
 9     private int id;
10     private String name;
11     
12     // 省略
13 
14     @Override
15     public boolean equals(Object o) {
16         if (this == o) return true;
17         if (o == null || getClass() != o.getClass()) return false;
18         User user = (User) o;
19         return id == user.id && Objects.equals(name, user.name);
20     }
21 
22     @Override
23     public int hashCode() {
24         return Objects.hash(id, name);
25     }
26 }
27 
28 public class TestRemoveAll {
29     public static void main(String[] args) {
30         List list = new ArrayList();
31         list.add(new User(1, "1"));
32         list.add(new User(2, "2"));
33         list.add(new User(3, "3"));
34         list.add(new User(4, "4"));
35         List list1 = new ArrayList();
36         list1.add(new User(1, "1"));
37         list1.add(new User(2, "2"));
38         list1.add(new User(3, "3"));
39         list1.add(new User(5, "5"));
40         list.removeAll(list1);
41         System.out.println("list remove list1 以后的大小为 " + list.size());
42     }
43 }

这里重写equals方法推荐使用idea自动生成的,然后执行了下main方法,得到的结果如下:

注意:

1、要想是removeAll方法生效,只需重写equals方法即可。  

  为何这里还要重写hashcode方法,重写hashcode方法为了将数据存入HashSet/HashMap/Hashtable类时进行比较。

2、看完removeAll方法的源码后,我们看到的是双层迭代,这种方式在数据量大的情况下,势必会影响效率。所以谨慎使用removeAll()函数