5.Java集合框架剖析 之 Hashset和LinkedHashSet源码剖析
1 package java.util; 2 3 import java.io.InvalidObjectException; 4 import sun.misc.SharedSecrets; 5 6 //HashSet底层是数组 + 单链表 + 红黑树的数据结构 7 public class HashSet<E> extends AbstractSet<E> 8 implements Set<E>, Cloneable, java.io.Serializable { 9 10 static final long serialVersionUID = -5024744406713321676L; 11 12 // 底层使用HashMap来保存HashSet中所有元素 13 private transient HashMap<E,Object> map; 14 15 // 定义一个虚拟的Object对象作为HashMap的value,将此对象定义为static final 16 private static final Object PRESENT = new Object(); 17 18 //默认的无参构造器,构造一个空的HashSet。 19 // 实际底层会初始化一个空的HashMap,并使用默认初始容量为16和加载因子0.75。 20 public HashSet() { 21 map = new HashMap<>(); 22 } 23 24 //构造一个包含指定collection中的元素的新set。 25 //实际底层使用默认的加载因子0.75和足以包含指定 collection中所有元素的初始容量来创建一个HashMap。 26 public HashSet(Collection<? extends E> c) { 27 map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); 28 addAll(c); 29 } 30 31 //以指定的initialCapacity和loadFactor构造一个空的HashSet。 32 //实际底层以相应的参数构造一个空的HashMap。 33 public HashSet(int initialCapacity, float loadFactor) { 34 map = new HashMap<>(initialCapacity, loadFactor); 35 } 36 37 //以指定的initialCapacity构造一个空的HashSet。 38 //实际底层以相应的参数及加载因子loadFactor为0.75构造一个空的HashMap。 39 public HashSet(int initialCapacity) { 40 map = new HashMap<>(initialCapacity); 41 } 42 43 //以指定的initialCapacity和loadFactor构造一个新的空链接哈希集合。 44 //此构造函数为包访问权限,不对外公开,实际只是是对LinkedHashSet的支持。 45 //实际底层会以指定的参数构造一个空LinkedHashMap实例来实现。 46 HashSet(int initialCapacity, float loadFactor, boolean dummy) { 47 map = new LinkedHashMap<>(initialCapacity, loadFactor); 48 } 49 50 //返回对此set中元素进行迭代的迭代器。返回元素的顺序并不是特定的。 51 //底层实际调用底层HashMap的keySet来返回所有的key。 52 //可见HashSet中的元素,只是存放在了底层HashMap的key上,value使用一个static final的Object对象标识。 53 public Iterator<E> iterator() { 54 return map.keySet().iterator(); 55 } 56 57 //返回此set中的元素的数量(set的容量)。 58 //底层实际调用HashMap的size()方法返回Entry的数量,就得到该Set中元素的个数。 59 public int size() { 60 return map.size(); 61 } 62 63 //判断set是否为空,则返回true,底层实际调用HashMap的isEmpty()判断该HashSet是否为空。 64 public boolean isEmpty() { 65 return map.isEmpty(); 66 } 67 68 //如果此set包含指定元素,则返回true。 69 //底层实际调用HashMap的containsKey判断是否包含指定key。 70 public boolean contains(Object o) { 71 return map.containsKey(o); 72 } 73 74 //如果此set中尚未包含指定元素,则添加指定元素。 75 //底层实际将将该元素作为key放入HashMap。 76 //由于HashMap的put()方法添加key-value对时,当新放入HashMap的Entry中key 77 //与集合中原有Entry的key相同(hashCode()返回值相等,通过equals比较也返回true), 78 //新添加的Entry的value会将覆盖原来Entry的value,但key不会有任何改变, 79 //因此如果向HashSet中添加一个已经存在的元素时,新添加的集合元素将不会被放入HashMap中, 80 //原来的元素也不会有任何改变,这也就满足了Set中元素不重复的特性。 81 public boolean add(E e) { 82 return map.put(e, PRESENT)==null; 83 } 84 85 //如果指定元素存在于此set中,则将其移除。 86 public boolean remove(Object o) { 87 return map.remove(o)==PRESENT; 88 } 89 90 //从此set中移除所有元素。此调用返回后,该set将为空。 91 //底层实际调用HashMap的clear方法清空Entry中所有元素。 92 public void clear() { 93 map.clear(); 94 } 95 96 //返回此HashSet实例的浅表副本:并没有复制这些元素本身。 97 @SuppressWarnings("unchecked") 98 public Object clone() { 99 try { 100 HashSet<E> newSet = (HashSet<E>) super.clone(); 101 newSet.map = (HashMap<E, Object>) map.clone(); 102 return newSet; 103 } catch (CloneNotSupportedException e) { 104 throw new InternalError(e); 105 } 106 } 107 108 //序列化 109 private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { 110 s.defaultWriteObject(); 111 112 s.writeInt(map.capacity()); 113 s.writeFloat(map.loadFactor()); 114 115 s.writeInt(map.size()); 116 117 for (E e : map.keySet()) 118 s.writeObject(e); 119 } 120 121 //反序列化 122 private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException { 123 s.defaultReadObject(); 124 125 int capacity = s.readInt(); 126 if (capacity < 0) { 127 throw new InvalidObjectException("Illegal capacity: " + capacity); 128 } 129 130 float loadFactor = s.readFloat(); 131 if (loadFactor <= 0 || Float.isNaN(loadFactor)) { 132 throw new InvalidObjectException("Illegal load factor: " + loadFactor); 133 } 134 135 int size = s.readInt(); 136 if (size < 0) { 137 throw new InvalidObjectException("Illegal size: " + size); 138 } 139 140 capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f), HashMap.MAXIMUM_CAPACITY); 141 142 SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, HashMap.tableSizeFor(capacity)); 143 144 map = (((HashSet<?>)this) instanceof LinkedHashSet ? 145 new LinkedHashMap<E,Object>(capacity, loadFactor) : 146 new HashMap<E,Object>(capacity, loadFactor)); 147 148 for (int i=0; i<size; i++) { 149 @SuppressWarnings("unchecked") 150 E e = (E) s.readObject(); 151 map.put(e, PRESENT); 152 } 153 } 154 155 public Spliterator<E> spliterator() { 156 return new HashMap.KeySpliterator<E,Object>(map, 0, -1, 0, 0); 157 } 158 }
1 package java.util; 2 3 //LinkedHashSet继承自HashSet,内部使用的是LinkHashMap,好处是LinkedHashSet中的元素顺序是可以保证的,即遍历顺序和插入顺序是一致的。 4 //LinkedHashSet底层是 数组 + 单链表 + 红黑树 + 双向链表的数据结构 5 public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable { 6 7 private static final long serialVersionUID = -2851667679971038690L; 8 9 //调用HashSet中的LinkedHashMap 10 public LinkedHashSet(int initialCapacity, float loadFactor) { 11 super(initialCapacity, loadFactor, true); 12 } 13 14 //调用HashSet中的 LinkedHashMap 的构造方法,该方法初始化了初始起始容量,以及加载因子, 15 //dummy 参数没有作用这里可以忽略,在HashSet中只是为了区别其他的构造方法而已 16 public LinkedHashSet(int initialCapacity) { 17 super(initialCapacity, .75f, true); 18 } 19 20 //初始化 LinkedHashMap 的初始容量为诶 16 加载因子为 0.75f 21 public LinkedHashSet() { 22 super(16, .75f, true); 23 } 24 25 //初始化 LinkedHashMap 的初始容量为 Math.max(2*c.size(), 11) 加载因子为 0.75f 26 public LinkedHashSet(Collection<? extends E> c) { 27 super(Math.max(2*c.size(), 11), .75f, true); 28 addAll(c); 29 } 30 31 // 32 @Override 33 public Spliterator<E> spliterator() { 34 return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.ORDERED); 35 } 36 }
posted on 2018-11-11 23:28 liuyongwade 阅读(132) 评论(0) 收藏 举报
浙公网安备 33010602011771号