JDK1.8学习笔记6-Stream流

本文介绍了Java 8的Stream流特性,包括与传统迭代器的区别、流的构成和操作分类。详细讲解了如何获取Stream流、并行操作的优势、流的中间和终止操作,并通过实例展示了Stream简化代码的过程,同时提到了使用流时的陷阱和注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于Stream流

Stream流是一系列元素支持串行和并行聚合操作

  • Collection提供了新的stream()方法
  • 流不存储值,而是通过管道的方式获取值
  • 流的本质是函数式的—即对流的操作会生成一个结果,不过并不会修改底层的数据源,集合可以作为流的底层数据源
  • 延迟查找—很多流操作(过滤,映射,排序等)都可以延迟实现

与传统迭代器的不同之处

  • 和迭代器不同的是,Stream可以并行化操作,而迭代器只能命令式地,串行化操作
  • 当使用串行方式去遍历时,每个元素读完后再读下一个元素
  • 使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出
  • Stream的并行操作依赖于Java7引入的Fork/Join框架

1 Stream流的构成

Stream流由三部分构成

  • 零个或多个中间操作
  • 终止操作

2 Stream流操作的分类

  • 惰性求值—零个或多个操作
  • 及早求值—终止操作

3 得到Stream流的三种方式

  • 通过流的静态方法

  • 通过数组的方式

  • 通过集合的方式

  • 示例

public class StreamTest {

    public static void main(String[] args) {

        //1.通过流的静态方法
        Stream<String> stream1 = Stream.of("hello", "world", "hello world");
		Stream<String> stream2 = Stream.generate(UUID.randomUUID()::toString);
		Stream<Integer> stream3 = Stream.iterate(1, item -> item + 2).limit(10);

        //2.通过数组的方式
        String[] arr = {"hello", "world", "hello world"};
        Stream<String> stream2 = Stream.of(arr);

        //3.通过集合的方式
        List<String> list = Arrays.asList("hello", "world", "hello world");
        Stream<String> stream3 = list.stream();

    }
}

4 通过使用流来简化代码

  • 举例
public class SteamTest {


    public static void main(String[] args) {

        //迭代输出1,2,3
        IntStream.of(1, 2, 3).forEach(System.out :: println);
        System.out.println("+++++");

        //输出3,8(不包含8)之间的数
        IntStream.range(3, 8).forEach(System.out :: println);
        System.out.println("+++++");


        //输出3,8(包含8)之间的数
        IntStream.rangeClosed(3, 8).forEach(System.out :: println);
        System.out.println("+++++");
		
		//将整型list中元素两倍之和
		List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        System.out.println(list.stream().map(item -> 2 * item).reduce(0, Integer::sum));
    }
}

执行结果

1
2
3
+++++
3
4
5
6
7
+++++
3
4
5
6
7
8
+++++
30

5 stream相关接口

5.1 collect接口

public class StreamTest {

    public static void main(String[] args) {

        //将Stream转为数组
        Stream<String> stream = Stream.of("hello", "world", "hello world");
		
		//其操作结果是将stream中的元素添加到list中
		//第一个参数为容器提供者
		//第二个参数为累加器
		//第三个参数为组合器
        List<String> resultList 
        	= stream.collect(() -> new ArrayList<>(), 
        	(list, item) -> list.add(item), 
        	(list1, list2) -> list1.addAll(list2));
        //通过方法引用,上述代码可简化为
        //List<String> resultList  = stream.collect(ArrayList::new, List::add, List::addAll);
        
        System.out.println(resultList);
        resultList.forEach(System.out :: println);
    }
}

执行结果

hello
world
hello world

5.2 map映射接口及reduce压缩接口

public class StreamTest {

    public static void main(String[] args) {
        //对整型list中的数据*2并得到和
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

        System.out.println(list.stream().map(item -> 2 * item).reduce(0, Integer::sum));
    }
}

执行结果

30

示例中的map接口起到的作用就是将流中的元素*2并返回
reduce接口起到的作用则是将流中的元素以累加求和的方式对流中的元素进行压缩并返回

5.3 多个接口整合使用

  • 示例1
public class StreamTest {


    public static void main(String[] args) {
        List<String> list1 = Arrays.asList("hello", "world", "hello world", "test");

        //将元素转为大写并输出
        list1.stream().map(String::toUpperCase).forEach(System.out :: println);


        System.out.println("+++++++");
        //将元素的幂输出
        List<Integer> list2 = Arrays.asList(1, 2, 3, 4, 5);
        list2.stream().map(item -> item*item).forEach(System.out :: println);

        System.out.println("+++++++");
        //将多个list整合为一个list并元素的幂输出
        Stream<List<Integer>> stream = Stream.of(Arrays.asList(1), Arrays.asList(2), Arrays.asList(3), Arrays.asList(4, 5, 6));
        stream.flatMap(list -> list.stream().map(item -> item * item)).forEach(System.out :: println);
    }
}

执行结果

HELLO
WORLD
HELLO WORLD
TEST
+++++++
1
4
9
16
25
+++++++
1
4
9
16
25
36

  • 示例2
public class StreamTest {

