当前位置: 首页 > 知识库问答 >
问题:

从一个长的流创建流

周朗
2023-03-14

我想根据Streams的内容将单个Stream拆分为StreamsStream。生成的Stream应该包含原始流数据的一部分。

我的实际应用程序更复杂(它是对时间间隔列表中的日志行进行分组),但我的问题是如何处理流,因此这里我询问一个简化的示例。

我希望能够拆分

例如,以下流包含:

{1,1,1,2,2,3,6,7,7,1,1}

需要产生包含以下内容的流:

{{1,1,1},{3},{7,7},{1,1}}

省略偶数,我可以用过滤器开始(或结束):

Stream<Integer> input = ...;
Straem<Stream<Integer>> output = input.filter(this::isOdd).someOtherOperation();

这是不希望的,因为这将意味着对每个输入值进行两次评估,这是可以接受的,但我更希望避免这种情况。

我当前的解决方案就是迭代流的内容并创建一个列表

我还认为我可以通过编写自己的从流中读取的迭代器来实现这一点,但我不确定这将如何工作。

如何基于原始的内容将转换为,而不首先将完整结果存储为列表列表。


共有3个答案

邢令
2023-03-14

恐怕这是不可行的,至少不是以一种好的方式。即使您将元素映射到流中并减少它们,这些内部流也必须知道它们包含哪些元素,以便它们必须存储一些内容。

最简单的解决方案是使用group pingBy,但是它会将所有结果存储在地图中:

List<Integer> input = asList(1, 1, 1, 2, 2, 2, 3, 6, 7, 7, 1, 1);
Map<Integer, List<Integer>> grouped = input.stream().collect(groupingBy(i -> i));
Stream<Stream<Integer>> streamOfStreams = grouped.values().stream().map(list -> list.stream());

您可以尝试使用reduce操作,但它需要您实现自己的流,您必须在其中存储每个流包含的元素。更不用说实施它需要付出很大的努力。

对于您的情况,我能想到的最佳解决方案是在列表上迭代两次:

public static void main(String[] args) {
    List<Integer> input = asList(1, 1, 1, 2, 2, 2, 3, 6, 7, 7, 1, 1);

    input.stream().distinct().filter(i -> isOdd(i)).forEach(i -> {
        List<Integer> subList = input.stream().filter(j -> Objects.equals(j, i)).collect(toList());
        System.out.println(subList); // do something with the stream instead of collecting to list
    });
}

private static boolean isOdd(Integer i) {
    return (i & 1) == 1;
}

但是请注意,它具有时间复杂度O(n^2)

编辑:

此解决方案将只包含本地元素组。它只存储当前本地组。

public static void main(String[] args) {
    Stream<Integer> input = Stream.of(1, 1, 1, 2, 2, 2, 3, 6, 7, 7, 1, 1);

    Iterator<Integer> iterator = input.iterator();
    int first;
    int second = iterator.next();

    List<Integer> buffer = new ArrayList<>();
    buffer.add(second);

    do {
        first = second;
        second = iterator.next();

        if (Objects.equals(first, second)) {
            buffer.add(second);
        } else {
            doSomethingWithTheGroup(buffer);
            buffer = new ArrayList<>(); // let GC remove the previous buffer
            buffer.add(second);
        }
    } while (iterator.hasNext());
    doSomethingWithTheGroup(buffer);
}

private static void doSomethingWithTheGroup(List<Integer> buffer) {
    System.out.println(buffer);
}

private static boolean isOdd(Integer i) {
    return (i & 1) == 1;
}

输出:

[1, 1, 1]
[2, 2, 2]
[3]
[6]
[7, 7]
[1, 1]
马渊
2023-03-14
匿名用户

您可以使用我的StreamEx库。它具有执行作业的groupRuns

List<Integer> input = Arrays.asList(1, 1, 1, 2, 2, 2, 3, 6, 7, 7, 1, 1);
Stream<Stream<Integer>> streams = StreamEx.of(input).filter(this::isOdd)
    .groupRuns(Integer::equals)
    .map(List::stream);

