java源码阅读HashSet

1类签名与注解

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable

此类实现Set接口,由哈希表(实际为HashMap实例)支持。 对集合的迭代次序不作任何保证。特别是,它不能保证顺序在一段时间内保持不变(HashMap的扩容重hash)。 这个类允许null元素。

请注意,此实现不同步。 如果多个线程并发访问哈希集,并且至少有一个线程修改该集合,那么它必须在外部进行同步。 这通常通过在自然地封装集合的一些对象上进行同步来实现。 如果没有这样的对象存在,那么该集合应该使用Collections.synchronizedSet方法“包装”。 这最好在创建时完成,以防止对该集合的意外不同步访问:

 Set s = Collections.synchronizedSet(new HashSet(...)); 

该类iterator方法返回的迭代器是故障快速的。此机制在HashMap一节中有详细讲述。

2属性

static final long serialVersionUID = -5024744406713321676L;

private transient HashMap<E,Object> map;

// Map中的一个虚拟值
private static final Object PRESENT = new Object();

HashSet是通过HashMap实现的,所以内部持有map的引用,Set的值对应着Map中的key,但是每次map的插入需要是<key,value>的键值对,所以就有了虚拟的value对象,就是PRESENT。

3构造方法

//1默认
public HashSet() {
        map = new HashMap<>();
    }

//2通过集合构造
public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

//3指定HashMap的初始化容量和负载因子
public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }

//4指定HashMap的初始化容量
public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

//5指定初始化容量和负载因子,内部通过LinkedHashMap实现
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

注意:构造方法5的dummy参数其实没有特别的意义,唯一作用是通过多一个参数来区别构造方法3(方法重载)。

4常用方法

(1)add

public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

(2)remove

public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

(3)contains

public boolean contains(Object o) {
        return map.containsKey(o);
    }

(4)其他常用

public Iterator<E> iterator() {
        return map.keySet().iterator();
    }

public int size() {
        return map.size();
    }

public boolean isEmpty() {
        return map.isEmpty();
    }

HashSet的常用方法都是通过调用HashMap的方法实现的。

5其他

(1)clone

public Object clone() {
        try {
            HashSet<E> newSet = (HashSet<E>) super.clone();
            newSet.map = (HashMap<E, Object>) map.clone();
            return newSet;
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }
    }

(2)序列化与反序列化

//序列化
private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // Write out any hidden serialization magic
        s.defaultWriteObject();

        // Write out HashMap capacity and load factor
        s.writeInt(map.capacity());
        s.writeFloat(map.loadFactor());

        // Write out size
        s.writeInt(map.size());

        // Write out all elements in the proper order.
        for (E e : map.keySet())
            s.writeObject(e);
    }

//反序列化
private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in any hidden serialization magic
        s.defaultReadObject();

        // 读capacity(检查非负).
        int capacity = s.readInt();
        if (capacity < 0) {
            throw new InvalidObjectException("Illegal capacity: " + capacity);
        }

        // 读负载因子(不能为null).
        float loadFactor = s.readFloat();
        if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
            throw new InvalidObjectException("Illegal load factor: " + loadFactor);
        }

        // Read size and verify non-negative.
        int size = s.readInt();
        if (size < 0) {
            throw new InvalidObjectException("Illegal size: " + size);
        }
        // 计算需要的capacity
        capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
                HashMap.MAXIMUM_CAPACITY);

        // Constructing the backing map will lazily create an array when the first element is
        // added, so check it before construction. Call HashMap.tableSizeFor to compute the
        // actual allocation size. Check Map.Entry[].class since it‘s the nearest public type to
        // what is actually created.

        SharedSecrets.getJavaOISAccess()
                     .checkArray(s, Map.Entry[].class, HashMap.tableSizeFor(capacity));

        // 构造HashMap对象
        map = (((HashSet<?>)this) instanceof LinkedHashSet ?
               new LinkedHashMap<E,Object>(capacity, loadFactor) :
               new HashMap<E,Object>(capacity, loadFactor));

        // 往HashMap中添加键值对
        for (int i=0; i<size; i++) {
            @SuppressWarnings("unchecked")
                E e = (E) s.readObject();
            map.put(e, PRESENT);
        }
    }

(3)equals

HashSet类中没有实现equls方法,而是从父类AbstractSet中继承过来的。AbstractSet中equls实现如下:

