目录
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);
}
}
}