赞
踩
一、HashSet
1.定义
- public class HashSet<E>
- extends AbstractSet<E>
- implements Set<E>, Cloneable, java.io.Serializable
2.基本属性
- private transient HashMap<E,Object> map;
-
- //定义一个Object对象作为HashMap的value
- // 定义一个虚拟的Object PRESENT是向map中插入key-value对应的value
- // 因为HashSet中只需要用到key,而HashMap是key-value键值对;
- // 所以,向map中添加键值对时,键值对的值固定是PRESENT
- private static final Object PRESENT = new Object();
3.构造函数
- // 默认构造函数 底层创建一个HashMap
- public HashSet() {
- // 调用HashMap的默认构造函数,创建map
- map = new HashMap<E,Object>();
- }
-
-
-
- // 带集合的构造函数
- public HashSet(Collection<? extends E> c) {
- // 创建map。
- // 为什么要调用Math.max((int) (c.size()/.75f) + 1, 16),
- // 从 (c.size()/.75f) + 1 和 16 中选择一个比较大的树呢?
-
-
- // 首先,说明(c.size()/.75f) + 1
- // 因为从HashMap的效率(时间成本和空间成本)考虑,HashMap的加载因子是0.75。
- // 当HashMap的“阈值”(阈值=HashMap总的大小*加载因子) < “HashMap实际大小”时,
- // 就需要将HashMap的容量翻倍。
- // 所以,(c.size()/.75f) + 1 计算出来的正好是总的空间大小。
- // 接下来,说明为什么是 16 。
- // HashMap的总的大小,必须是2的指数倍。若创建HashMap时,指定的大小不是2的指数倍;
- // HashMap的构造函数中也会重新计算,找出比“指定大小”大的最小的2的指数倍的数。
- // 所以,这里指定为16是从性能考虑。避免重复计算。
-
- map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));
- // 将集合(c)中的全部元素添加到HashSet中
- addAll(c);
- }
-
-
-
- // 指定HashSet初始容量和加载因子的构造函数
- public HashSet(int initialCapacity, float loadFactor) {
- map = new HashMap<E,Object>(initialCapacity, loadFactor);
- }
-
- // 指定HashSet初始容量的构造函数
- public HashSet(int initialCapacity) {
- map = new HashMap<E,Object>(initialCapacity);
- }
-
- HashSet(int initialCapacity, float loadFactor, boolean dummy) {
- map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);
- }

