深入源码探讨HashSet_Java

我们在工作中时常会用到HashSet,面试也有时候容易被问到,下面咱们就来聊聊HashSet

使用案例

public class Test {
    public static void main(String[] args) {
        HashSet<String> hashSet = new HashSet<>();
        hashSet.add("Java");
        hashSet.add("R");
        hashSet.add("C");
        hashSet.add("C#");
        hashSet.add("C#");
        hashSet.add("Java");
        hashSet.add(null);
        System.out.println("hashSet的长度:" + hashSet.size());
        //第一种遍历方式
        System.out.println("第一种方式遍历");
        Iterator<String> iterator = hashSet.iterator();
        while (iterator.hasNext()) {
            System.out.print(iterator.next());
            System.out.print(" ");
        }
        System.out.println("");
        System.out.println("第二种方式遍历");
        for (String string : hashSet) {
            System.out.print(string);
            System.out.print(" ");
        }
    }
}

输出

hashSet的长度:5
第一种方式遍历
C# null Java R C
第二种方式遍历
C# null Java R C

从上面的例子可以得知几个结果:

1,不能放入重复的。

2,不是按照放入的顺序存放的。

3,null也可以存放。

关键源码

HashSet的类图

深入源码探讨HashSet_Java_02

我们来看看HashSet到底是怎么玩的

深入源码探讨HashSet_Java_03

JDK1.2开始有的HashSet,实现了解耦Set,CloneableSerializable

  1. Set是个接口,定义了很多方法。
  2. Cloneable可以对象克隆。
  3. Serializable可以序列化与反序列。
  4. 继承了抽象类AbstractSet

AbstractSet也是Set接口的实现类,主要有以下几个方法:

深入源码探讨HashSet_Java_04

AbstractSet实现了三个方法equalshashCoderemoveAll。然后所有继承AbstractSet的子类都不用自己去实现此三个方法。

Set定义了如下方法:

深入源码探讨HashSet_java_05

HashSet中关键的变量和方法:

//定义了一个HashMap类型的变量
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
//与备份Map中的对象关联的虚拟值
private static final Object PRESENT = new Object();
public HashSet() {
    map = new HashMap<>();
}
//设置HashSet的容量其实就是设置HashMap的容量
public HashSet(int initialCapacity) {
    map = new HashMap<>(initialCapacity);
}
//求HashSet的大小就是求HashMap
public int size() {
   return map.size();
}
//往HashSet存放数据其实就是往HashMap存放数据
//数据作为key,然后value就是固定Object对象PRESENT
public boolean add(E e) {
   return map.put(e, PRESENT)==null;
}
public Iterator<E> iterator() {
   return map.keySet().iterator();
}
public int size() {
   return map.size();
}
....
//序列化与反序列化
private void writeObject(java.io.ObjectOutputStream s){
    //...
}
private void readObject(java.io.ObjectInputStream s){
    //...
}

可以看出,HashSet的操作都是基于HashMap的操作。

总结

HashSet的所有操作都是基于HashMap进行操作,用存放进HashSet的数据作为HashMap的key在使用一个固定Object对象作为HashMap的value。

在这金三银四的季节,栈长为大家准备了四份面试宝典:


《java面试宝典5.0》


《Java(BAT)面试必备》


《350道Java面试题:整理自100+公司》


《资深java面试宝典-视频版》


大量电子书籍


分别适用于初中级,中高级,以及资深级工程师的面试复习。


内容包含java基础、javaweb、各个性能优化、JVM、锁、高并发、反射、Spring原理、微服务、Zookeeper、数据库、数据结构、限流熔断降级等等。