Java8_Stream流_详解

Java8_Stream流

(1) lambda_详解
(2) stream流的分组操作

一、什么是Stream流?

定义:流就是由支持数据处理的源数据生成的元素序列

流的特点:

  1. java.util.Stream 流是一系列数据项,它不是一种数据结构。
  2. Stream 的创建需要指定一个数据源, 比如 集合、数组, 不支持Map。
  3. 每次生成的流,只能使用(消费)一次。(二次使用报:stream has already been operated upon or closed
  4. Stream 的操作可以串行执行或者并行执行。

流与集合的区别:

  • 集合是一个内存中的数据结构,集合里面的元素都的先算出来才能添加到集合中
  • 而流的话,它像一个延迟创建的集合,只有一个在消费者要求的时候才会计算值(也就是–>按需生成
  • 流的话是内部迭代,集合是外部迭代

二、流的使用

1)流的创建
写法
从Collection(集合)中构建list.stream()
由值创建流Stream.of(v1,v2,v3)
从数组中构建Arrays.stream(arr)
从文件中构建Files.lines(path)
由函数生成创建无限流

1)从集合中获取流

	//从集合中获取流
    @Test
    public void fromCollection() {
        List<String> list = new ArrayList<>(Arrays.asList("a","b"));
        Stream<String> stream = list.stream();
    }

2)使用Stream.of()获取流

 Stream<Integer> stream = Stream.of(1, 2, 3);  

3)使用Arrays.stream()从数组中获取流

    @Test
    public void fromArrays() {
        String[] arr = {"aa", "ee", "ii"};
        Stream<String> stream = Arrays.stream(arr);
    }

4)从文件中获取流

     @Test
    public void fromFile() throws IOException {
        Path path = Paths.get("D:\\IdeaProjects\\DataStructure\\Data\\src\\pers\\xu\\lambda\\LambdaDemo.java");
        Stream<String> stream = Files.lines(path);
        stream.forEach(System.out::println);
    }

5)使用函数创建无效流

有2种方法:

  • Stream.iterate():接受一个初始值,以及一个依次引用在每个产生的新值上的Lambda

  • Stream.generate():接受一个Supplier类型的Lambda提供新的值

    @Test
    public void fromIterate() {
        // 从2开始,后面 n *= 2  2,4,8
        Stream<Integer> stream1 = Stream.iterate(2, n -> n*2).limit(3);
        
        // 使用 generate()
        Stream<Double> stream2 = Stream.generate(Math::random).limit(3);
    }

6)示例:创建一个Apple对象的无限流

	// 自定义类实现 供给型接口
    class AppleSupplier implements Supplier<Apple>{

        private final Random random = new Random();
        String[] colors = {"red","green","blue","yellow","white"};

        @Override
        public Apple get() {
            int size = random.nextInt(100);
            return new Apple(size,colors[random.nextInt(colors.length)]);
        }
    }

    @Test
    public void test21(){
        Stream.generate(new AppleSupplier())
                .limit(6)
                .sorted(Comparator.comparingInt(Apple::getSize))
                .forEach(System.out::println);
    }

// 输出结果
Apple{size=21, color='blue'}
Apple{size=30, color='white'}
Apple{size=64, color='red'}
Apple{size=65, color='yellow'}
Apple{size=75, color='white'}
Apple{size=99, color='green'}

1)中间操作

filter、limit、skip、map、flatMap的使用

  • filter:过滤
  • limit:返回一个不超过给定长度的流;
  • skip:返回一个扔掉了前n 个元素的流;【skip(k):跳过前k个元素
  • map:将流中元素其映射成一个新的元素
  • flatMap:把一个流中的每个值都换成另一个流,然后把所有的流连接
  • peek:接受一个Consumer接口。对当前流中的元素进行操作,返回的原来的流
  • distinct:去重
  • sorted:排序

1)filter():过滤

接收一个断言式接口(Predicate)作为参数

    @Test
    public void testFilter(){
        List<Integer> nums = Arrays.asList(10, 2, 2, 5, 6, 6, 3);
        //从流中选出偶数,且没有重复
        nums.stream().filter(i -> i%2==0)  //过滤
                .distinct()  //去重
                .sorted((x,y) -> y.compareTo(x)) //排序
                .forEach(x -> System.out.println(x+"-"));
    }