4.方法
- 返回对HashSet中元素进行迭代的迭代器,返回元素的顺序并不是特定的
- 底层调用HashMap的keySet返回所有的key,这点反应了HashSet中的所有元素都是保存在HashMap的key中,value则是使用的PRESENT对象,该对象为static final
public Iterator<E> iterator() { // 实际上返回的是HashMap的“key集合的迭代器” return map.keySet().iterator(); }
- 返回此 set 中的元素的数量(set 的容量),底层调用HashMap的size方法,返回HashMap容器的大小
public int size() { return map.size();
- 调用HashMap的isEmpty()来判断HaspSet是否为空
- HashMap为null,对应的HashSet也为空
public boolean isEmpty() { return map.isEmpty(); }
- 调用HashMap的containsKey判断是否包含指定的key
- HashSet的所有元素就是通过HashMap的key来保存的
- contains(),判断某元素是否存在于HashSet()中,即(o==null ? e==null : o.equals(e)),存在返回true,否则返回false
- 底层调用containsKey判断HashMap的key值是否为空,即判断hashset中是否存在指定元素(key)
public boolean contains(Object o) { return map.containsKey(o); } public boolean containsKey(Object key) { return getNode(hash(key), key) != null; } // key和 hash(key) final Node<K,V> getNode(int hash, Object key) { Node<K,V>[] tab; Node<K,V> first, e; int n; K k; //判断数组不为空 长度不为0 找到数组位置 if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { /* 判断第一个节点hash等于传进来的hash(key) && 第一节点的key=传进来的key || first是数组某个位置上的Node节点 */ if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) return first; if ((e = first.next) != null) { //树节点 就按照树去查找 if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key); /*如果不是树 ,遍历链表每个元素 有则返回 从第二个才开始遍历 e是first.next的节点 第二节点 判断 hash值相同 key相同或者key.equels相同 */ do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; }
- hashset只是不允许重复的元素加入,而不是不允许元素连成链表
- 当两个hashcode相同但key不相等的entry插入时,仍然会连成一个链表,长度超过8时依然会和hashmap一样扩展成红黑树
- 当两个hashcode相同且key相等的entry插入时,会发生value的替换,只因所有entry的value一样,让人以为相同元素没有插入,事实是将value替换成和原来相同的值,所以和没有插入时一样的
- 当add方法发生冲突时,如果key相同,则替换value(value值相同),如果key不同,则连成链表
- add()如果此 set 中尚未包含指定元素,则添加指定元素
- 如果此Set没有包含满足(e==null ? e2==null : e.equals(e2)) 的e2时,则将e2添加到Set中,否则不添加且返回false
- 由于底层使用HashMap的put方法将key = e,value=PRESENT构建成key-value键值对,当此e存在于HashMap的key中,则value将会覆盖原有value,但是key保持不变,所以如果将一个已经存在的e元素添加中HashSet中,新添加的元素是不会保存到HashMap中,所以这就满足了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); } final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; // 拿到Node数组 如果数组为空 那么扩容 if ((tab = table) == null || (n = tab.length) == 0) //扩容看resize() n为新扩容的大小 n = (tab = resize()).length; // 在新扩容(可能不需要扩容)的实际位置 如果数组位置为空 直接赋值 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { //如果已经存在值 //对e做赋值的过程 Node<K,V> e; K k; //e为Node;hash相同;key也相同就赋值 //这里针对的是存在数组位置上的 //Node就和你插入的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); //p是什么 //某个位置上的Node(已经存在) 而且是存在值的(可能next为空) else { //开始循环 循环的是链表 for (int binCount = 0; ; ++binCount) { //如果是链表末尾,也就是没有next链表 if ((e = p.next) == null) { //在末尾创建新节点并且赋值 p.next = newNode(hash, key, value, null); /*如果链表长度>=7 那么就创建treeifyBin 在treeifyBin中 if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) resize(); 在判断数组长度<64 时 只会扩容 */ if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } //在循环过程中找到了相同的key 和 hash if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; //循环过程中 直接覆盖掉p p永远是e的next节点 p = e; } } // 对e赋值完成 //判断是否是覆盖操作,对接到上一个break之前 //如果是覆盖的话 // existing mapping for key if (e != null) { V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; //判断accessOrder才会执行 afterNodeAccess(e); //覆盖操作 返回老的值 return oldValue; } } //设置修改频次 ++modCount; //如果达到扩容的阈值 那么会扩容 上面的老值就肯定不需要扩容了 if (++size > threshold) resize(); //判断evict才会执行 afterNodeInsertion(evict); return null; }
- remove如果指定元素存在于此 set 中,则将其移除。底层使用HashMap的remove方法删除指定的Entry
public boolean remove(Object o) { return map.remove(o)==PRESENT; }
- clear从此 set 中移除所有元素。底层调用HashMap的clear方法清除所有的Entry
public void clear() { map.clear(); }
- clone返回此 HashSet 实例的浅表副本:并没有复制这些元素本身
public Object clone() { try { HashSet<E> newSet = (HashSet<E>) super.clone(); newSet.map = (HashMap<E, Object>) map.clone(); return newSet; } catch (CloneNotSupportedException e) { throw new InternalError(); } }
二、TreeSet
1.定义
- public class TreeSet<E> extends AbstractSet<E>
- implements NavigableSet<E>, Cloneable, java.io.Serializable
2.TreeSet中的变量
- private transient NavigableMap<E,Object> m;
-
- //PRESENT会被当做Map的value与key构建成键值对
- private static final Object PRESENT = new Object();
3.构造方法
- //默认构造方法,根据其元素的自然顺序进行排序
-
- public TreeSet() {
- this(new TreeMap<E,Object>());
- }
-
- //构造一个包含指定 collection 元素的新 TreeSet,它按照其元素的自然顺序进行排序。
- public TreeSet(Comparator<? super E> comparator) {
- this(new TreeMap<>(comparator));
- }
-
- //构造一个新的空 TreeSet,它根据指定比较器进行排序。
- public TreeSet(Collection<? extends E> c) {
- this();
- addAll(c);
- }
-
- //构造一个与指定有序 set 具有相同映射关系和相同排序的新 TreeSet。
- public TreeSet(SortedSet<E> s) {
- this(s.comparator());
- addAll(s);
- }
-
- TreeSet(NavigableMap<E,Object> m) {
- this.m = m;
- }

