Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/374.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 从一个长流创建一个流_Java_Java 8_Java Stream - Fatal编程技术网

Java 从一个长流创建一个流

Java 从一个长流创建一个流,java,java-8,java-stream,Java,Java 8,Java Stream,我想根据流的内容将单个流拆分为流的流。生成的流应包含原始流的部分数据 我的实际应用程序更复杂(它是对时间间隔列表中的日志行进行分组),但我的问题是如何处理流,因此这里我询问一个简化的示例 示例问题 我希望能够根据重复的相同数字将流拆分为流,只留下奇数流 例如,以下流包含: {1,1,1,2,2,3,6,7,7,1,1} 需要产生包含以下内容的流: {{1,1,1},{3},{7,7},{1,1} 通过使用过滤器开始(或结束)可以省去偶数: Stream<Integer> input

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

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

示例问题 我希望能够根据重复的相同数字将
拆分为
,只留下奇数流

例如,以下流包含:

{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();
流输入=。。。;
stream output=input.filter(this::isOdd).someOtherOperation();
这是不希望的,因为这将意味着对每个输入值进行两次评估,这是可以接受的,但我更希望避免这种情况

解决办法的想法 我当前的解决方案是迭代流的内容,创建一个
列表
,并将其转换为
。但是,这意味着完整的结果将保留在内存中(这对于我的应用程序来说是不需要的)

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

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

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

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

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());
但是请注意,它具有时间复杂度
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;
}

像@Jaroslaw一样,我也使用Map来保存不同的流。但是,映射将保存从输入构建的流,并且不预先收集,这是可行的。使用
Stream.concat
Stream.of
可以向流中添加一个元素:

    Map<Integer, Stream<Integer>> streamMap = new HashMap<>();

    int[] arr = {1,1,1,2,2,2,3,6,7,7,1,1};
    Arrays.stream(arr)
    .filter(this::isOdd)
    .forEach(i -> {
        Stream<Integer> st = streamMap.get(i);
        if (st == null)  st = Stream.of(i);
        else st = Stream.concat(st, Stream.of(i));
        streamMap.put(i, st);
    });

    streamMap.entrySet().stream().forEach(e -> {
        System.out.print(e.getKey() + "={");
        e.getValue().forEach(System.out::print);
        System.out.println("}");
    });

您可能希望实现自己的方法来实现这一点。库中已经有类似的东西(第一个链接重定向到质子包中实现的链接)

请注意,您将获得一个
(您可以尝试修改实现以直接获得
,但始终需要缓冲少量元素;这取决于窗口的大小;以测试是否应创建新窗口)。例如:

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]
你可以用我的图书馆。它具有以下功能:

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);
输出:

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);
Stream-streams=streamx.of(input).filter(this::isOdd)
.runLength()
.mapKeyValue(StreamX::常量);
该方法返回条目流,其中key是元素,value是相邻重复元素的数量。然后使用
StreamEx.constant
,这是
Stream.generate(()->value.limit(length)
的快捷方式。因此,即使对于很长的组,也会有恒定的中间内存消耗。当然,这个版本也是并行友好的


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

这相当于按
分组并将所有内容存储在列表中
Stream.of
只存储您传入的值,这样您的映射将使用
O(n)
内存,这正是@Thirler想要避免的。
Stream.of
加载一个元素。使用我的解决方案,在
forEach
中,您可以访问地图和所有流,因为它们被填充(=原始流被消耗)。使用您的解决方案,一旦原始流耗尽并且所有内容都已收集完毕,地图就可以访问,但一旦使用整个流,地图将包含该流中的所有元素。在提到我的解决方案之前,我建议您完全阅读我的答案,因为它包含两个解决方案……在我看来,您的第二个解决方案不回答OP,因为它不提供流。它提供了一次加载整个子流的选项。注意,这不会产生与我的示例相同的结果。在打印地图之前,它还会将完整结果存储在地图中。第一个forEach遍历完整的输入并存储结果,这与存储列表非常相似。同样,拥有列表流意味着这些包含所有元素的列表存储在内存中,这是不需要的。@JaroslawPawlak据我所知,每个列表都是在管道需要下一段数据时生成的(使用
tryAdvance
方法)。完成后,
currentSlide
列表将使用一个新的引用(
currentSlide=new ArrayList();
)重新初始化,因此我想前一个将符合GC的条件,但老实说,我
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
Stream<Stream<Integer>> streams = StreamEx.of(input).filter(this::isOdd)
        .runLengths()
        .mapKeyValue(StreamEx::constant);