2)limit():截短流

该方法会返回一个不超过给定长度的流;

    @Test
    public void testLimit(){
        String[] arr = {"aa","b","cc","d"};
       Arrays.stream(arr).
               limit(3). //取前3个
               forEach(System.out::println);
    }

3)skip():跳过元素

返回一个扔掉了前n 个元素的流

    @Test
    public void testSkip(){
        String[] arr = {"aa","b","cc","d"};
        Arrays.stream(arr).
                skip(2). //跳过前2个 [aa,b
                forEach(System.out::println);
        //结果:cc d
    }

4)map():映射

将字符串元素转换为它的长度

    @Test
    public void testMap2(){
        List<String> strs = Arrays.asList("a", "bb", "ccc", "dd");
        strs.stream().map(x -> x.length())   // 方法引用:map(String::length)
                .collect(Collectors.toList())
                .forEach(System.out::println);
    }

5)flatmap():流的扁平化

示例:获取所有单词的字符表,过滤重复字符

 	@Test
    public void test13(){
        String[] arr = {"hello","TOM","go","Nothing"};
        Stream<String> words = Arrays.stream(arr);

        List<String> list = words.map(s -> s.split(""))    // 映射为字符数组
                                 .flatMap(Arrays::stream)  // 将每个字符数组生成的流,扁平为一个流
                                 .distinct()
                                 .collect(Collectors.toList());

        System.out.println(list);
    }

// 输出结果:
[h, e, l, o, T, O, M, g, N, t, i, n]

6)peek()

接受一个Consumer接口,返回原来的流

	public List<Apple> getList(){
        List<Apple> list = new ArrayList<>();
        list.add(new Apple(12,"red"));
        list.add(new Apple(5,"deepRed"));
        list.add(new Apple(9,"green"));
        list.add(new Apple(14,"yellow"));
        list.add(new Apple(3,"blue"));
        list.add(new Apple(3,"red"));

        return list;
    }

    @Test
    public void test11(){
        List<Apple> list = this.getList();

        list.stream().peek(apple -> {
            if("red".equals(apple.getColor())){
                apple.setColor("pink");
            }
        }).forEach(System.out::println);
    }

// 输出结果
Apple{size=12, color='pink'}
Apple{size=5, color='deepRed'}
Apple{size=9, color='green'}
Apple{size=14, color='yellow'}
Apple{size=3, color='blue'}
Apple{size=3, color='pink'}

7)sorted()

把苹果按size排序

	@Test
    public void test12(){
        List<Apple> list = this.getList();

        list.stream().
                sorted(Comparator.comparingInt(Apple::getSize)).
                forEach(System.out::println);
    }
// 输出结果
Apple{size=3, color='blue'}
Apple{size=3, color='red'}
Apple{size=5, color='deepRed'}
Apple{size=9, color='green'}
Apple{size=12, color='red'}
Apple{size=14, color='yellow'}

2)终端操作

