Java源码研究002:HashSet保持元素唯一性

先是往HashSet中添加元素用到的add()方法

public class demo{
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<String>();
       set.add("ada");
    }
}    

我们找一下源码,add()方法

package java.util;
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable
{
  private transient HashMap<E,Object> map;
  
public boolean add(E e) { return map.put(e, PRESENT)==null; } }

我们可以看到,调了一个map的put()方法,而且我们也可以注意到,HashSet底层是用HashMap实现的

现在我们追踪map.put()方package java.uti

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable 
{   
  final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;

     //如果hash未初始化,就对其进行初始化
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;

     //根据对象的hash值计算对象的储存位置,如果该位置没有元素,就存储元素
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);

        else {
            Node<K,V> e; K k;
       /*
              如果该位置有元素,则进行比较
        存入的元素和之前的元素比较hash值
          如果hash值不同,则会向下执行,将元素添加到集合
          如果hash值相同,会调用对象的equals()方法
            如果返回false会继续向下执行,把元素添加进集合
            如果返回true,e = p; 说明元素重复,不存储 !
        */
        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);
            else {
                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;
    }


  public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
   }

  static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
  }
}              

关于HashSet如何保持元素唯一性的解析已经写在注解里了。

 

另外

我们发现put方法又调用了同类里的putVal()作为返回值

我们看到put方法中酱hash(key)作为参数传入了putVal

hash值和元素的hashCode方法相关。

putVal()方法的作用是

追踪key.hashCode()到Object类

package java.lang;
public class Object { @HotSpotIntrinsicCandidate public native int hashCode(); }

根据这个方法的声明可知,该方法返回一个int类型的数值,并且是本地方法,因此在Object类中并没有给出具体的实现。

ps:(

说到本地方法>>>JAVA中有两种方法:JAVA方法和本地方法

JAVA方法是由JAVA编写的,编译成字节码,存储在class文件中

本地方法是由其它语言编写的,编译成和处理器相关的机器代码

本地方法保存在动态链接库中,即.dll(windows系统)文件中,格式是各个平台专有的

JAVA方法是与平台无关的,但是本地方法是要依赖平台的。

 

 

没写完,回来补上!

 

posted @ 2020-12-28 13:06  佩洛君  阅读(93)  评论(0编辑  收藏  举报