『面试の神』Java怎样实现流合并?

「这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战」。

前言

Hello 大家好,我是l拉不拉米流式计算一直是Java面试中出现频率很高的一个问题,今天『面试の神』系列就来讲一讲 Java 流合并的实现方式。

普通Java方法

JDK 8 Stream 类有一些有用的静态方法。比如concat():

合并两个流

@Test 
public void merge() { 
    Stream<Integer> stream1 = Stream.of(1, 3, 5); 
    Stream<Integer> stream2 = Stream.of(2, 4, 6); 
    
    Stream<Integer> resultingStream = Stream.concat(stream1, stream2); 
    
    assertEquals( 
        Arrays.asList(1, 3, 5, 2, 4, 6), 
        resultingStream.collect(Collectors.toList())); 
}
复制代码

合并多个流

当我们要合并多个流时,操作会变得复杂一些。一个可行的办法是,先合并前两个流,再用合并结果合并其他的流。

比如:

@Test 
public void merge() { 
    Stream<Integer> stream1 = Stream.of(1, 3, 5); 
    Stream<Integer> stream2 = Stream.of(2, 4, 6); 
    Stream<Integer> stream3 = Stream.of(18, 15, 36);
    
    Stream<Integer> resultingStream = Stream.concat( 
       Stream.concat(stream1, stream2), stream3);
    
    assertEquals( 
        Arrays.asList(1, 3, 5, 2, 4, 6, 18, 15, 36),
        resultingStream.collect(Collectors.toList())); 
}
复制代码

这种方法对于更多的流变得不可行。当然,我们可以创建中间变量或辅助方法以使其更具可读性,但我们还有更好的办法:

@Test 
public void merge() { 
    Stream<Integer> stream1 = Stream.of(1, 3, 5); 
    Stream<Integer> stream2 = Stream.of(2, 4, 6); 
    Stream<Integer> stream3 = Stream.of(18, 15, 36); 
    Stream<Integer> stream4 = Stream.of(99); 
    
    Stream<Integer> resultingStream = Stream.of( 
        stream1, stream2, stream3, stream4) 
        .flatMap(i -> i); 

    assertEquals( 
        Arrays.asList(1, 3, 5, 2, 4, 6, 18, 15, 36, 99), 
        resultingStream.collect(Collectors.toList())); 
}
复制代码

它经过两个步骤:

  • 首先创建一个包含 4 个 Streams 的新 Stream,生成嵌套的流 Stream<Stream>
  • 然后我们使用恒等函数将它 flatMap() 转换为 Stream

使用 StreamEx

StreamEx 是一个开源 Java 库,它扩展了 Java 8 Streams 的可能性。它使用 StreamEx 类作为对 JDK 的 Stream 接口的增强。

合并流

StreamEx 库允许我们使用 append() 实例方法合并流:

@Test 
public void merge() { 
    Stream<Integer> stream1 = Stream.of(1, 3, 5); 
    Stream<Integer> stream2 = Stream.of(2, 4, 6); 
    Stream<Integer> stream3 = Stream.of(18, 15, 36); 
    Stream<Integer> stream4 = Stream.of(99); 
    
    Stream<Integer> resultingStream = StreamEx.of(stream1) 
        .append(stream2) 
        .append(stream3) 
        .append(stream4); 
    
    assertEquals( 
        Arrays.asList(1, 3, 5, 2, 4, 6, 18, 15, 36, 99), 
        resultingStream.collect(Collectors.toList())); 
}
复制代码

由于它是一个实例方法,我们可以轻松地将它链接起来并附加多个 streams。

使用prepend()合并流

StreamEx 还包含一个方法,该方法在另一个之前添加元素,称为 prepend()

@Test 
public void merge() { 
    Stream<String> stream1 = Stream.of("foo", "bar");
    Stream<String> openingBracketStream = Stream.of("[");
    Stream<String> closingBracketStream = Stream.of("]");
    
    Stream<String> resultingStream = StreamEx.of(stream1) 
        .append(closingBracketStream) 
        .prepend(openingBracketStream); 
    
    assertEquals( 
        Arrays.asList("[", "foo", "bar", "]"), 
        resultingStream.collect(Collectors.toList())); 
}
复制代码

使用Jooλ

jooλ是JDK 8兼容库,可为JDK提供有用的扩展。此处最重要的流抽象称为 SEQ。请注意,这是一个顺序和有序的流,因此调用 parallel() 将没有效果。

合并流

和StreamEx库一样,jooλ也有append()方法:

@Test 
public void merge() { 
    Stream<Integer> seq1 = Stream.of(1, 3, 5); 
    Stream<Integer> seq2 = Stream.of(2, 4, 6); 

    Stream<Integer> resultingSeq = Seq.ofType(seq1, Integer.class) 
        .append(seq2); 

    assertEquals( 
        Arrays.asList(1, 3, 5, 2, 4, 6), 
        resultingSeq.collect(Collectors.toList())); 
}
复制代码

使用prepend()合并流

@Test 
public void merge() { 
    Stream<String> seq = Stream.of("foo", "bar");
    Stream<String> openingBracketStream = Stream.of("[");
    Stream<String> closingBracketStream = Stream.of("]");
    
    Stream<String> resultingStream = Seq.ofType(seq, String.class)
        .append(closingBracketStream) 
        .prepend(openingBracketStream); 
    
    assertEquals( 
        Arrays.asList("[", "foo", "bar", "]"), 
        resultingStream.collect(Collectors.toList())); 
}
复制代码

总结

可以看出来合并两个流的时候使用 JDK 8 比较简单方便,但当我们需要多个流合并时,使用SteamEx或JOOλ库可以通过append()的方式追加流,可读性更高。

猜你喜欢

转载自juejin.im/post/7032233642193748004