Java 之 removeAll

在处理归并不同集合时去除其重复元素的时候没有什么好的方法,本来打算手撸一个去处重复元素的函数,但是想起集合类里肯定有解决这一问题的方法,就去查了一下 API ,还真有,那就是使用 removeAll() 。

boolean removeAll(Collection<?> c)  
从列表中移除指定 collection 中包含的其所有元素(可选操作)。

直接用一个简单的例子来讲一下如何使用 removeAll() :

public class Problems {
    private Integer id;
    private String title;

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title == null ? null : title.trim();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Problems )) {
            return false
        }

        Problems that = (Problems) o;

        if (getId() != null ? !getId().equals(that.getId()) : that.getId() != null){
            return false;
        }
        if (getTitle() != null ? !getTitle().equals(that.getTitle()) : that.getTitle() != null){
            return false;
        }
        return true;
    }
    @Override
    public int hashCode() {
        int result = getId() != null ? getId().hashCode() : 0;
        result = 31 * result + (getTitle() != null ? getTitle().hashCode() : 0);
        return result;
    }      
}

因为在执行removeAll方法时,会先对集合元素进行比较,如果元素相等才执行移除操作,说到这,相信很多人都已经明白是怎么回事了,因为不相等(equals),所以没有执行移除。也就是说直接使用 removeAll 是没有效果的,要在实体类中重写 hashCode 和 equals 方法才行。现在大多数 IDE 都支持自动生成实体类的 hashCode 和 equals 方法,不妨查一下你的 IDE 的快速生成方法,不然就得手撸了。
然后现在就可以来试一下了:

import java.util.ArrayList;  
import java.util.List;  

public class ProblemsList {  

    private List<Problems> subList;  
    private List<Problems> allList;  

    public UserList(){  
        subList=new ArrayList<Problems>();  
        allList=new ArrayList<Problems>();  

        for(int i=0;i<3;i++){  
            Problems problems=new Problems();  
            Problems.setId(i);  
            Problems.setTitle("This is"+i);  
            subList.add(Problems);  
        }  

       for(int i=0;i<10;i++){  
            Problems problems=new Problems();  
            Problems.setId(i);  
            Problems.setTitle("This is"+i);  
            subList.add(Problems);  
        }  
    }  

    public static void main(String[] args){  
        ProblemsList problemsList =new ProblemsList();  
        problemsList.allList.removeAll(problemsList.subList);//调用removeAll方法
        System.out.println(problemsList.allList.size());  

    }  
}  

OK了,到这里本来可以结束了,不过不妨再深入看看 removeAll:

    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        boolean modified = false;
        Iterator<?> it = iterator();
        while (it.hasNext()) {
            if (c.contains(it.next())) {
                it.remove();
                modified = true;
            }
        }
        return modified;
    } 

可以看到在调用removeAll方法时,其中调用了contains()方法,一块贴上源码:

    public boolean contains(Object o) {
        Iterator<E> it = iterator();
        if (o==null) {
            while (it.hasNext())
                if (it.next()==null)
                    return true;
        } else {
            while (it.hasNext())
                if (o.equals(it.next()))//这里调用了equals()方法
                    return true;
        }
        return false;
    } 

但是很奇怪,为什么我们还要重写 hashCode 方法呢?来看官方文档:

在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。
以下情况不 是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。
实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)
当 equals 方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。

抓下重点:
1.如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。
2.当 equals 方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
也就是说,两个对象是相等的(equals ),那么其 hashCode 结果必须相等,所以有必要重写 hashCode 方法。