【问题标题】:One liner for getting a sublist from a Set一个用于从 Set 中获取子列表的衬垫
【发布时间】:2012-09-11 12:57:58
【问题描述】:

是否有一个单行(可能来自 Guava 或 Apache Collections)从集合中获取子列表。在内部它应该做这样的事情:

public <T> List<T> sublist(Set<T> set, int count) {
  Iterator<T> iterator = set.iterator();
  List<T> sublist = new LinkedList<T>();
  int pos = 0;
  while (iterator.hasNext() && pos++ < count) {
    sublist.add(iterator.next());
  }
  return sublist;
}

显然,如果没有足够的元素,它必须返回尽可能多的元素。

【问题讨论】:

  • 如果你想从你的集合中获得subList,你不应该首先使用Set,因为它的迭代顺序是undefined,你的实现可能会产生不同的结果,具体取决于在...月相。
  • 你怎么知道我的Set 中的元素顺序是未定义的?
  • 来自 Java 集合 API - 请参阅 iterator() description in Set。 Set 接口本身对 Collection 合约没有添加任何关于迭代顺序的内容;另一方面设置实现can order its elements somehow(TreeSet 和 LinkedHashSet 可以)。
  • 你怎么知道我的Set 实现没有添加迭代顺序?
  • 我不知道你的impl,但是在方法签名中没有YourSet但是Set参数,因此方法可以(你不禁止它)被调用为sublist(new HashSet(Arrays.asList(2,3,1), 2) which 可以产生与sublist(new LinkedHashSet(Arrays.asList(2,3,1), 2)不同的结果。

标签: java guava apache-commons-collection


【解决方案1】:

这个呢

Set<String> s = new HashSet<String>();
// add at least two items to the set 
Set<String> subSet = new HashSet(new ArrayList<String>(s).subList(1, 2));

这将在 1 和 2 之间进行子列表

【讨论】:

  • 这个具体的 sn-p 会给 IndexOutOfBoundsException ;)
  • @Xaerxess 啊,是的,你是对的。让我更新代码示例。谢谢
【解决方案2】:

使用番石榴:

return FluentIterable.from(set) 
  .limit(count)
  .toImmutableList();

(此外,与大多数其他解决方案相比,这实际上不会遍历整个集合——它实际上只会遍历第一个 count 元素然后停止.)

【讨论】:

  • +1 'new XXXList(set)' 处理 Set 中的所有元素,FluentIterable 不处理。
  • 从 Guava 3.0 开始,可以使用 Iterables.limit(set, count);。更简单的 API,恕我直言。
  • @XtremeBiker:这种方法将其转换为List,我认为这是问题的一部分。
【解决方案3】:

无需事先创建Set 的副本,您可以(使用番石榴):

Lists.newLinkedList(Iterables.getFirst(Iterables.partition(mySet, count), ImmutableList.of()))

这是一个真正的 LinkedList 仅包含(最多)第一个 count 元素,而不是更大列表中的视图。

【讨论】:

    【解决方案4】:

    您可以使用TreeSet 并使用它的subSet 方法:

    返回此集合的一部分的视图,其元素范围从 fromElement 到 toElement。如果 fromElement 和 toElement 相等,则返回的集合为空,除非 fromExclusive 和 toExclusive 都为真。返回的集合由该集合支持,因此返回集合中的更改会反映在该集合中,反之亦然。返回的集合支持该集合支持的所有可选集合操作。

    使用整数的示例:

    TreeSet<Integer> t = new TreeSet<Integer>();
    t.add(1);
    t.add(2);
    t.add(3);
    t.add(4);
    t.add(5);
    
    System.out.println("Before SubSet:");
    
    for(Integer s : t){
        System.out.println(s);
    }
    
    System.out.println("\nAfter SubSet:");
    
    
    for(Integer s : t.subSet(2,false,5,true)){
        System.out.println(s);
    }
    

    输出:

    Before SubSet:
    1
    2
    3
    4
    5
    
    After SubSet:
    3
    4
    5
    

    或者,如果您不知道元素并且想要返回两点之间的元素,您可以使用由 Set 构造的 ArrayList 并使用 subList 方法。

    System.out.println("\nAfter SubSet:");
    
    t = new TreeSet(new ArrayList(t).subList(2, 5));
    
    for(Integer s : t){
        System.out.println(s);
    }
    

    【讨论】:

    • 这违反了上述要求。我不知道元素的值,我只需要获取它们的数量
    • @yegor256 我道歉。我在末尾添加了一个方法,我认为它可能更符合您的要求?
    【解决方案5】:
    (new LinkedList<Object>(mySet)).sublist(0, Math.min(count, mySet.size()))
    

    但请注意:代码(甚至是您的原始代码)有点臭,因为集合的迭代顺序取决于相关的实际集合实现(在HashSet 和TreeSet 的密钥顺序中完全未定义s)。所以,这实际上是一个悬而未决的问题,哪些元素进入了最终的子列表。

    【讨论】:

      【解决方案6】:

      应该这样做:

      return (new LinkedList<T>(set)).subList(0, count);
      

      但要确保count 的大小不大于set 的大小。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-11-10
        • 1970-01-01
        • 2014-05-24
        • 2022-10-04
        • 1970-01-01
        • 1970-01-01
        • 2011-02-22
        • 2013-10-20
        相关资源
        最近更新 更多