    public static void main(String[] args) {
        //找出流中大于2的元素,然后将每个元素乘以2,然后忽略掉流中的前两个元素,然后再取流中的前两个元素,最后求出流中元素的和
        Stream<Integer> stream = Stream.iterate(1, item -> item + 2).limit(10);
        System.out.println(stream.filter(item -> item > 2).mapToInt(item -> item * 2).skip(2).limit(2).sum());

    }
}

执行结果

32

-示例3

public class StreamTest {


    public static void main(String[] args) {
        //找出流中大于2的元素,然后将每个元素乘以2,然后忽略掉流中的前两个元素,然后再取流中的前两个元素,最后求出流中最小和最大的元素
        Stream<Integer> stream = Stream.iterate(1, item -> item + 2).limit(10);

        IntSummaryStatistics intSummaryStatistics = stream.filter(item -> item > 2).mapToInt(item -> item * 2).skip(2).limit(2).summaryStatistics();

        System.out.println(intSummaryStatistics.getMax());
        System.out.println(intSummaryStatistics.getMin());
    }
}

执行结果

18
14

6 Stream陷阱

6.1 重复操作同一个流报错

如下代码

public class StreamTest {
    public static void main(String[] args) {
        //找出流中大于2的元素,然后将每个元素乘以2,然后忽略掉流中的前两个元素,然后再取流中的前两个元素,最后求出流中最小和最大的元素
        Stream<Integer> stream = Stream.iterate(1, item -> item + 2).limit(10);
        System.out.println(stream);
        System.out.println(stream.filter(item -> item > 2));
        System.out.println(stream.distinct());
    }
}

执行结果

java.util.stream.SliceOps$1@15aeb7ab
java.util.stream.ReferencePipeline2@3feba861Exceptioninthread"main"java.lang.IllegalStateException:streamhasalreadybeenoperateduponorclosedatjava.util.stream.AbstractPipeline.<init>(AbstractPipeline.java:203)atjava.util.stream.ReferencePipeline.<init>(ReferencePipeline.java:94)atjava.util.stream.ReferencePipeline2@3feba861 Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed at java.util.stream.AbstractPipeline.<init>(AbstractPipeline.java:203) at java.util.stream.ReferencePipeline.<init>(ReferencePipeline.java:94) at java.util.stream.ReferencePipeline2@3feba861Exceptioninthread"main"java.lang.IllegalStateException:streamhasalreadybeenoperateduponorclosedatjava.util.stream.AbstractPipeline.<init>(AbstractPipeline.java:203)atjava.util.stream.ReferencePipeline.<init>(ReferencePipeline.java:94)atjava.util.stream.ReferencePipelineStatefulOp.(ReferencePipeline.java:647)
at java.util.stream.DistinctOps$1.(DistinctOps.java:56)
at java.util.stream.DistinctOps.makeRef(DistinctOps.java:55)
at java.util.stream.ReferencePipeline.distinct(ReferencePipeline.java:384)
at com.learn.jdk8.stream.StreamTest10.main(StreamTest10.java:20)

报错原因是
同一个stream流不能被连续操作两次
为了规避上述问题,代码需更改为

public class StreamTest {

    public static void main(String[] args) {
        Stream<Integer> stream = Stream.iterate(1, item -> item + 2).limit(10);

        System.out.println(stream);
        Stream<Integer> stream1 = stream.filter(item -> item > 2);
        System.out.println(stream1);
        Stream<Integer> stream2 = stream1.distinct();
        System.out.println(stream2);
    }
}

执行结果

java.util.stream.SliceOps$1@15aeb7ab
java.util.stream.ReferencePipeline$2@3feba861
java.util.stream.DistinctOps$1@5b480cf9

但是建议使用示例3方法链的方式进行调用,而不是上述方式

6.2

如下代码

public class StreamTest {
    public static void main(String[] args) {
        IntStream.iterate(0, i -> (i + 1) % 2).distinct().limit(6).forEach(System.out :: println);
    }
}

执行结果

0
1

虽然正常输出了,但是程序并未终止,原因就在于iterate是无限循环的,若为在其后使用limit方法对其进行限制,那么程序将会一直执行下去
所以应更改为

public class StreamTest {
    public static void main(String[] args) {
        IntStream.iterate(0, i -> (i + 1) % 2).limit(6).distinct().forEach(System.out :: println);
    }
}

7 中间操作和终止操作的区别

  • 示例1
public class StreamTest {

    public static void main(String[] args) {

        Stream<String> stream = Stream.of("hello", "world", "hello world");
        //将首字母大写并输出
        stream.map(item -> {
            String result = item.substring(0, 1).toUpperCase() + item.substring((1));
            System.out.println(result);
            return result;
        });
    }
}

执行结果

  • 示例2
public class StreamTest {

    public static void main(String[] args) {

        Stream<String> stream = Stream.of("hello", "world", "hello world");
        //将首字母大写并输出
        stream.map(item -> {
            String result = item.substring(0, 1).toUpperCase() + item.substring((1));
            System.out.println(result);
            return result;
        }).forEach(System.out :: println);
    }
}

执行结果

Hello
Hello
World
World
Hello world
Hello world

两示例的区别仅在于末尾是否有终止操作方法forEach,但执行结果却不同
原因是由于中间操作方法惰性求值,只有被终止操作方法调用时,才会执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值