本文共 7146 字,大约阅读时间需要 23 分钟。
public class HashSetDemo02 { public static void main(String[] args) { /** * 底层是 HashMap * public HashSet() { * map = new HashMap<>(); * } */ HashSet hashSet = new HashSet(); /** * public boolean add(E e) { * return map.put(e, PRESENT)==null; * } */ System.out.println(hashSet.add("java")); System.out.println(hashSet.add("spring")); // false System.out.println(hashSet.add("java")); System.out.println(hashSet.add("python")); System.out.println(hashSet.add("Vue")); hashSet.remove("java"); // [spring, python, Vue] System.out.println(hashSet); System.out.println("========================="); hashSet = new HashSet(); // hashSet= [] System.out.println("hashSet= " + hashSet); // hashSet 不能添加相同的元素/数据 hashSet.add("lucy"); hashSet.add("lucy"); hashSet.add(new Dog("tom")); hashSet.add(new Dog("tom")); // hashSet= [Dog{name='tom'}, Dog{name='tom'}, lucy] System.out.println("hashSet= " + hashSet); System.out.println("========================="); // 非常经典的面试题 hashSet.add(new String("ll")); hashSet.add(new String("ll")); // [ll, Dog{name='tom'}, Dog{name='tom'}, lucy] System.out.println(hashSet); }}class Dog { private String name; public Dog(String name) { this.name = name; } @Override public String toString() { return "Dog{" + "name='" + name + '\'' + '}'; }}
1.执行构造器
public HashSet() { map = new HashMap<>(); }
2.执行add
private static final Object PRESENT = new Object();
public boolean add(E e) { return map.put(e, PRESENT)==null; }
3.执行put方法
static final int hash(Object key) { int h; // 得到key对应的hash值 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
4.执行putVal方法
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node[] tab; Node p; int n, i; // transient Node [] table; // table是HashMap的一个属性,类型是Node[] // if 语句表示如果当前table是null, 或者 大小 = 0 // 就进行第一次扩容,得到16个空间 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; // (1)根据key, 得到hash,去计算该key应该存放到table表的哪个索引位置 // 并把这个位置的对象,赋给p // (2)判断p 是否为null // private static final Object PRESENT = new Object(); // (2.1) 如果p为null, 表示还没有存放元素,就创建一个Node(key="java",value=PRESENT) // (2.2) 就放在 tab[i] = newNode(hash, key, value, null); /** * Node newNode(int hash, K key, V value, Node next) { * return new Node<>(hash, key, value, next); * } */ // 这里判断元素是否重复 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { //当元素重复时 // 一个开发技巧: 在需要局部变量的时候(辅助变量)时候,在创建 Node e; K k; // 如果当前索引位置对应的链表的第一个元素和准备添加的key的hash值一样 // 并且满足 下面两个条件之一: // (1)准备加入的key 和 p 指向的Node 节点的 key 是同一个对象 // (2) p 指向的Node 节点的 key 的 equals() 和 准备加入的key比较后相同 // 就不能加入 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; // 判断p是不是一颗红黑树, // 如果是一颗红黑树, 就调用putTreeVal来添加 else if (p instanceof TreeNode) e = ((TreeNode )p).putTreeVal(this, tab, hash, key, value); else { // 如果table对应索引位置,已经是一个链表,就使用for循环比较 // (1)依次和该链表的每一个元素比较后,都不相同,则加入到该链表的最后 // 把元素添加到链表后,立即判断, 该链表是否已经达到8个节点 // ,就调用 treeifyBin() 对当前这个链表进行树化(转成红黑树) // 注意,在转成红黑树时,还进行判断,判断条件如下 // if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) // resize(); // 如果上面条件成立,先table扩容 // 只有上面条件不成立时,才进行转成红黑树 // (2)依次和该链表的每一个元素比较过程中,如果有相同情况,就直接break 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; }
resize方法
final Node[] resize() { Node [] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold; int newCap, newThr = 0; if (oldCap > 0) { if (oldCap >= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; // double threshold } else if (oldThr > 0) // initial capacity was placed in threshold newCap = oldThr; else { // zero initial threshold signifies using defaults // static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 newCap = DEFAULT_INITIAL_CAPACITY; // static final float DEFAULT_LOAD_FACTOR = 0.75f; // newThr => (int)(0.75 * 16) => 12 newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } threshold = newThr; @SuppressWarnings({ "rawtypes","unchecked"}) Node [] newTab = (Node [])new Node[newCap]; table = newTab; if (oldTab != null) { for (int j = 0; j < oldCap; ++j) { Node e; if ((e = oldTab[j]) != null) { oldTab[j] = null; if (e.next == null) newTab[e.hash & (newCap - 1)] = e; else if (e instanceof TreeNode) ((TreeNode )e).split(this, newTab, j, oldCap); else { // preserve order Node loHead = null, loTail = null; Node hiHead = null, hiTail = null; Node next; do { next = e.next; if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } else { if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; }
转载地址:http://thce.baihongyu.com/