zoukankan      html  css  js  c++  java
  • HashSet源码分析

    HashSet底层是HashMap实现的,关于HashMap的分析请移步到HashMap源码分析

    属性

    //底层使用HashMap来实现
    private transient HashMap<E,Object> map;
     
    //虚拟的Object对象作为HashMap的value
    private static final Object PRESENT = new Object();

    HashSet底层是使用HashMap实现的,由于HashMap存储的是<Key,Value>键值对,而HashSet不需要Value,所以HashSet内部使用了一个虚拟的Object对象作为底层HashMap的值。

    构造方法

    public HashSet() {
        //底层会初始化一个空的HashMap,并使用默认初始容量为16和加载因子0.75。
            map = new HashMap<>();
        }
        
        public HashSet(Collection<? extends E> c) {
            map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
            addAll(c);
        }
     
        public HashSet(int initialCapacity, float loadFactor) {
            map = new HashMap<>(initialCapacity, loadFactor);
        }
     
        public HashSet(int initialCapacity) {
            map = new HashMap<>(initialCapacity);
        }
     
        HashSet(int initialCapacity, float loadFactor, boolean dummy) {
            map = new LinkedHashMap<>(initialCapacity, loadFactor);
        }

    初始化HashSet时,可以指定初始化容量和负载因子。因为HashSet是由HashMap来实现,如果不指定,则默认值都与HashMap中的一样,即默认初始化容量为16,默认负载因子为0.75。

    add方法

    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
     
    //HashMap的put方法
    public V put(K key, V value) {  
        //map为空表时,进行扩充  
        if (table == EMPTY_TABLE) {  
            inflateTable(threshold);  
        }  
        //如果key为null,直接定位到table[0]处,进行处理  
        if (key == null)  
            return putForNullKey(value);  
        //计算key的hash值  
        int hash = hash(key);  
        //根据key的hash,定位key在table中索引  
        int i = indexFor(hash, table.length);  
        //判断key是否存在  
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {  
            Object k;  
            //如果key已存在,则覆盖原value  
            //【判断key相等】:也就是判断两个Object是否相等  
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
                //暂存旧值
                V oldValue = e.value; 
                //用新值覆盖旧值
                e.value = value;  
                e.recordAccess(this);  
                //返回旧值(方法返回后,可能还要用到旧值)  
                return oldValue;  
            }  
        }//key不存在         
        //修改次数+1  
        modCount++;  
        //添加<k,v>  
        addEntry(hash, key, value, i);  
        return null;  
    } 

    从源码可以看出,HashSet如果添加了重复的元素,即key已存在时,会进行覆盖操作。 借助该特点,可以使用HashSet实现去重操作。

    面试题:
    1.HashSet的实现原理?

    2.Set中元素是无序的

    HashSet set=new HashSet();
    set.add("a");
    set.add("b");
    set.add("c");
    set.add("d");
    System.out.println(set);//结果:[d,b,c,a]

    3.HashSet不允许重复

    情景0

    //两次添加"a"
    HashSet set=new HashSet();
    System.out.println(set.add("a"));//结果:true
    set.add("b");
    set.add("c");
    set.add("d");
    System.out.println(set.add("a"));//结果:false

    情景1

    //两次分别添加不同的对象。
    HashSet set=new HashSet();
     System.out.println(set.add(new People("张三")));//true
    System.out.println(set.add(new People("张三")));//true

    情景2

    //两次都是添加p1
    HashSet set=new HashSet();
            
    People p1=new People("张三");
    System.out.println(set.add(p1));//true
    System.out.println(set.add(p1));//false

    情景3

    //两次分别添加s1,s2(显然,s1和s2是不同的对象)。
    HashSet set=new HashSet();
            
    String s1=new String("a");
    String s2=new String("a");
     
    System.out.println(set.add(s1));//true
    System.out.println(set.add(s2));//false

    4.HashSet如何保证不重复的?

    hashset底层用的hashmap实现,hashset实际上是没有value的,只是用了一个虚拟的value(Object PRESENT),每个key的值都是该value。

    当要插入一个存在的对象时,hashmap对相同的key则进行value值覆盖操作,所以相当于用新的<key,PRESENT>覆盖掉旧的<key,PRESENT>。所以表面看起来没有插入新的重复元素,也就保证了不重复。

    5.HashSet添加元素的过程

    ①HashCode
    当HashSet在添加元素时,会先调用hashCode()方法,判断即将加入的元素的hashCode是否与集合中的元素有相同的,如果没有,则允许添加该元素。如果有相同的,则继续调用equals()方法,如果equals()方法返回true,则表示对象已经加入过了,不允许再添加了。否则,如果equels方法返回false,则允许添加新元素。
    ②equals

    对于两个对象来说,如果使用equals返回true, 则这两个对象的hashcode一定相同。
    对于两个对象来说,如果使用equals返回false,则这两个对象的hashcode不一定不相同(可以相同或者不同)。如果不同,可以提高性能。

    对于Object类来说,不同的Object对象的hashcode值是不同的(hashCode值表示对象的地址)

    String类的hashCode()方法重写了Object类的hashCode()方法,只要两个String对象的内容相同则认为hashCode相同,所以情景3比较特殊。

  • 相关阅读:
    大数据之MapReduce工作机制
    swoft +nginx 快速获取客户端的真实的IP
    JWT 介绍 详解
    swoft 切面编程 (AOP编程思想)
    CSP 编程模型
    Ubuntu 16.04 Mysql 重置密码
    ubuntu 提示:rm: cannot remove 'you-get/tmp': Directory not empty
    Ubuntu 系统升级到php7.2/7.3 (平滑升级)-朝花夕拾
    git clone 非标准的ssh端口(非22)端口
    Ubuntu 升级npm 以及安装cross-env 过程中遇到的问题
  • 原文地址:https://www.cnblogs.com/rouqinglangzi/p/10291706.html
Copyright © 2011-2022 走看看