如何在Java中合并两个流?

24

假设我们有以下两个流:

IntStream stream1 = Arrays.stream(new int[] {13, 1, 3, 5, 7, 9});
IntStream stream2 = Arrays.stream(new int[] {1, 2, 6, 14, 8, 10, 12});
stream1.merge(stream2); // some method which is used to merge two streams.

有没有使用Java 8流API将这两个流合并为[13, 1, 2, 3, 5, 6, 7, 8, 9, 10, 12, 14]的便捷方法(顺序无关紧要)?或者我们只能同时处理一个流吗?

此外,如果这两个流是对象流,如何保留唯一的对象而不覆盖equals()hashCode()方法?例如:

public class Student {

    private String no;

    private String name;
}

Student s1 = new Student("1", "May");
Student s2 = new Student("2", "Bob");
Student s3 = new Student("1", "Marry");

Stream<Student> stream1 = Stream.of(s1, s2);
Stream<Student> stream2 = Stream.of(s2, s3);
stream1.merge(stream2);  // should return Student{no='1', name='May'} Student{no='2', name='Bob'}
只要他们的“No”相同,我们就认为他们是同一个学生(因此May和Marry是同一个人,因为他们的编号都是“1”)。 我找到了“distinct()”方法,但这个方法是基于“Object#equals()”。如果我们不被允许覆盖“equals()”方法,如何将“stream1”和“stream2”合并成一个没有重复项的流?

3
你可以直接写IntStream.of(13, 1, etc)代替Arrays.stream(new int[] {13, 1, 3, 5, 7, 9}); - SME_Dev
3个回答

27

@Jigar Joshi已回答了您的问题的第一部分,即 "如何将两个IntStream合并为一个"

您的另一个问题是 "如何合并两个Stream<T>而不覆盖equals()hashCode()方法?" 可以使用toMap收集器来完成,即假设您不希望结果作为Stream<T>。 示例:

Stream.concat(stream1, stream2)
      .collect(Collectors.toMap(Student::getNo, 
               Function.identity(), 
               (l, r) -> l, 
               LinkedHashMap::new)
      ).values();

如果你想要结果作为一个Stream<T>,那么可以这样做:
 Stream.concat(stream1, stream2)
       .collect(Collectors.collectingAndThen(
               Collectors.toMap(Student::getNo,
                    Function.identity(),
                    (l, r) -> l,
                    LinkedHashMap::new), 
                    f -> f.values().stream()));

这可能不是最高效的方法,但它是另一种返回一个Stream<T>的方式,其中T的项都是唯一的,而不使用你提到的覆盖equalshashcode的方法。

16
你可以使用 concat()
IntStream.concat(stream1, stream2)

谢谢,它起作用了。但遗憾的是 contact() 方法不能为我们删除重复项,所以我们必须再使用一个 distinct() 方法。 - weaver
7
为什么您会期望 concat 函数能够删除重复项呢? - Ousmane D.
5
在连接后,为了去除重复项,您可以调用.distinct() - Andrii Abramov
1
@weaver 这就是流的作用...在流上链接操作。 - ETL

3

对于第一个问题,您可以使用“flatMap”

    IntStream stream1 = Arrays.stream(new int[] {13, 1, 3, 5, 7, 9});
    IntStream stream2 = Arrays.stream(new int[] {1, 2, 6, 14, 8, 10, 12});

    List<Integer> result = Stream.of(stream1, stream2).flatMap(IntStream::boxed)
            .collect(Collectors.toList());
    //result={13, 1, 3, 5, 7, 9, 1, 2, 6, 14, 8, 10, 12}

编辑

感谢 @Vinicius 的建议, 我们可以使用

Stream<Integer> result = Stream.of(stream1, stream2).flatMap(IntStream::boxed).distinct();

在这里,我们将获得一个基于equals方法的所有元素不同的流。


1
只需在flatMap后面加上distinct(),这就是更好的答案。 - Vinicius
1
非基本类型流的变体: Stream.of(stream1, stream2, ... streamN).flatMap(Function.identity()) - snuk182

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接