目录

1.重写equals去重

2.HashSet去重

3.HashMap去重

以下的操作基于User实体

public class user    {
    private String ID;

    private String name;

    private String address;

    private int age;

    // get & set....
}

1.重写equals去重

首先List对于String、Integer做去重是很简单的:

// 假如现在有个strList('1','1','2','2','3','3')或者intList(1,1,2,2,3,3)
List<String> strList = new ArrayList<>();
// 去过重的list
List<String> distinctList = new ArrayList<>();
for(String str : strList)
{
    // 若元素不存在,则直接添加
    if(!duplicatedList.contains(str))
    {
        duplicatedList.add(str);
    }
}

来看ArrayList中contains方法的源码

public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                // 判断元素是否重复的依据
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

能看出来判断元素存在的依据就是equals方法

String重写的equals方法,是将String分拆为char依次判断值是否相等

// String的equals方法
public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

Integer的equals方法是通过内部int来判断值是否相等

// Integer的equals方法 
public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }


 public int intValue() {
        return value;
    }

所以,List去重一个很重要的点就是需要重写equals方法,假设现在需要对一个List<User>去重,前提为list中,User除了主键ID不同,age,address,name信息都一致,代码如下:

public class User {
    // 那么我们可以直接在Person类中重写equals方法
    @Override
    public boolean equals(Object obj) {
        // 在list中,obj是list中的元素,所以
        User user= (User) obj;
        if (this.getAge() == user.getAge() && user.getAddress().equals(user.getAddress()) && this.getName().equals(user.getName())) {
            return true;
        }
        return false;
    }
}

去重的代码和之前保持一致即可。

2.HashSet去重

代码:

// 假如现在有个strList('1','1','2','2','3','3')或者intList(1,1,2,2,3,3)
List<String> strList = new ArrayList<>();
// 去过重的list
List<String> distinctList = new ArrayList<>();
Set<String> set = new HashSet<>(strList);
duplicatedList.addAll(set);

HashSet是如何实现去重的呢?

解析一下Set<String> set = new HashSet<>(strList); 这句代码做了什么事:

// 1.实例化,将list传进来,触发set初始化以及addAll方法
    public HashSet(Collection<? extends E> c) {
        // 这里注意,HashSet内部实现实际上是基于HashMap
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

    // 2.addAll方法实际上是其父类AbstractCollection中的方法,如下
    public boolean addAll(Collection<? extends E> c) {
        boolean modified = false;
        for (E e : c)
            //重点:循环遍历添加元素
            if (add(e))
                modified = true;
        return modified;
    }

    // 3.HashSet重写了父类AbstractCollection的add方法,如下
    public boolean add(E e) {
        // 调用HashMap的put方法来添加元素
        // 这里的PRESENT,实际上始终是一个Object对象
        // HashSet中的定义:private static final Object PRESENT = new Object();
        return map.put(e, PRESENT)==null;
    }

    // 4.HashMap的put方法
    public V put(K key, V value) {
         //HashMap是拿到key的hashCode值,以此来做内部计算从而保存到map的具体桶位置
        return putVal(hash(key), key, value, false, true);
    }

    // 5.hashMap的hash方法,实际上就是根据hashCode拿到hash值来确定保存在map数组位置
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

    // 6. putVal方法
     final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            //着重注意这里就可以,在插入元素的时候需要判断hashCode以及equals判断key
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

这一顿操作下来,聪明的你就能大致了解,Set的去重是通过key的hashCode和equals来做的,也就是说如果key一样,那么hashCode就一样,根据HasbMap的特性,key值相同则替换value,否则新增一个key-value。所以,如果要使用Set去重,我们就需要重写hashCode和equals方法。

public class User{
   @Override
    public int hashCode() {
        String str = String.valueOf(this.getAge()) + this.getAddress() + this.getName();
        return str.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        // 在list中,obj是list中的元素,所以
        User user= (User) obj;
        if (this.getAge() == user.getAge() && user.getAddress().equals(user.getAddress()) && this.getName().equals(user.getName())) {
            return true;
        }
        return false;
    }
}

把需要判断的字段合成String,通过String的hashCode,那么String一样,则hashCode一样,也就完成了去重。

3.HashMap去重

直接组成特定key来进行保存,不需要重写hashCode和equals

Map<String, String> map = new HashMap<>();
List<String> distinctList = new ArrayList<>();
for (ListEntity ad : list) {
    String str = ad.getAge() + ad.getAddress() + ad.getName();
    map.put(str, ad.getID());
    }
List<String> ids = new ArrayList<>();
ids.addAll(map.values());
for (String id:ids) {
    for (User ls:list) {
        if(ls.getID().equals(id)){
         distinctList.add(ls);
     }
   }
}