HashSet、LinkedHashSet学习笔记


一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2) 的元素对 e1e2,并且最多包含一个 null 元素。正如其名称所暗示的,此接口模仿了数学上的 set 抽象。

注:如果将可变对象用作 set 元素,那么必须极其小心。如果对象是 set 中某个元素,以一种影响 equals 比较的方式改变对象的值,那么 set 的行为就是不确定的。此项禁止的一个特殊情况是不允许某个 set 包含其自身作为元素。

AbstractSet

此抽象类重写AbstractCollection的任何方法,只是单独的增加了equalshashCode 的实现

public abstract class AbstractSet<E> extends AbstractCollection<E> implements Set<E> {

    protected AbstractSet() {
    }

    // 比较指定对象与此 set的相等性,如果指定的对象也是一个set,并且两个set有相同的大小,元素,则返回true。
    public boolean equals(Object o) {
        if (o == this)
            return true;

        //1.先检查指定的对象是不是set
        if (!(o instanceof Set))
            return false;
        Collection<?> c = (Collection<?>) o;
        //2.再检查指定set的大小与这个set的大小是否相等
        if (c.size() != size())
            return false;
        try {
            //3.最后检查这个set是否包含指定set中的所有元素
            return containsAll(c);
        } catch (ClassCastException unused)   {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }
    }

    //返回此set的哈希码值:即该set中所有元素的哈希值之和
    public int hashCode() {
        int h = 0;
        Iterator<E> i = iterator();
        while (i.hasNext()) {
            E obj = i.next();
            if (obj != null)
                h += obj.hashCode();
        }
        return h;
    }

    //从此 set中移除包含在指定 collection中的所有元素
    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        boolean modified = false;

        //如果此set的大小>指定集合c的大小
        if (size() > c.size()) {
            //迭代指定集合c中的元素,并在此set中逐个删除
            for (Iterator<?> i = c.iterator(); i.hasNext(); )
                modified |= remove(i.next()); 
        } else {
            for (Iterator<?> i = iterator(); i.hasNext(); ) {
                if (c.contains(i.next())) {
                    i.remove();
                    modified = true;
                }
            }
        }
        return modified;
    }
}

HashSet

概述

HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持。主要具有以下的特点:

  • 不保证set的迭代顺序,特别是它不保证该顺序恒久不变

  • 有且只允许一个null元素

  • 不允许有重复元素,这是因为HashSet是基于HashMap实现的,HashSet中的元素都存放在HashMap的key上面,而value中的值都是统一的一个private static final Object PRESENT = new Object();

  • 非同步的。如果多 个线程同时访问一个哈希set,而其中至少一个线程修改了该 set,那么它必须保持外部同步。这通常是通过对自然封装该set的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装” set。最好在创建时完成这一操作,以防止对该set进行意外的不同步访问:

Set s = Collections.synchronizedSet(new HashSet());
  • HashSet通过iterator()返回的迭代器是fail-fast的

类继承实现关系

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

类属性

//hashMap
    private transient HashMap<E,Object> map;

    //HashMap的value
    private static final Object PRESENT = new Object();

构造方法

public HashSet() {
        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);
    }
//不对外公开的一个构造方法(默认default修饰),底层构造的是LinkedHashMap,dummy只是一个标示参数,无具体意义。 给LinkedHashSet调用的
        HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

其他的所有方法都是调用的HashMap的方法。只需要知道HashSet的特点就可以了。

LinkedHashSet

特点和HashSet一样。基于LinkedHashMap的双向链表。构造方法都是调用HashSet那个默认的构造方法,创建的是LinkedHashMap。有序,有且只有一个null,不允许重复,线程不安全

posted @ 2020-04-07 15:05  无话可说丶  阅读(294)  评论(0编辑  收藏  举报