问题
通过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<User> list = new ArrayList<User>(); 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<User> list1 = new ArrayList<User>(); 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<E> ##
1 /** 2 * {@inheritDoc} 3 * 4 * <p>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 <tt>remove</tt> method. 8 * 9 * <p>Note that this implementation will throw an 10 * <tt>UnsupportedOperationException</tt> if the iterator returned by the 11 * <tt>iterator</tt> method does not implement the <tt>remove</tt> 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 * <p>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<E> 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<User> list = new ArrayList<User>(); 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<User> list1 = new ArrayList<User>(); 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()函数