终端操作会从流的流水线 生成结果。
不做终止操作,中间环节就不执行,体现的就是延迟加载的思想

  • match:查看元素是否匹配(返回boolean),
    • allMatch(): 检查是否匹配所有元素
    • anyMatch() : 检查是否至少匹配一个元素
    • noneMatch(): 检查是否与 所有元素 都不匹配
  • find:
    • isPresent()将在Optional包含值的时候返回true, 否则返回false;
    • ifPresent(Consumer block)`会在值存在的时候执行给定的代码块;
    • T get()会在值存在时返回值,否则抛出一个NoSuchElement异常;
    • T orElse(T other)会在值存在时返回值,否则返回一个默认值;
    • Optional<T> of(T value) : 通过value构造一个Optional;
  • max(Comparator c): 返回流中最大值
  • min(Comparator c):返回流中最小值
  • forEach(Consumer c):内部迭代
  • findFirst():取集合第一个元素
  • reduce():规约
  • 把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。
  • 从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。
  • 例如 Stream 的 sum 就相当于Integer sum = integers.reduce(0, (a, b) -> a+b);
  • 也有没有起始值的情况,这时会把 Stream 的前面两个元素组合起来,返回的是 Optional 。

1)count()

对流中的元素进行计数,返回一个long类型值

    @Test
    public void test18(){
        int[] arr = {1,2,3,4,5,0};
        long count = Arrays.stream(arr).filter(x -> x % 2 == 1).count();
        System.out.println(count);
    }
// 输出:3

2)match()

    @Test
    public void test17(){
        List<Integer> arr = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
        System.out.println(arr.stream().allMatch(i -> i > 10));
        System.out.println(arr.stream().anyMatch(i -> i > 6));
        System.out.println(arr.stream().noneMatch(i -> i < 0));
    }
// 输出
false
true
true
  1. reduce:规约

什么是规约?

就是将流中所有元素反复结合起来,得到一个值,比如一个Integer。称为规约

示例1:元素求和,最大值,最小值

    @Test
    public void test15(){
        int[] arr = {1,2,3,4,5,0};

        // 带起始种子
        int sum1 = Arrays.stream(arr).reduce(0, (a,b)->a+b);
        int sum2 = Arrays.stream(arr).reduce(0, (a,b)->Integer.sum(a,b));
        int sum3 = Arrays.stream(arr).reduce(0, Integer::sum);
        System.out.println("sum= "+sum1);

        // 无起始种子,返回Option
        OptionalInt sum4 = Arrays.stream(arr).reduce(Integer::sum);
        sum4.ifPresent(System.out::println);

        // 最大值
        int max = Arrays.stream(arr).reduce(0, Integer::max);
        System.out.println("max= "+max);

        // 最小值
        int min = Arrays.stream(arr).reduce(0, Integer::min);
        System.out.println("min= "+min);
    }

// 输出结果
sum= 15
15
max= 5
min= 0

4)min / max(Comparator c)

	@Test
    public void test19(){
        List<Apple> list = this.getAppleList();
        // size 最大的苹果
        Optional<Apple> maxApple1 = list.stream().max(Comparator.comparingInt(Apple::getSize));
        
        // size 最小的苹果
        Optional<Apple> minApple2 = list.stream().min(Comparator.comparingInt(Apple::getSize));
    }

三、收集器

一般来说,Collector会对元素应用一个转换函数,并将结果累积在一个数据结构里面,产生最终输出

具体的说,对流调用collect 方法将对流中的元素触发一个归约操作(由Collector 来参数化

主要提供3大功能

  • 将元素归约和汇总为一个值
  • 元素分组
  • 元素分区

1)查找流中元素最大值和最小值

  • Collectors.maxBy():最大值
  • Collectors.minBy():最小值
@Test
    public void test19(){
        List<Apple> list = this.getAppleList();
        // size 最大的苹果
        Optional<Apple> maxApple = list.stream().collect(Collectors.maxBy(Comparator.comparingInt(Apple::getSize)));
       
        // size 最小的苹果
        Optional<Apple> minApple = list.stream().collect(Collectors.minBy(Comparator.comparingInt(Apple::getSize)));

    }

2)汇总

  • Collectors.summingInt():总和
  • Collectors.averagingInt():求平均
  • Collectors.summarizingInt():数据汇总
    @Test
    public void test19(){
        List<Apple> list = this.getAppleList();
        
        // 对apple的size汇总
        Integer sizeSum = list.stream().collect(Collectors.summingInt(Apple::getSize));
        System.out.println("sum= "+sizeSum);

        // 对apple的size求平均
        Double avg = list.stream().collect(Collectors.averagingInt(Apple::getSize));
        System.out.println("avg= "+avg);

        // 对apple进行大汇总,包括个数,size最大,最小,平均,总和
        IntSummaryStatistics summaryStatistics = list.stream().collect(Collectors.summarizingInt(Apple::getSize));
        System.out.println(summaryStatistics);

    }

// 输出结果
count= 6
sum= 46
avg= 7.666666666666667
IntSummaryStatistics{count=6, sum=46, min=3, average=7.666667, max=14}

3)连接字符串

    @Test
    public void test20(){
        List<Apple> list = this.getAppleList();
        String str = list.stream().map(Apple::getColor).collect(Collectors.joining());
        System.out.println(str);
    }

// 输出结果
reddeepRedgreenyellowbluered

四、串行流与并行流

Stream有串行和并行两种,串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。

使用:将 stream() 改为parallelStream() 即可

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值