Set
# 前言
Java
集合可分为Collection
和Map
两种体系:
Collection
接口:单列数据,定义了存取一组对象的方法的集合List
:元素有序、可重复的集合ArrayList
、LinkedList
、Vector
Set
:元素无序、不可重复的集合HashSet
、LinkedHashSet
、TreeSet
Map
接口:双列数据,保存具有映射关系“key-value对
”的集合,也称为键值对。HashMap
、LinkedHashMap
、TreeMap
、Hashtable
、Properties
接下来学习Set接口
# 概述
Set
接口是Collection
的子接口,set接口没有提供额外的方法Set
集合不允许包含相同的元素,如果试把两个相同的元素加入同一个Set
集合中,则添加操作失败。Set
判断两个对象是否相同不是使用==
运算符,而是根据equals()
方法
结论:Set存储无序的、不可重复的数据。
# 面试题
下面输出结果是?
remove p1 后:[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}]
重新添加后:[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}]
最后:[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}, Person{id=1001, name='AA'}]
1
2
3
4
5
2
3
4
5
为什么p1没移除成功?
以下是java.util.HashSet.remove()方法的声明。
public boolean remove(Object o)
1
我们打印一下移除结果,返回false,这是为什么?
**HashSet 集合判断两个元素相等的标准:**两个对象通过hashCode()
方法比较相等,并且两个对象的equals()
方法返回值也相等。
当HashSet使用HashMap实现时,指定元素将保存在Node的key中,而value是HashSet自定义的一个对象,元素的哈希值会保存在节点的hash属性中。
当改变了p1的名字后,HashSet就无法判断这两个元素相等了。所以就remove失败。(虽然遍历还是能指向这个元素,能获取修改后的值)
当已经添加到HashMap中的对象改变了hash值后,不会改变它在HashMap中的位置,此时元素的Hash值和节点的Hash值会不同。
HashSet删除时会根据要删除元素的哈希值,去找到节点相同哈希值的对象,但是我们已经修改了元素的哈希值,所以肯定找不到,所以就返回false,删除失败。
下面准备开始执行remove()方法,看一下HashMap的remove()方法代码:
@Override
public boolean remove(Object key, Object value) {
return removeNode(hash(key), key, value, true, true) != null;
}
1
2
3
4
2
3
4
调用了removeNode()方法:
final Node<K,V> removeNode(int hash, Object key, Object value,
boolean matchValue, boolean movable) {
Node<K,V>[] tab; Node<K,V> p; int n, index;
if ((tab = table) != null && (n = tab.length) > 0 &&
(p = tab[index = (n - 1) & hash]) != null) {
Node<K,V> node = null, e; K k; V v;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
node = p;
else if ((e = p.next) != null) {
if (p instanceof TreeNode)
node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
else {
do {
if (e.hash == hash &&
((k = e.key) == key ||
(key != null && key.equals(k)))) {
node = e;
break;
}
p = e;
} while ((e = e.next) != null);
}
}
if (node != null && (!matchValue || (v = node.value) == value ||
(value != null && value.equals(v)))) {
if (node instanceof TreeNode)
((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
else if (node == p)
tab[index] = node.next;
else
p.next = node.next;
++modCount;
--size;
afterNodeRemoval(node);
return node;
}
}
return null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
最快方式去除List内的重复元素
public List removeDuplicateElements(List list){
HashSet set = new HashSet();
set.addAll(list);
return new ArrayList(set);
}
1
2
3
4
5
2
3
4
5
# 总结
- Set接口:存储无序的、不可重复的数据
HashSet
实现类:作为Set
接口的主要实现类;线程不安全的;可以存储null
值。底层是数组+链表。LinkedHashSet
实现类:作为HashSet
的子类;遍历其内部数据时,可以按照添加的顺序遍历。底层是数组+双向链表。
TreeSet
实现类:可以按照添加对象的指定属性,进行排序。底层是红黑树。
帮我改善此页面 (opens new window)
上次更新: 2020/12/27, 05:12:33