【问题标题】:What is an efficient and elegant way to add a single element to an immutable set?将单个元素添加到不可变集合的有效且优雅的方法是什么?
【发布时间】:2012-05-08 10:01:06
【问题描述】:

我有一个可能包含许多元素的不可变集合(转换为Set<Integer>)。我需要一个包含该集合中的元素和一个附加元素的集合。我有一些笨拙的代码来复制集合,然后附加元素,但我正在寻找使事情尽可能高效的正确方法。

我有 Guava 可用,但我不需要使用它。

【问题讨论】:

    标签: java guava


    【解决方案1】:

    如果 Set 是不可变的,除了复制 Set,然后添加新元素之外,我看不到任何其他方法。请记住,复制一个集合就像在创建新集合时将基本集合传递给构造函数一样简单。

    【讨论】:

    • 我认为这是隐含的;问题是关于如何实现这一点。
    【解决方案2】:

    当我在同一个句子中阅读“不可变”和“添加到”时,我感到认知失调。您可以将新元素添加到不可变值的可变副本的末尾,但不能修改不可变集。我不知道有什么优雅的。

    【讨论】:

    • 这里没有矛盾。数字 3 是不可变的,但您仍然可以谈论将 2 加到 3 以返回 5。
    • 你不明白发生了什么。如果您将新元素添加到不可变集合中,您并没有对其进行任何更改。您创建了一个新集合,它是原始不可变集合的副本,并添加了新元素
    • 谢谢,但这正是我的理解。您在这里的回答表明您不理解这一点,但也许只是解释得不好。
    • 我的意思是“添加到”这个词并不意味着突变;我曾希望通过添加整数的类比来澄清这一点。你没有必要错误地告诉我我不明白的事情;这很粗鲁。
    【解决方案3】:

    不确定性能,但可以使用 Guava 的ImmutableSet.Builder

    import com.google.common.collect.ImmutableSet
    
    // ...
    Set<Integer> newSet = new ImmutableSet.Builder<Integer>()
                                    .addAll(oldSet)
                                    .add(3)
                                    .build();
    

    当然你也可以为自己写一个辅助方法:

    public static <T> Set<T> setWith(Set<T> old, T item) {
      return new ImmutableSet.Builder<T>().addAll(old).add(item).build();
    }
    
    // ...
    Set<Integer> newSet = setWith(oldSet, 3);
    

    【讨论】:

      【解决方案4】:

      你有三个选择。

      • 使用可变集。
      • 检查该元素是否已存在,如果不存在,则创建该集合的副本并添加一个元素。
      • 创建一个包含前一个集合和元素的包装集。

      有时BitSet 是比Set&lt;Integer&gt; 更好的选择,具体取决于您的值的分布情况。

      【讨论】:

      • 不能使用可变集 - 我不控制返回值的 API。检查它不存在当然是个好建议。谢谢。
      • 与复制成本相比,检查速度很快。
      • 建造者不会自己检查它是否已经存在吗?我的意思是在分配副本之前。
      【解决方案5】:

      您可以考虑 Sets.union()。构建会更快,但使用更慢。

      public static <T> Set<T> setWith(Set<T> old, T item) {
        return Sets.union(old, Collections.singleton(item);
      }
      

      (com.google.common.collect.Sets & java.util.Collections)

      【讨论】:

        【解决方案6】:

        如果您希望获得比完整副本更好的性能,并且对元素进行排序,则可以使用 B+ tree 周围的有效不可变包装器来获得良好的增量集性能。

        将项目添加到 B+ 树需要 O(log(n)) 时间和增量分配,而不是像 ImmutableSet.builder().addAll(...).add(...).build() 那样的 O(n)。这意味着从 n 个增量添加构建一个集合是 O(n*log(n)),而不是 O(sqr(n))。

        这个answer 有一个指向jdbm 库的指针,因此可能值得一看jdbm:jdbm

        【讨论】:

          【解决方案7】:

          使用 Java 8,您还可以使用流来实现该效果

          Stream.concat(oldSet.stream(),
                        Stream.of(singleElement))
                .collect(Collectors.toSet())
          

          顺便说一下,从 JDK 10 开始,Collectors 还允许累积为不可变类型(与 Set.of() 等静态工厂创建的类型相同):

          Stream.concat(oldSet.stream(),
                        Stream.of(singleElement))
                .collect(Collectors.toUnmodifiableSet())
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-02-07
            • 2011-05-28
            • 1970-01-01
            • 1970-01-01
            • 2022-08-10
            • 1970-01-01
            • 2019-05-27
            • 2017-01-22
            相关资源
            最近更新 更多