博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java HashSet
阅读量:337 次
发布时间:2019-03-04

本文共 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 + '\'' + '}'; }}
  • HashSet底层是HashMap
  • 添加一个元素时,先得到Hash值,会转成 索引值
  • 找到存储数据表table,看到这个索引位置是否已经存放有元素
  • 如果没有,直接加入
  • 如果有,调用equals比较,如果相同,就放弃添加,如果不相同,则添加到最后
  • 在java8中,如果一条链表的元素个数到达(默认是8),并且table的大小>=MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)

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/

你可能感兴趣的文章