【问题标题】:Display a chunked items list in Java 8在 Java 8 中显示分块项目列表
【发布时间】:2019-11-10 09:54:12
【问题描述】:

使用以下代码:

public class Main {

    public static void main(String[] args) {
        final List<Integer> items = 
             IntStream.rangeClosed(0, 23).boxed().collect(Collectors.toList());

        final String s = items
            .stream()
            .map(Object::toString)
            .collect(Collectors.joining(","))
            .toString()
            .concat(".");

        System.out.println(s);
    }
}

我明白了:

0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23 .

我想做的是每10个项目打破一次,以获得:

0,1,2,3,4,5,6,7,8,9,

10,11,12,13,14,15,16,17,18,19,

20,21,22,23.

我在谷歌搜索后尝试了很多东西但没有成功! 你能帮帮我吗?

谢谢,

奥利维尔。

【问题讨论】:

  • 编写自己的 Spliterator 来获取流,对元素进行分组,并将其自身包装在一个新的 Stream 中是通常的方法。但是,对于这个特定示例,您还可以编写自己的收集器/归约器,以生成数字列表的列表。收集下一个数字时,检查最后一个列表是否还有空间,否则添加一个新的空列表。然后流式传输结果,用逗号连接列表的元素,用换行符连接结果字符串。缺点是您会生成一个包含所有元素的集合,这可能是内存问题(Spliterator 方法不会)。
  • 顺便说一下,Holger 对@AlexisC 问题的回答的最后一部分。提到的以非常简洁的方式产生了我提到的减少。因此,如果您可以忍受创建中间集合,这是一个非常酷的解决方案。
  • 我使用 StreamEx 的 groupRuns 的两分钱:IntStreamEx.rangeClosed(0, 23).boxed().groupRuns((i1, i2) -&gt; i1 / 10 == i2 / 10).collect(Collector.of(() -&gt; new StringJoiner(System.lineSeparator(), "", "."), (joiner, numbers) -&gt; joiner.add(String.join(",", numbers.stream().map(String::valueOf).toArray(String[]::new))), StringJoiner::merge, StringJoiner::toString));

标签: list java-8 java-stream chunked


【解决方案1】:

如果您愿意使用第三方库,以下内容将使用 Eclipse Collections Collectors2.chunk(int)

String s = IntStream.rangeClosed(0, 23)
        .boxed()
        .collect(Collectors2.chunk(10))
        .collectWith(MutableList::makeString, ",")
        .makeString("", ",\n", ".");

Collectors2.chunk(10) 的结果将是 MutableList&lt;MutableList&lt;Integer&gt;&gt;。此时,我从 Streams API 切换到使用可直接在集合上使用的原生 Eclipse Collections API。方法makeString 类似于Collectors.joining()。方法collectWith 类似于Stream.map(),不同之处在于将Function2 和一个额外的参数传递给该方法。这允许在此处使用方法引用而不是 lambda。等效的 lambda 为 list -&gt; list.makeString(",")

如果你只使用 Eclipse Collections API,这个问题可以简化如下:

String s = Interval.zeroTo(23)
        .chunk(10)
        .collectWith(RichIterable::makeString, ",")
        .makeString("", ",\n", ".");

注意:我是 Eclipse Collections 的提交者。

【讨论】:

    【解决方案2】:

    如果你只想处理这些升序的数字,你可以这样做

    String s = IntStream.rangeClosed(0, 23).boxed()
        .collect(Collectors.groupingBy(i -> i/10, LinkedHashMap::new,
                Collectors.mapping(Object::toString, Collectors.joining(","))))
        .values().stream()
        .collect(Collectors.joining(",\n", "", "."));
    

    此解决方案也可以适用于任意随机访问列表,例如

    List<Integer> items = IntStream.rangeClosed(0, 23).boxed().collect(Collectors.toList());
    
    String s = IntStream.range(0, items.size()).boxed()
        .collect(Collectors.groupingBy(i -> i/10, LinkedHashMap::new,
                Collectors.mapping(ix -> items.get(ix).toString(), Collectors.joining(","))))
        .values().stream()
        .collect(Collectors.joining(",\n", "", "."));
    

    但是,对于任意流没有简单而优雅的解决方案,这一限制适用于所有类型的任务,这些任务依赖于元素的位置。

    【讨论】:

      【解决方案3】:

      这是对 cme​​ts Collector 中已链接的改编:

          private static Collector<String, ?, String> partitioning(int size) {
          class Acc {
              int count = 0;
      
              List<List<String>> list = new ArrayList<>();
      
              void add(String elem) {
                  int index = count++ / size;
                  if (index == list.size()) {
                      list.add(new ArrayList<>());
                  }
                  list.get(index).add(elem);
              }
      
              Acc merge(Acc right) {
      
                  List<String> lastLeftList = list.get(list.size() - 1);
                  List<String> firstRightList = right.list.get(0);
                  int lastLeftSize = lastLeftList.size();
                  int firstRightSize = firstRightList.size();
      
                  // they are both size, simply addAll will work
                  if (lastLeftSize + firstRightSize == 2 * size) {
                      System.out.println("Perfect!");
                      list.addAll(right.list);
                      return this;
                  }
      
                  // last and first from each chunk are merged "perfectly"
                  if (lastLeftSize + firstRightSize == size) {
                      System.out.println("Almost perfect");
                      int x = 0;
                      while (x < firstRightSize) {
                          lastLeftList.add(firstRightList.remove(x));
                          --firstRightSize;
                      }
                      right.list.remove(0);
                      list.addAll(right.list);
                      return this;
                  }
      
                  right.list.stream().flatMap(List::stream).forEach(this::add);
                  return this;
              }
      
              public String finisher() {
                  return list.stream().map(x -> x.stream().collect(Collectors.joining(",")))
                          .collect(Collectors.collectingAndThen(Collectors.joining(",\n"), x -> x + "."));
              }
      
          }
          return Collector.of(Acc::new, Acc::add, Acc::merge, Acc::finisher);
      }
      

      用法如下:

      String result = IntStream.rangeClosed(0, 24)
                  .mapToObj(String::valueOf)
                  .collect(partitioning(10));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-10-24
        • 2017-12-12
        • 1970-01-01
        • 2018-04-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多