集合家族——HashSet

Posted xiao-ran

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了集合家族——HashSet相关的知识,希望对你有一定的参考价值。

一、概述

  对于 HashSet 而言,它是基于 HashMap 来实现的,底层采用 HashMap 来保存元素。所以只要懂得Hashmap,HashSet就很简单了

 

二、特点

  2.1 无序性

  2.2 不可重复性

  2.3 HashSet 不是线程安全的

  2.4 集合元素可以是 null

 

三、定义

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

  HashSet 继承 AbstractSet 类,实现 Set、Cloneable、Serializable 接口。其中 AbstractSet 提供 Set 接口的骨干实现,从而最大限度地减少了实现此接口所需的工作。Set 接口是一种不包括重复元素的 Collection,它维持它自己的内部排序,所以随机访问没有任何意义。

  3.1 基本属性  

 //基于HashMap实现,底层使用HashMap保存所有元素
            private transient HashMap<E,Object> map;

            //定义一个Object对象作为HashMap的value
            private static final Object PRESENT = new Object();

  3.2 构造方法

 /**
             * 默认构造函数
             * 初始化一个空的HashMap,并使用默认初始容量为16和加载因子0.75。
             */
            public HashSet() {
                map = new HashMap<>();
            }

            /**
             * 构造一个包含指定 collection 中的元素的新 set。
             */
            public HashSet(Collection<? extends E> c) {
                map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
                addAll(c);
            }

            /**
             * 构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和指定的加载因子
             */
            public HashSet(int initialCapacity, float  loadFactor) {
                map = new HashMap<>(initialCapacity, loadFactor);
            }

            /**
             * 构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和默认的加载因子(0.75)。
             */
             public HashSet(int initialCapacity) {
               map = new HashMap<>(initialCapacity);
            }

            /**
             * 在API中我没有看到这个构造函数,今天看源码才发现 (原来访问权限为包权限,不对外公开的)
             * 以指定的initialCapacity和loadFactor构造一个新的空链接哈希集合。
             * dummy 为标识 该构造函数主要作用是对LinkedHashSet起到一个支持作用
             */
            HashSet(int initialCapacity, float loadFactor, boolean dummy) {
               map = new LinkedHashMap<>(initialCapacity, loadFactor);
            }

 

四、方法

  4.1 iterator()

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

  iterator() 方法返回对此 set 中元素进行迭代的迭代器。返回元素的顺序并不是特定的。底层调用 HashMap 的 keySet 返回所有的 key,这点反应了 HashSet 中的所有元素都是保存在 HashMap 的 key 中,value 则是使用的 PRESENT 对象,该对象为 static final。

  4.2 size()

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

  size() 返回此 set 中的元素的数量(set 的容量)。底层调用 HashMap的 size 方法,返回 HashMap 容器的大小。

  4.3 contains()

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

  contains(),判断某个元素是否存在于 HashSet() 中,存在返回 true,否则返回 false。更加确切的讲应该是要满足这种关系才能返回true:(o==null ? e==null : o.equals(e))。底层调用 containsKey 判断 HashMap 的 key 值是否为空。

  4.4 add() 

  我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,
  此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断
  数组此位置上是否已经有元素:
    如果此位置上没有其他元素,则元素a添加成功。 --->情况1
    如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
    如果hash值不相同,则元素a添加成功。--->情况2
    如果hash值相同,进而需要调用元素a所在类的equals()方法:
      equals()返回true,元素a添加失败
      equals()返回false,则元素a添加成功。--->情况2

  对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据以链表的方式存储。
  jdk 7 :元素a放到数组中,指向原来的元素。
  jdk 8 :原来的元素在数组中,指向元素a
  总结:七上八下

  HashSet底层:数组+链表的结构。

  技术图片

 

 

 

五、HashSet注意

  向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()

 

六、简谈LinkedHashSet

  • LinkedHashSet 是 HashSet 的子类
  • LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置, 但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入 顺序保存的。
  • LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全 部元素时有很好的性能。
  • LinkedHashSet 不允许集合元素重复。

  技术图片

以上是关于集合家族——HashSet的主要内容,如果未能解决你的问题,请参考以下文章

集合和泛型

java 集合大家族

Java - 集合

集合之集合的简介

Java集合 -- HashSet 和 HashMap

第1章 HashSet集合