Contents

Guava 中Set简介

1. 概述

在本教程中,我们将说明可以利用 Guava 处理 Java Set的最有用的方法。

让我们从非常简单的开始,使用 Guava创建一个没有 new 运算符的HashSet

Set<String> aNewSet = Sets.newHashSet();

2. 集合并集

首先,让我们看看如何对 Sets 进行联合操作——使用简单的Sets.union() API:

@Test
public void whenCalculatingUnionOfSets_thenCorrect() {
    Set<Character> first = ImmutableSet.of('a', 'b', 'c');
    Set<Character> second = ImmutableSet.of('b', 'c', 'd');
    Set<Character> union = Sets.union(first, second);
    assertThat(union, containsInAnyOrder('a', 'b', 'c', 'd'));
}

3. 集合的笛卡尔积

我们还可以使用*Sets.cartesianProduct()*获得**两组的乘积,**如下例所示:

@Test
public void whenCalculatingCartesianProductOfSets_thenCorrect() {
    Set<Character> first = ImmutableSet.of('a', 'b');
    Set<Character> second = ImmutableSet.of('c', 'd');
    Set<List<Character>> result =
      Sets.cartesianProduct(ImmutableList.of(first, second));
    Function<List<Character>, String> func =
      new Function<List<Character>, String>() {
        public String apply(List<Character> input) {
            return Joiner.on(" ").join(input);
        }
    };
    Iterable<String> joined = Iterables.transform(result, func);
    assertThat(joined, containsInAnyOrder("a c", "a d", "b c", "b d"));
}

请注意 - 为了能够轻松测试结果,我们使用FunctionJoiner将复杂的*Set<*List<Character»结构转换为更易于管理的Iterable<String>

4. 设置intersection

接下来——让我们看看如何使用Sets.intersection() API获取两个集合之间的交集:

@Test
public void whenCalculatingSetIntersection_thenCorrect() {
    Set<Character> first = ImmutableSet.of('a', 'b', 'c');
    Set<Character> second = ImmutableSet.of('b', 'c', 'd');
    Set<Character> intersection = Sets.intersection(first, second);
    assertThat(intersection, containsInAnyOrder('b', 'c'));
}

5. 集合的对称差

现在,让我们看看两个集合的对称差异——所有元素都包含在集合 1 或集合 2 中,但不包含在两者中:

@Test
public void whenCalculatingSetSymmetricDifference_thenCorrect() {
    Set<Character> first = ImmutableSet.of('a', 'b', 'c');
    Set<Character> second = ImmutableSet.of('b', 'c', 'd');
    Set<Character> intersection = Sets.symmetricDifference(first, second);
    assertThat(intersection, containsInAnyOrder('a', 'd'));
}

6. 幂集

现在 - 让我们看看如何计算幂集 - 该集合的所有可能子集的集合。

在下面的例子中——我们使用*Sets.powerSet()*来计算给定字符集的幂集:

@Test
public void whenCalculatingPowerSet_thenCorrect() {
    Set<Character> chars = ImmutableSet.of('a', 'b');
    Set<Set<Character>> result = Sets.powerSet(chars);
    Set<Character> empty =  ImmutableSet.<Character> builder().build();
    Set<Character> a = ImmutableSet.of('a');
    Set<Character> b = ImmutableSet.of('b');
    Set<Character> aB = ImmutableSet.of('a', 'b');
    assertThat(result, contains(empty, a, b, aB));
}

7. ContiguousSet

接下来——让我们看一下一组排序的连续集—— ContiguousSet

在下面的示例中——我们将一组整数 [10, 11, …, 30] 放入ContiguousSet中:

@Test
public void whenCreatingRangeOfIntegersSet_thenCreated() {
    int start = 10;
    int end = 30;
    ContiguousSet<Integer> set = ContiguousSet.create(
      Range.closed(start, end), DiscreteDomain.integers());
    assertEquals(21, set.size());
    assertEquals(10, set.first().intValue());
    assertEquals(30, set.last().intValue());
}

这种类型的数据结构当然是你可以在纯 Java 中使用TreeSet完成的事情——但是如果你需要以这种方式表示数据,那么这种特殊类型的集合的语义会更好地使用。

8. RangeSet

现在让我们看一下RangeSet。我们可以使用RangeSet来保存断开连接和非空的范围。 在下面的例子中——从 2 个不连贯的范围开始,然后我们将它们连接成一个单一的大范围:

@Test
public void whenUsingRangeSet_thenCorrect() {
    RangeSet<Integer> rangeSet = TreeRangeSet.create();
    rangeSet.add(Range.closed(1, 10));
    rangeSet.add(Range.closed(12, 15));
    assertEquals(2, rangeSet.asRanges().size());
    rangeSet.add(Range.closed(10, 12));
    assertTrue(rangeSet.encloses(Range.closed(1, 15)));
    assertEquals(1, rangeSet.asRanges().size());
}

让我们详细看一下这个例子:

  • 首先——我们插入 2 个不连贯的范围:[1, 10][12, 15]
  • 接下来——我们添加第三个范围来连接现有的 2:[10, 12]
  • 最后——我们验证RangeSet足够聪明,可以看到 3 个范围现在是一个大范围,并将它们合并为:[1, 15]

9. Multiset

接下来——让我们讨论如何使用Multiset。与普通集合相反,Multiset确实支持添加重复元素——它被视为出现次数

在下面的例子中——我们经历了一些简单的多集逻辑:

@Test
public void whenInsertDuplicatesInMultiSet_thenInserted() {
    Multiset<String> names = HashMultiset.create();
    names.add("John");
    names.add("Adam", 3);
    names.add("John");
    assertEquals(2, names.count("John"));
    names.remove("John");
    assertEquals(1, names.count("John"));
    assertEquals(3, names.count("Adam"));
    names.remove("Adam", 2);
    assertEquals(1, names.count("Adam"));
}

10. 获取MultiSet中的前 N 个元素

现在让我们看一个使用MultiSet的更复杂和有用的示例。我们将获得集合中出现最多的 N 个元素——基本上是最常见的元素。

在下面的例子中——我们使用Multisets.copyHighCountFirst()Multiset中的元素进行排序:

@Test
public void whenGetTopOcurringElementsWithMultiSet_thenCorrect() {
    Multiset<String> names = HashMultiset.create();
    names.add("John");
    names.add("Adam", 5);
    names.add("Jane");
    names.add("Tom", 2);
    Set<String> sorted = Multisets.copyHighestCountFirst(names).elementSet();
    List<String> sortedAsList = Lists.newArrayList(sorted);
    assertEquals("Adam", sortedAsList.get(0));
    assertEquals("Tom", sortedAsList.get(1));
}