Java中的LinkedHashSet指南

2023/06/07

1. 概述

在本文中,我们将探讨Java Collection API的LinkedHashSet。我们将深入研究此数据结构的特性并演示其功能。

2. LinkedHashSet介绍

LinkedHashSet是属于Java.util库的通用数据结构。它是HashSet数据结构的直接后代,因此在每个给定时间都包含非重复元素。

除了具有贯穿其所有元素的双向链表之外,它的实现与HashSet的不同之处在于它保持可预测的迭代顺序。迭代顺序由元素插入集合的顺序定义。

LinkedHashSet使客户端免于HashSet提供的不可预测的排序,而不会招致TreeSet带来的复杂性

虽然它的性能可能略低于HashSet,但由于维护链表的额外费用,它在add()、contains()和remove()操作方面具有恒定时间(O1)性能

3. 创建LinkedHashSet

有几个构造函数可用于创建LinkedHashSet,让我们来看看它们中的每一个:

3.1 默认无参数构造函数

Set<String> linkedHashSet = new LinkedHashSet<>();
assertTrue(linkedHashSet.isEmpty());

3.2 使用初始容量创建

初始容量表示LinkedHashSet的初始长度,提供初始容量可防止Set在增长时不必要地调整其大小。默认初始容量为16:

LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>(20);

3.3 从集合创建

我们还可以使用Collection的内容在创建时填充LinkedHashSet对象:

@Test
void whenCreatingLinkedHashSetWithExistingCollection_shouldContainAllElementOfCollection(){
    Collection<String> data = Arrays.asList("first", "second", "third", "fourth", "fifth");
    LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>(data);

    assertFalse(linkedHashSet.isEmpty());
    assertEquals(data.size(), linkedHashSet.size());
    assertTrue(linkedHashSet.containsAll(data) && data.containsAll(linkedHashSet));
}

3.4 使用初始容量和负载因子创建

当LinkedHashSet的大小增长到超过初始容量的值时,新容量是负载因子乘以之前的容量。在下面的代码片段中,初始容量设置为20,负载因子为3。

LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>(20, 3);

默认负载因子为0.75。

4. 向LinkedHashSet添加元素

我们可以分别使用add()和addAll()方法将单个元素或元素集合添加到LinkedHashSet。如果Set中尚不存在某个元素,则会添加该元素。当元素被添加到集合中时,这些方法返回true,否则返回false

4.1 添加单个元素

这是将元素添加到LinkedHashSet的实现:

@Test
void whenAddingElement_shouldAddElement(){
    Set<Integer> linkedHashSet = new LinkedHashSet<>();
    assertTrue(linkedHashSet.add(0));
    assertFalse(linkedHashSet.add(0));
    assertTrue(linkedHashSet.contains(0));
}

4.2 添加元素集合

如前所述,我们还可以将元素集合添加到LinkedHashSet中:

@Test
void whenAddingCollection_shouldAddAllContentOfCollection(){
    Collection<Integer> data = Arrays.asList(1,2,3);
    LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>();

    assertTrue(linkedHashSet.addAll(data));
    assertTrue(data.containsAll(linkedHashSet) && linkedHashSet.containsAll(data));
}

不添加重复元素的规则也适用于addAll()方法,如下所示:

@Test
void whenAddingCollectionWithDuplicateElements_shouldMaintainUniqueValuesInSet(){
    LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>();
    linkedHashSet.add(2);
    Collection<Integer> data = Arrays.asList(1, 1, 2, 3);

    assertTrue(linkedHashSet.addAll(data));
    assertEquals(3, linkedHashSet.size());
    assertTrue(data.containsAll(linkedHashSet) && linkedHashSet.containsAll(data));
}

请注意,data变量包含重复值1,并且LinkedHashSet在调用addAll()方法之前已包含整数值2。

5. 遍历LinkedHashSet

与Collection的所有其他后代一样,我们可以遍历LinkedHashSet。LinkedHashSet中有两种类型的迭代器可用:IteratorSpliterator

前者只能对Collection进行遍历和执行任何基本操作,而后者将Collection拆分为子集,并在每个子集上并行执行不同的操作,从而使其成为线程安全的

5.1 使用Iterator进行迭代

@Test
void whenIteratingWithIterator_assertThatElementIsPresent(){
    LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>();
    linkedHashSet.add(0);
    linkedHashSet.add(1);
    linkedHashSet.add(2);

    Iterator<Integer> iterator = linkedHashSet.iterator();
    for (int i = 0; i < linkedHashSet.size(); i++) {
        int nextData = iterator.next();
        assertEquals(i, nextData);
    }
}

5.2 使用Spliterator进行迭代

@Test
void whenIteratingWithSpliterator_assertThatElementIsPresent(){
    LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>();
    linkedHashSet.add(0);
    linkedHashSet.add(1);
    linkedHashSet.add(2);

    Spliterator<Integer> spliterator = linkedHashSet.spliterator();
    AtomicInteger counter = new AtomicInteger();
    spliterator.forEachRemaining(data -> {
        assertEquals(counter.get(), (int)data);
        counter.getAndIncrement();
    });
}

6. 从LinkedHashSet中删除元素

以下是从LinkedHashSet中删除元素的不同方法:

6.1 remove()

如果我们知道要删除的确切元素,则此方法会从Set中删除一个元素。它接收一个参数,该参数是我们要删除的实际元素,如果成功删除则返回true,否则返回false:

@Test
void whenRemovingAnElement_shouldRemoveElement(){
    Collection<String> data = Arrays.asList("first", "second", "third", "fourth", "fifth");
    LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>(data);

    assertTrue(linkedHashSet.remove("second"));
    assertFalse(linkedHashSet.contains("second"));
}

6.2 removeIf()

removeIf()方法删除满足指定谓词条件的元素。下面的示例删除了LinkedHashSet中大于2的所有元素:

@Test
void whenRemovingAnElementGreaterThanTwo_shouldRemoveElement(){
    LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>();
    linkedHashSet.add(0);
    linkedHashSet.add(1);
    linkedHashSet.add(2);
    linkedHashSet.add(3);
    linkedHashSet.add(4);

    linkedHashSet.removeIf(data -> data > 2);
    assertFalse(linkedHashSet.contains(3));
    assertFalse(linkedHashSet.contains(4));
}

6.3 使用Iterator删除

迭代器也是我们可以用来从LinkedHashSet中删除元素的另一个选项,Iterator的remove()方法删除Iterator当前所在的元素:

@Test
void whenRemovingAnElementWithIterator_shouldRemoveElement(){
    LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>();
    linkedHashSet.add(0);
    linkedHashSet.add(1);
    linkedHashSet.add(2);

    Iterator<Integer> iterator = linkedHashSet.iterator();
    int elementToRemove = 1;
    assertTrue(linkedHashSet.contains(elementToRemove));
    while(iterator.hasNext()){
        if(elementToRemove == iterator.next()){
            iterator.remove();
        }
    }
    assertFalse(linkedHashSet.contains(elementToRemove));
}

7. 总结

在本文中,我们研究了Java Collection库中的LinkedHashSet数据结构。我们演示了如何通过不同的构造函数创建LinkedHashSet,添加和删除元素,以及遍历它。我们还了解了该数据结构的底层、它相对于HashSet的优势以及其常见操作的时间复杂度。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章