首页 > 解决方案 > 将 Guava Sets 转换为 List 的最快方法

问题描述

我什至不确定这是否可行,但我正在执行类似unionor的 Set 操作intersection,我需要将其转换为 aList以便对列表进行洗牌并将其传递给接受 aList而不是a 的不同方法Set。所以我将结果转换为 a List,一切都很好。但是从分析器中,我看到该操作在负载下需要很长时间,这是因为 Guava Sets 的方式.size()。它不是像普通 java Set 那样的常量操作。

这是代码示例

    @Test
    void testSet() {
        Set<Character> first = ImmutableSet.of('a', 'b', 'c');
        Set<Character> second = ImmutableSet.of('b', 'c', 'd');

        Set<Character> union = Sets.union(first, second);

        List<Character> characters = new ArrayList<>(union);
    }

我正在尝试找到将 Guava 转换SetsList. 通过挖掘代码,这就是 Guava Sets 所做的事情https://github.com/google/guava/blob/master/guava/src/com/google/common/collect/Sets.java#L694。这不是一个持续的操作,它会损害高负载下的性能。我猜这个.size调用是从 Java 想要将一个新的 Collection 复制到一个新的 List 时开始的,它必须知道创建一个 List 的大小。

标签: javalistoptimizationsetguava

解决方案


抛开“高负载下的性能”的论点(如果它与您的用例真正相关,我建议做适当的 JMH 微基准测试),Sets操作是内存优化的,所以如果您要立即复制数据,您可能想尝试不同的方法根本不叫大小。

首先,Sets.union返回SetView<E>which has immutableCopy(),然后您可以在其上调用.asList()view,返回一个不可变列表(随意将所有操作链接在一起):

@Test
public void testSetCopy() {
    Set<Character> first = ImmutableSet.of('a', 'b', 'c');
    Set<Character> second = ImmutableSet.of('b', 'c', 'd');

    Sets.SetView<Character> union = Sets.union(first, second);
    List<Character> characters = union.immutableCopy().asList();

    assertThat(characters).containsOnly('a', 'b', 'c', 'd');
}

其次,您也可以Set首先考虑使用,如 Louis 所说:

@Test
public void testMultiset() {
    Set<Character> first = ImmutableSet.of('a', 'b', 'c');
    Set<Character> second = ImmutableSet.of('b', 'c', 'd');

    // here it's ugly but maybe you can collect to set in the first place
    ImmutableMultiset<Character> set = ImmutableSet.<Character>builder()
             .addAll(first)
             .addAll(second)
             .build(); // [a, b, c, d]

    List<Character> characters = set.asList();

    assertThat(characters).containsOnly('a', 'b', 'c', 'd');
}

也就是说,YMMV,我再次鼓励您在选择任何可读性较差的选项之前进行微基准测试。


推荐阅读