【问题标题】:Converting Stream<IntStream> to a single IntStream将 Stream<IntStream> 转换为单个 IntStream
【发布时间】:2019-03-09 08:21:57
【问题描述】:

我想将List&lt;String&gt; 转换为IntStream。假设我的列表类似于["abc", "de", "fghi"]。然后我想要的IntStream 就像1,1,1,2,2,3,3,3,3。 (IntStream 中数字i 的出现次数取决于给定列表中ith 字符串的长度)

我为此编写了以下方法(它不会编译):

private static IntStream getSingleIntStream(List<String> list) {
    final AtomicInteger atomicInteger = new AtomicInteger();
    return list.stream()
            .map(str -> {
                        atomicInteger.incrementAndGet();
                        return IntStream.range(0, str.length())
                                .map(i -> atomicInteger.get());
                    }); // Now I don't understand how can I convert this Stream<IntStream> to a single IntStream 

}

但我不明白如何转换我可以将Stream&lt;IntStream&gt; 转换为单个IntStream。 (我的猜测是我们可以以某种方式使用flatMap,但我不完全了解如何使用它。)

我使用IntStream 而不是Stream&lt;Integer&gt; 来避免自动装箱并使我的整个系统更高效。

【问题讨论】:

标签: java java-8 java-stream


【解决方案1】:

另一种解决方案是这样的:

IntStream result =  IntStream.range(0,list.size())
                             .flatMap(i-> IntStream.range(0, list.get(i)
                                                                 .length())
                                                   .map(j->i+1));

【讨论】:

  • 我想我几乎用List&lt;Integers写了一个类似的版本虽然private static IntStream getSingleIntStream(List&lt;String&gt; list) { List&lt;Integer&gt; integerList = new ArrayList&lt;&gt;(); for (int i = 0; i &lt; list.size(); i++) { for (int j = 0; j &lt; list.get(i).length(); j++) { integerList.add(i + 1); } } return integerList.stream().mapToInt(i -&gt; i); }
  • 我显然更喜欢这个,而不是使用 AtomicInteger 的不太好的副作用。我觉得更自然一点的变体是IntStream.range(0, list.size()).flatMap(index -&gt; list.get(index).chars().map(ch -&gt; index + 1)),但可能效率不高,而且肯定是口味问题。
  • @OleV.V. ...list.get(index).chars().map(ch -&gt; index + 1)...因为简洁比我的要好。
  • 欢迎采纳。很高兴你喜欢它。
【解决方案2】:

扩展@JB_Nizet 的评论

我需要使用的方法是flatMapToInt

private static IntStream getSingleIntStream(List<String> list) {
    final AtomicInteger atomicInteger = new AtomicInteger();
    return list.stream()
            .flatMapToInt(str -> {
                atomicInteger.incrementAndGet();
                return IntStream.range(0, str.length())
                        .map(i -> atomicInteger.get());
            });
}

【讨论】:

  • 您能否详细介绍一下您想要实现的目标? AtomicInteger 的使用表明您可能正在以艰难的方式(不必要地)做某事
  • @GrzegorzPiwowarek 我只想得到IntStream。学习 Java-8 的流 API 更像是一种游戏,而不是做一些有用的事情。但如果您能做一些我们不使用AtomicInteger 的事情,我将不胜感激。我也觉得很丑。
  • 是的,但是您想实现什么?我看到您只是从输入列表中丢弃所有字符串并将其替换为 AtomicInteger 生成的序列?
  • 这就是我想做的,字符串Strings的内容无关紧要,而是每个字符串的长度很重要,在此基础上我需要生成一个序列。
  • 怎么样IntStream.range(0,list.size()) .flatMap(i-&gt; IntStream.range(0, list.get(i).length()).map(j-&gt;i+1));
【解决方案3】:

解决方案是使用flatMapToInt:

private static IntStream getSingleIntStream(List<String> list) {
    final AtomicInteger atomicInteger = new AtomicInteger();
    return list.stream()
        .flatMapToInt(str -> {
            atomicInteger.incrementAndGet();
            return IntStream.range(0, str.length())
                .map(i -> atomicInteger.get());
        });
}

但我会重新考虑你想在这里实现什么。现在,初始列表中的每个 String 都将被替换为取自 AtomicInteger 的数字(这将对每个字符串重复 String.length() 次):

getSingleIntStream(Arrays.asList("a", "bc")).forEach(System.out::println); // 1 2 2

我假设您想为每个 String 中的每个字符编号:

private static IntStream getSingleIntStream(List<String> list) {
    AtomicInteger atomicInteger = new AtomicInteger();
    return list.stream()
        .flatMapToInt(str -> IntStream.range(0, str.length())
            .map(i -> atomicInteger.incrementAndGet()));
}

// 1 2 3

【讨论】:

    【解决方案4】:

    您对 flatMap 的看法是绝对正确的,正如 Hadi J 已经在一个很好的答案中表明的那样。我只是想提供我的相同变体:

    private static IntStream getSingleIntStream(List<String> list) {
        return IntStream.rangeClosed(1, list.size())
                .flatMap(i -> list.get(i - 1).chars().map(ch -> i));
    }
    

    您可能会发现此版本更自然或更简洁。在任何情况下,这两个版本都具有避免AtomicInteger 和流管道对其的副作用的优势。流管道应该没有副作用。

    让我们也看看它的实际效果:

        List<String> list = List.of("abc", "de", "fghi");
        int[] nums = getSingleIntStream(list).toArray();
        System.out.println(Arrays.toString(nums));
    

    [1, 1, 1, 2, 2, 3, 3, 3, 3]

    当您需要流中元素的索引时,一般技巧是从索引的IntStream 开始,然后在流管道中从索引中查找元素 (list.get(i - 1))。在这种情况下,我(非常规地)使用基于 1 的索引,因为您希望得到的数字从 1 开始。所以我们需要在列表查找中减去 1。

    【讨论】:

      猜你喜欢
      • 2023-03-11
      • 1970-01-01
      • 1970-01-01
      • 2022-06-10
      • 2014-07-03
      • 2016-05-04
      • 2019-10-25
      • 2021-08-10
      相关资源
      最近更新 更多