4.方法
- add:将指定的元素添加到此 set(如果该元素尚未存在于 set 中)
public boolean add(E e) { return m.put(e, PRESENT)==null; } public V put(K key, V value) { Entry<K,V> t = root; if (t == null) { //空树时,判断节点是否为空 compare(key, key); // type (and possibly null) check root = new Entry<>(key, value, null); size = 1; modCount++; return null; } int cmp; Entry<K,V> parent; // split comparator and comparable paths Comparator<? super K> cpr = comparator; //非空树,根据传入比较器进行节点的插入位置查找 if (cpr != null) { do { parent = t; //节点比根节点小,则找左子树,否则找右子树 cmp = cpr.compare(key, t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; //如果key的比较返回值相等,直接更新值(一般compareto相等时equals方法也相等) else return t.setValue(value); } while (t != null); } else { //如果没有传入比较器,则按照自然排序 if (key == null) throw new NullPointerException(); @SuppressWarnings("unchecked") Comparable<? super K> k = (Comparable<? super K>) key; do { parent = t; cmp = k.compareTo(t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null); } //查找的节点为空,直接插入,默认为红节点 Entry<K,V> e = new Entry<>(key, value, parent); if (cmp < 0) parent.left = e; else parent.right = e; //插入后进行红黑树调整 fixAfterInsertion(e); size++; modCount++; return null; }
- get:获取元素
public V get(Object key) { Entry<K,V> p = getEntry(key); return (p==null ? null : p.value); }
- ceiling:返回此 set 中大于等于给定元素的最小元素;如果不存在这样的元素,则返回 null
public E ceiling(E e) { return m.ceilingKey(e); }
- clear:移除此 set 中的所有元素
public void clear() { m.clear(); }
- clone:返回 TreeSet 实例的浅表副本。属于浅拷贝
public Object clone() { TreeSet<E> clone = null; try { clone = (TreeSet<E>) super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(); } clone.m = new TreeMap<>(m); return clone; }
- comparator:返回对此 set 中的元素进行排序的比较器;如果此 set 使用其元素的自然顺序,则返回 null
public Comparator<? super E> comparator() { return m.comparator(); }
- contains:如果此 set 包含指定的元素,则返回 true
public boolean contains(Object o) { return m.containsKey(o); }
- descendingIterator:返回在此 set 元素上按降序进行迭代的迭代器
public Iterator<E> descendingIterator() { return m.descendingKeySet().iterator(); }
- descendingSet:返回此 set 中所包含元素的逆序视图
public NavigableSet<E> descendingSet() { return new TreeSet<>(m.descendingMap()); }
- first:返回此 set 中当前第一个(最低)元素
public E first() { return m.firstKey(); }
- floor:返回此 set 中小于等于给定元素的最大元素;如果不存在这样的元素,则返回 null
public E floor(E e) { return m.floorKey(e); }
- headSet:返回此 set 的部分视图,其元素严格小于 toElement
public SortedSet<E> headSet(E toElement) { return headSet(toElement, false); }
- higher:返回此 set 中严格大于给定元素的最小元素;如果不存在这样的元素,则返回 null
public E higher(E e) { return m.higherKey(e); }
- isEmpty:如果此 set 不包含任何元素,则返回 true
public boolean isEmpty() { return m.isEmpty(); }
- iterator:返回在此 set 中的元素上按升序进行迭代的迭代器
public Iterator<E> iterator() { return m.navigableKeySet().iterator(); }
- last:返回此 set 中当前最后一个(最高)元素
public E last() { return m.lastKey(); }
- lower:返回此 set 中严格小于给定元素的最大元素;如果不存在这样的元素,则返回 null
public E lower(E e) { return m.lowerKey(e); }
- pollFirst:获取并移除第一个(最低)元素;如果此 set 为空,则返回 null
public E pollFirst() { Map.Entry<E,?> e = m.pollFirstEntry(); return (e == null) ? null : e.getKey(); }
- pollLast:获取并移除最后一个(最高)元素;如果此 set 为空,则返回 null
public E pollLast() { Map.Entry<E,?> e = m.pollLastEntry(); return (e == null) ? null : e.getKey(); }
- remove:将指定的元素从 set 中移除(如果该元素存在于此 set 中)
public boolean remove(Object o) { return m.remove(o)==PRESENT; }
- size:返回 set 中的元素数(set 的容量)
public int size() { return m.size(); }
- subSet:返回此 set 的部分视图
/** * 返回此 set 的部分视图,其元素范围从 fromElement 到 toElement。 */ public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { return new TreeSet<>(m.subMap(fromElement, fromInclusive, toElement, toInclusive)); } /** * 返回此 set 的部分视图,其元素从 fromElement(包括)到 toElement(不包括)。 */ public SortedSet<E> subSet(E fromElement, E toElement) { return subSet(fromElement, true, toElement, false); }
- tailSet:返回此 set 的部分视图
/** * 返回此 set 的部分视图, *其元素大于(或等于,如果 inclusive 为 true)fromElement。 */ public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { return new TreeSet<>(m.tailMap(fromElement, inclusive)); } /** * 返回此 set 的部分视图,其元素大于等于 fromElement。 */ public SortedSet<E> tailSet(E fromElement) { return tailSet(fromElement, true); }
三、LinkedHashSet
1.定义
2.构造函数
- //Constructor - 1
-
- public LinkedHashSet(int initialCapacity, float loadFactor)
- {
- super(initialCapacity, loadFactor, true); //Calling super class constructor
- }
-
- //Constructor - 2
-
- public LinkedHashSet(int initialCapacity)
- {
- super(initialCapacity, .75f, true); //Calling super class constructor
- }
-
- //Constructor - 3
-
- public LinkedHashSet()
- {
- super(16, .75f, true); //Calling super class constructor
- }
-
- //Constructor - 4
-
- public LinkedHashSet(Collection<? extends E> c)
- {
- super(Math.max(2*c.size(), 11), .75f, true); //Calling super class constructor
- addAll(c);
- }

- HashSet(int initialCapacity, float loadFactor, boolean dummy)
- {
- map = new LinkedHashMap<>(initialCapacity, loadFactor);
- }
3.LinkedHashSet如何维护插入顺序(具体参见Java基础汇总(十六)——LinkedHashMap)
参见文章:Java基础汇总(十六)——LinkedHashMap_我爱豆子的博客-CSDN博客
- private static class Entry<K,V> extends HashMap.Entry<K,V>
- {
- // These fields comprise the doubly linked list used for iteration.
- Entry<K,V> before, after;
-
- Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
- super(hash, key, value, next);
- }
- }
附:
访问级别:
private | 默认 | protected | public | |
---|---|---|---|---|
同一类的成员 | √ | √ | √ | √ |
同一个包的其他类(包括子类) | × | √ | √ | √ |
不同包的子类 | × | × | √ | √ |
不同包的非子类 | × | × |
其中:以上访问级别只适用于类和类的成员,不适用于局部变量。
成员变量、成员方法、构造方法都可以使用上面的四种访问级别
参考文章:
Java-Tutorial/Java集合详解7:HashSet,TreeSet与LinkedHashSet.md at master · h2pl/Java-Tutorial · GitHub
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。