使用示例:

streams.map(s -> StreamEx.of(s).joining(",")).forEach(System.out::println);

输出:

1,1,1
3
7,7
1,1

与protonpack库类似,内部有一个自定义拆分器,但使用StreamEx可以利用并行处理(protonpack根本不拆分)。

在顺序处理中,一次最多有一个中间列表驻留在内存中(其他列表符合GC条件)。如果您仍然担心内存消耗(例如,您有很长的组),则有另一种解决此任务的方法,因为StreamEx 0.3.3:

Stream<Stream<Integer>> streams = StreamEx.of(input).filter(this::isOdd)
        .runLengths()
        .mapKeyValue(StreamEx::constant);

runLengths方法返回条目流,其中key是元素,value是相邻重复元素的数量。之后,使用StreamEx.constant,这是Stream.generate(()-

更新:StreamEx 0.3.3已发布,因此第二个解决方案现在也符合条件。

孟花蜂
2023-03-14

为此,您可能需要实现自己的聚合拆分器。质子包库中已经有了类似的东西(第一个链接重定向到质子包中实现的链接)。

注意,你得到一个Stream

StreamUtils.aggregate(Stream.of(1, 1, 1, 2, 2, 2, 3, 6, 7, 7, 1, 1), 
                      Objects::equals)
           .forEach(System.out::println);

产出:

[1, 1, 1]
[2, 2, 2]
[3]
[6]
[7, 7]
[1, 1]

 类似资料:
  • 问题内容: 我想一个拆分成的基础上的内容。结果将包含原始流数据的一部分。 我的实际应用程序更加复杂(将时间间隔列表中的日志行分组),但是我的问题是如何处理流,因此在这里我问一个简化的示例。 我希望能够基于重复的相同数字将a拆分为一个,而仅将流保留为奇数。 例如,以下流包含: 需要产生包含以下内容的流: 通过使用过滤器开始(或结束),可以省去偶数: 这是不希望的,因为这意味着对每个输入值进行两次评估

  • 问题内容: 如果可能,我想通过管道将两个Node.js流合并为一个。我正在使用转换流。 换句话说,我希望我的图书馆归还给人们使用。例如,他们可以写: 在内部,我使用的是第三方,它可以完成一些工作,并插入包含在我自己的逻辑中。因此,以上内容将转换为: 我可以那样做吗?我已经尝试过了,但这显然行不通。 为了与进行类比,假设我想编写一个程序来检查某个流()的最后一行中是否存在该字母,我可以创建一个she

  • 问题内容: 我在存储过程中的MS- SQL中有一个结果集,可以说它有一个VARCHAR列,但有很多行。我想创建一个包含所有这些值的逗号分隔的字符串,是否有一种简单的方法来执行此操作,还是我必须逐步检查每个结果并手动构建字符串? 最好是我想在存储过程本身中执行此操作。 问题答案: 这是一种方法(使用AdventureWorks2008 DB): 这是另一个(从SQL 2005开始): 在这两种情况下

  • 问题内容: 我有一些基于NDK的C 代码,需要构建一个android位图对象。我敢肯定有一种方法可以直接从C 代码执行此操作,但这并不是最简单的事情;) 所以我想调用的方法是 因此,要从本机代码执行此操作,我需要执行以下步骤。 找到类(android.graphics.Bitmap)。 获取“ createBitmap”的静态方法ID。 创建枚举。 调用静态方法。 (最终,我将需要创建一个jint

  • 我想同时执行两个流,并生成作为前两个流的组合的第三个流 我想从这两个中生成一个新的,它将与这一个等价 我目前找到的是或

  • 我有一个学生名单a和学生名单B。 学生对象包含如下字段:否、年龄、城市、出生日期、工资 我的列表A包含这些对象 我的列表B包含这些对象 我想做的是提取ListA有但listB没有的学生对象,以及ListA和listB有但薪水不同的学生对象(如否、年龄、城市)。我还想写工资差异。 我想在java 8中使用流api。首先,我想将students对象提取到列表中,但我现在可以提取常见的student对象