JDK1.8源码解读之 LinkedHashSet

153 阅读3分钟

前言

  • Set接口的哈希表和链表实现,具有可预测的迭代顺序。
  • 此实现与HashSet的不同之处在于,它维护在其所有条目中运行的双向链接列表。
  • 此链表定义了迭代顺序,即元素插入到集合中的顺序(插入顺序)。
  • 请注意,如果将元素重新插入到集合中,则插入顺序不会受到影响。
  • 此类提供所有可选的Set操作,并允许空元素。
  • 像HashSet一样,它为基本操作(添加,包含和删除)提供恒定时间的性能,假设哈希函数将元素正确地分散在存储桶中。
  • 由于维护链接列表会增加开销,因此性能可能会略低于HashSet,
  • 但有一个例外:在LinkedHashSet上进行迭代需要的时间与集合的大小成正比,而不论其容量如何。
  • 在HashSet上进行迭代可能会更昂贵,需要的时间与其容量成正比。
  • 链接的哈希集具有两个影响其性能的参数:初始容量和负载因子。
  • 它们的定义与HashSet一样。
  • 但是请注意,与HashSet相比,此类为初始容量选择过高的值的惩罚不那么严重,因为此类的迭代时间不受容量的影响。
  • 请注意,此实现未同步。
  • 如果多个线程同时访问链接的哈希集,并且至少有一个线程修改了该哈希集,则必须在外部对其进行同步。
  • 通常,通过在自然封装了该集合的某个对象上进行同步来实现。
  • 如果不存在这样的对象,则应使用 Collections.synchronizedSet 方法来“包装”该集合。
  • 最好在创建时完成此操作,以防止意外地异步访问集合:
  • Set s = Collections.synchronizedSet(new LinkedHashSet(...));
  • 此类的迭代器方法返回的迭代器是快速失败的:
  • 如果在创建迭代器之后的任何时间以任何方式修改集合(通过迭代器自己的remove方法除外),
  • 则迭代器将抛出{@link ConcurrentModificationException}。
  • 因此,面对并发修改,迭代器会快速干净地失败,而不会在未来的不确定时间内冒任意,不确定的行为的风险
  • 注意,迭代器的快速失败行为无法得到保证,因为通常来说,在存在不同步的并发修改的情况下,不可能做出任何严格的保证。
  • 快速失败的迭代器会尽最大努力抛出ConcurrentModificationException。
  • 因此,编写依赖于此异常的程序的正确性是错误的:迭代器的快速失败行为仅应用于检测错误。

源码

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

    private static final long serialVersionUID = -2851667679971038690L;

    public LinkedHashSet(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor, true);
    }

    public LinkedHashSet(int initialCapacity) {
        super(initialCapacity, .75f, true);
    }

    public LinkedHashSet() {
        super(16, .75f, true);
    }

    public LinkedHashSet(Collection<? extends E> c) {
        super(Math.max(2*c.size(), 11), .75f, true);
        addAll(c);
    }

    @Override
    public Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.ORDERED);
    }
}

问题记录

  • 只有几个构造器,还有一个分割迭代器。1.8