遇到一个需求,把一个重复的列表去重。我想这还不简单,我立刻想到了使用Set这个集合类,没有具体研究过,但是知道这个就是可以自动去重的。
所以我写了一个方法:
private List<Bean> removeRepeated(List<Bean> beans) {
Set<Bean> set = new HashSet<>();
for (Bean bean : beans) {
set.add(name);
}
return new ArrayList<>(set);
}
结果没有达到理想的效果,我转念一想,这是个类的对象,没有重写equals方法,肯定不能就这么成功呀。然后我重写了equals方法
public boolean equals(@Nullable Object obj) {
if (obj instanceof Bean)
return name.equals(((Bean) obj).getName());
return false;
}
这样总没问题了吧,结果还是不行,我。。。
我在这个方法里大了一堆Log,结果一个都没显示,好家伙压根就没用这个方法。
我一气之下,直接改用list
private List<Bean> removeRepeated(List<Bean> beans) {
List<Bean> set = new ArrayList<>();
for (Bean bean : beans) {
if (!set.contains(bean))
set.add(bean);
}
return new ArrayList<>(set);
}
结果成了,到这里可以收工了,解决了就可以了嘛。
但是问题是为什么使用ArrayList可以,而HashSet 不可以呢?这个问题如鲠在喉。
- 先看一下为什么可以ArrayList可以。这里我用了contains这个方法做了一个判断,以下是ArrayList的相关源码
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;
}
源码很简单,使用了indexOf方法,在indexOf方法中调用了为被包含类的equals方法,刚好我在类中定义了,所以就成了。
这个理解很容易,那HashSet呢?
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
- HashSet中内部实现是使用HashMap的,而在HashMap中是使用key的hash值来进行比较存储的,这里HashMap.putVal逻辑有点复杂就不复制了。
- 因为每个对象的hash值是肯定不一样的,所以对于对象的去重是做不到的,一般用于对基本类型的去重比较好。但是要是非要用这个来做也不是不可以,可以自己重写hashCode方法,保证内容一样的对象hash值一样,这个做法不推荐。
- 那其他的set的实现类呢?TreeSet需要实现比较器(我又不用排序,用比较器就感觉奇奇怪怪的)ArraySet也用到了hash值,所以我还是用ArrayList吧。。。