public boolean equals(Object o) {
        if (o == this)
            return true;

        if (!(o instanceof Set))
            return false;
        Collection<?> c = (Collection<?>) o;
        if (c.size() != size())
            return false;
        try {
            return containsAll(c);
        } catch (ClassCastException unused)   {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }
    }

public boolean containsAll(Collection<?> c) {
        for (Object e : c)
            if (!contains(e))
                return false;
        return true;
    }

public boolean contains(Object o) {
        Iterator<E> it = iterator();
        if (o==null) {
            while (it.hasNext())
                if (it.next()==null)
                    return true;
        } else {
            while (it.hasNext())
                if (o.equals(it.next()))
                    return true;
        }
        return false;
    }

equals首先判断是否引用同1个对象,若是则返回true。

否则,判断是否都是Set类型的,若不是则返回false。

若是,判断是否都有相等的size,若不是则返回false。

若是,则调用containsAll是否包含所有元素,若是则返回true,否则返回false。

原文地址:https://www.cnblogs.com/ouym/p/9007389.html

时间: 2024-10-06 09:27:42

java源码阅读HashSet的相关文章

Java源码阅读

源码阅读目的是为了了解Java原理,学习优秀的类设计,整体阅读顺序和侧重主要参考基础类和常用类,参考网上整体归纳如下: 包 java.lang 1) Object 1 2) String 1 3) AbstractStringBuilder 1 4) StringBuffer 1 5) StringBuilder 1 6) Boolean 2 7) Byte 2 8) Double 2 9) Float 2 10) Integer 2 11) Long 2 12) Short 2 13) Thr

如何阅读Java源码 阅读java的真实体会

刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧,如果你从来没有学过Java,或是任何一门编程语言如C++,一开始去啃<Core Java>,你是很难从中吸收到营养的,特别是<深入Java虚拟机>这类书,别人觉得好,未必适合现在的你. 虽然Tomcat的源码很漂亮,但我绝不建议你一开始就读它.我文中会专门谈到这个,暂时不展开. 强烈

Java源码阅读的真实体会

原文:http://zwchen.iteye.com/blog/1154193 刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧,如果你从来没有学过Java,或是任何一门编程语言如C++,一开始去啃<Core Java>,你是很难从中吸收到营养的,特别是<深入Java虚拟机>这类书,别人觉得好,未必适合现在的你. 虽然Tomcat的

[收藏] Java源码阅读的真实体会

收藏自http://www.iteye.com/topic/1113732 刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧,如果你从来没有学过Java,或是任何一门编程语言如C++,一开始去啃<Core Java>,你是很难从中吸收到营养的,特别是<深入Java虚拟机>这类书,别人觉得好,未必适合现在的你. 虽然Tomcat的源码

java源码之HashSet

1,HashSet介绍 1)HashSet 是一个没有重复元素的集合.2)它是由HashMap实现的,不保证元素的顺序,而且HashSet允许使用 null 元素.3)HashSet是非同步的.如果多个线程同时访问一个哈希 set,而其中至少一个线程修改了该 set,那么它必须 保持外部同步.这通常是通过对自然封装该 set 的对象执行同步操作来完成的.如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来"包装" set.最好在创建时完成这一

java源码阅读String

1类签名与注释 public final class String implements java.io.Serializable, Comparable<String>, CharSequence String类被定义为final类型的,所以String对象一旦创建了,就是不可变的. String类实现了Serializable接口,表示可以序列化. String类实现了Comparable<String>接口,表示String类型可以相互比较.(通过compareTo方法) S

【java SE】java 源码阅读 —— Object

看源码也不太懂怎么看,Object类的 java 代码很少,方法实现都靠 C/C++ ,主要看注释,然后自己理解.有不对的地方请指正 1. 概览 import jdk.internal.HotSpotIntrinsicCandidate; /** * native 关键字修饰的方法: * 1. 没有方法体 * 2. 将调用C/C++实现的方法(可以搜索一下JNI) */ public class Object { /** * 这个方法使JVM能找到本地的方法(C/C++实现的方法) */ pri

Java源码阅读Vector

1类注释 /** * The {@code Vector} class implements a growable array of * objects. Like an array, it contains components that can be * accessed using an integer index. However, the size of a * {@code Vector} can grow or shrink as needed to accommodate * a

Java源码阅读ArrayList

1简介 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable ArrayList使用一个可变数组实现List接口,实现了List接口的所有可选操作.ArrayList除了是非线程安全的之外,其他的与Vector类似. 2成员属性 //序列化版本号 private static final lon