【Java】流(Stream)快速入门

本文为Java初学者提供了一套全面的Stream流学习指导,包括流的基本概念、Optional使用方法、流的创建方式及处理技巧等内容。通过实战案例演示流的过滤、排序、收集等操作。

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

本文是面向Java初学者的流(Stream)学习指导教程。文章内容偏向怎么用,而弱化其诞生背景、流的概念、内部原理等说明解释性的语段。

主要内容:

  1. Optional
  2. 创建流
  3. 操作流
  4. 收集流

目录

什么是流

Optional

简单认识和理解Optional

Optional操作

流获取(创建)

空流

集合、数组流

生成流

序列流

基本数据类型流

流处理

过滤 filter

去重 distinct

排序 sorted

流收集

收集到容器中

收集时统计

收集时分区、分组

收集时接合

收集时归约(reducing)

流终止

遍历forEach

聚合min/max/count

查找匹配find/match

规约reduce


什么是流

背景:

  1. 在数组和集合中,许多操作(如遍历)依然十分复杂、代码量大。
  2. 为了更一步简化对集合元素的操作,引入流的概念。

Stream流能解决什么问题?

  1. JDK1.8中,可以解决已有集合类库或者数组API的弊端。例如,遍历迭代集合的操作很繁杂。
  2. Stream流简化集合和数组各种的操作!
  3. 需要掌握Lambda表达式的写法。

第一个Stream程序:


import java.util.List;
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");

        // 需求:从集合中筛选出所有姓张的人出来。然后再找出姓名长度是3的人。

        // 原始实现
        // a.先找出姓张的人。
        //List<String> zhangLists = new ArrayList<>();
        //for (String s : list) {
        //    if(s.startsWith("张")){
        //        zhangLists.add(s);
        //    }
        //}
        //System.out.println(zhangLists);
        //
        // b.张姓姓名长度为3的人
        //List<String> zhangThreeLists = new ArrayList<>();
        //for (String s : zhangLists){
        //    if(s.length() == 3){
        //        zhangThreeLists.add(s);
        //    }
        //}
        //System.out.println(zhangThreeLists);

        // 流实现
        list.stream().filter(s -> s.startsWith("张")).filter( s -> s.length()== 3 )
                .forEach(System.out::println);
    }
}

Optional

在流操作中,有些流操作后的返回值为Optional,所以这里先介绍这种类型。

对Optional的理解:

  • 类似于一种数据类型,如Object、Integer、Double等。
  • Optional类包装了许多用于解决null的各种方法,主要目的是解决臭名昭著的空指针异常(NullPointerException)问题。
  • 它是一个可以为null的类型对象。
  • Optional和流(Stream)搭配使用才能发挥它的最佳实力!

简单认识和理解Optional

问题:

常规解决方案:

public class Main {
    public static void main(String[] args) {
        String str = null;

        //空指针解决方案一:使用try块捕捉、处理
        try {
            str.length();
            System.out.println("------待执行------");
        } catch (NullPointerException e){
            e.printStackTrace();
            System.out.println("发生了空指针异常!");
        }

        //解决方案二:使用判断语句
        if(str != null) {
            str.length();
            System.out.println("------待执行------");
        } else {
            System.out.println("str是null");
        }
    }
}

最佳解决方案:使用Optional申明一个可以为null的String变量

import java.util.Optional;

public class Main {
    public static void main(String[] args) {
        String str = null;

        //Optional<String>: 一个String类型的Optional容器
        Optional<String> strOpt = Optional.ofNullable(str); //把str赋值给strOpt,相当于这样:strOpt = str;

        //如果strOpt不是null,则返回strOpt中的值
        //如果strOpt是null,则返回orElse设置的默认值
        System.out.println(strOpt.orElse("default")); //输出:default
    }
}

Optional操作

创建:

  1. Optional<String> optStr = Optional.empty();//创建一个包装对象值为空的Optional对象
  2. Optional<String> optStr1 = Optional.of("值");//创建包装对象值非空的Optional对象
  3. Optional<String> optStr2 = Optional.ofNullable(null); //创建包装对象值允许为空的Optional对象

判断获取值:

  1. T get() 如果存在值,则返回该值,否则抛出 NoSuchElementException 。
  2. orElse 如果创建的Optional中有值存在,则返回此值,否则返回一个默认值
  3. orElseGet 如果创建的Optional中有值存在,则返回此值,否则返回一个由Supplier接口生成的值
  4. orElseThrow 如果创建的Optional中有值存在,则返回此值,否则抛出一个由指定的Supplier接口生成的异常

Optional实例:

import java.util.Optional;

public class MainTest {
    public static void main(String[] args) throws Exception {
        //Optional相当于是一种类型的申明器,可以申明多种类型,并且这种类型可以为null
        Optional<String> op01 = Optional.empty();
        Optional<String> op02 = Optional.of("aa");
        Optional<String> op03 = Optional.ofNullable(null);

        //get():该Optional变量存在值则返回该值,否则抛出 NoSuchElementException
        System.out.println(op02.get()); //输出:aa

        //orElse(other):如果创建的Optional中有值存在,则返回此值,否则返回other中的值
        System.out.println(op01.orElse("默认值")); //输出:默认值

        //orElseGet():如果创建的Optional中有值存在,则返回此值,否则返回一个由Supplier接口生成的值
        System.out.println(op01.orElseGet(() -> "这是由Supplier生成的值"));

        //如果创建的Optional中有值存在,则返回此值,否则抛出一个由指定的Supplier接口生成的异常
        System.out.println(op01.orElseThrow(TempRuntime::new));
        //输出:
        //Exception in thread "main" kyle.TempRuntime: 自定义TempRuntime异常
        //at java.base/java.util.Optional.orElseThrow(Optional.java:408)
        //at kyle.MainTest.main(MainTest.java:22)
    }
}

class TempRuntime extends RuntimeException {
    public TempRuntime() {
        super("自定义TempRuntime异常");
    }
}

进行处理:

  • filter 如果创建的Optional中的值满足filter中的条件,则返回包含该值的Optional对象,否则返回一个空的Optional对象
  • map 如果创建的Optional中的值存在,对该值执行提供的Function函数调用
  • boolean isParent 判断该Optional是否存在值

流获取(创建)

流创建的三种主要方式:

  1. 通过 java.util.Collection.stream() 方法用集合创建流
  2. 使用java.util.Arrays.stream(T[] array)方法用数组创建流
  3. 使用Stream的静态方法:of()、iterate()、generate()

空流

public static <T> Stream<T> empty()  空流

  1. java.util.Stream
  2. 产生一个空的流

功能:在创建流时使用empty()方法来避免对没有元素的流返回null。

public Stream<String> streamOf(List<String> list) {
    return list == null || list.isEmpty() ? Stream.empty() : list.stream();
}

集合、数组流

创建流的几种方式:

  1. 集合 Collection.stream()
  2. 静态方法 Stream.of(元素1,元素2,,,)
  3. 数组 Arrays.stream

基础的流获取(创建)

import java.util.Collection;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Arrays;
import java.util.stream.Stream;

public class Main {
    /*
        集合获取流:集合对象.stream();
        数组获取流:Arrays.stream(数组)   /  Stream.of(数组);
     */
    public static void main(String[] args) {
        /** --------------------Collection集合获取流-------------------------------   */
        // Collection集合如何获取Stream流。
        Collection<String> c = new ArrayList<>();
        Stream<String> ss = c.stream();

        /** --------------------Map集合获取流-------------------------------   */
        Map<String, Integer> map = new HashMap<>();
        // 先获取键的Stream流。
        Stream<String> keyss = map.keySet().stream();
        // 在获取值的Stream流
        Stream<Integer> valuess = map.values().stream();
        // 获取键值对的Stream流(key=value: Map.Entry<String,Integer>)
        Stream<Map.Entry<String,Integer>> keyAndValues = map.entrySet().stream();

        /** ---------------------数组获取流------------------------------   */
        // 数组也有Stream流。
        String[] arrs = new String[]{"Java", "JavaEE" ,"Spring Boot"};
        Stream<String> arrsSS1 = Arrays.stream(arrs);
        Stream<String> arrsSS2 = Stream.of(arrs);
    }
}

生成流

  1. static<T> Builder<T> builder()  根据元素构建一个流
  2. static <T> Stream<T> generate (Supplier<? extends T> s)  生成无限流
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args)  {
        //构建流
        Stream<String> streamBuilder = Stream.<String>builder().add("a").add("b").add("c").build();
        //无限流
        Stream<String> echo = Stream.generate(() -> "hello, kyle.");
        Stream<Double> randNum = Stream.generate(Math::random).limit(10); //生成10个

        streamBuilder.forEach(e -> System.out.print(e + "  ")); //输出:a b c
    }
}

序列流

  1. static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)  生成序列流:接收一个种子值和一个函数。第一个值是种子的值,第二个值为f(seed),第三个值为f(f(seed)),以此类推。
  2. static <T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)  有限序列流:参数一为种子值,参数二为生成的条件值,参数三为递增值。
import java.math.BigInteger;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args)  {
        //序列流
        Stream<BigInteger> integerStream = Stream.iterate(BigInteger.ZERO, n->n.add(BigInteger.ONE)); //无限

        var limit = new BigInteger("1000");
        Stream<BigInteger> integerStream1 = Stream.iterate(BigInteger.ZERO,
                n->n.compareTo(limit)<0,
                n->n.add(BigInteger.ONE)); //有限
        integerStream1.forEach(System.out::println);

        Stream<Integer> integerStream2 = Stream.iterate(40, n -> n + 2).limit(20); //有限
    }
}

基本数据类型流

对于八种基本数据类型

  1. boolean、byte、char、short、int、String对应IntStream;
  2. long对应LongStream;
  3. float、double对应DoubleStream;
import java.util.Random;
import java.util.regex.Pattern;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args)  {
        IntStream intStream = IntStream.range(1, 3); //以区间[参数1,参数2)构建序列流
        LongStream longStream = LongStream.rangeClosed(1, 3); //以区间[参数1,参数2]构建序列流
        Random random = new Random();
        DoubleStream doubleStream = random.doubles(3); //构建三个元素的流

        intStream.forEach(System.out::println); //输出:1 2
        longStream.forEach(System.out::println); //输出:1,2,3
        doubleStream.forEach(System.out::println); //输出:三个随机double数

        //串与流
        IntStream streamOfChars = "abc".chars();
        Stream<String> streamOfString = Pattern.compile(", ").splitAsStream("a, b, c");
        streamOfChars.forEach(System.out::println); //输出:97,98,99
        streamOfString.forEach(System.out::println); //输出:a b c
    }
}

流处理

流处理主要是对流中的元素进行一些列的操作,例如条件过滤、数据转换、去重、排序等操作。

过滤 filter

Stream<T> filter(Predicate<? super T> predicate)

  1. Predicate是函数式接口,可以直接用lambda代替;
  2. 如果有复杂的过滤逻辑,则用or、and、negate方法组合
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class MainTest {
    public static void main(String[] args) throws Exception {
        List<Integer> list = new ArrayList<>();
        list.add(10);list.add(22);list.add(33);list.add(456);list.add(99);
        Stream<Integer> stream = list.stream();
        stream.filter(e -> e>100).forEach(e -> System.out.print(e+" ")); //筛选出ArrayList中大于100的元素
    }
}

  1. <R> Stream<R> map(Function<? super T, ? extends R> mapper)
  2. IntStream mapToInt(ToIntFunction<? super T> mapper);
  3. LongStream mapToLong(ToLongFunction<? super T> mapper);
  4. DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class MainTest {
    public static void main(String[] args)  {
        Set<String> set = new HashSet<>();
        set.add("aa"); set.add("ee");
        set.add("bb"); set.add("ff");

        Stream<String> stream = set.stream();
        Set<String> newSet = stream.map(str -> str.toUpperCase()).collect(Collectors.toSet()); //将set中的元素全部转换为大写,再回写到set中
        newSet.stream().forEach(e -> System.out.print(e + " ")); //输出:AA EE BB FF
    }
}

去重 distinct

Stream<T> distinct();

import java.util.Arrays;
import java.util.List;

public class MainTest {
    public static void main(String[] args) {
        List<Integer> demo = Arrays.asList(1, 1, 2);
        demo.stream().distinct().forEach(System.out::println); //输出:1 2
    }
}

排序 sorted

Stream<T> sorted();

Stream<T> sorted(Comparator<? super T> comparator);

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class MainTest {
    public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("aa", "woman", 22, "Shanghai",588D));
        personList.add(new Person("bb", "man", 23, "Zhejiang",8820D));
        personList.add(new Person("cc", "woman", 42, "Shanghai", 8889D));
        personList.add(new Person("dd", "woman", 21, "Kunming", 8778D));

        // 按工资升序排序(自然排序)
        List<String> newList = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName)
                .collect(Collectors.toList());
        // 按工资倒序排序
        List<String> newList2 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed())
                .map(Person::getName).collect(Collectors.toList());
        // 先按工资再按年龄升序排序
        List<String> newList3 = personList.stream()
                .sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).map(Person::getName)
                .collect(Collectors.toList());
        // 先按工资再按年龄自定义排序(降序)
        List<String> newList4 = personList.stream().sorted((p1, p2) -> {
            if (p1.getSalary() == p2.getSalary()) {
                return p2.getAge() - p1.getAge();
            } else {
                double tmp = p2.getSalary() - p1.getSalary();
                if(tmp>0D) {
                    return 1;
                } else if(tmp<0D) {
                    return -1;
                } else {
                    return 0;
                }
            }
        }).map(Person::getName).collect(Collectors.toList());

        System.out.println("按工资升序排序:" + newList);
        System.out.println("按工资降序排序:" + newList2);
        System.out.println("先按工资再按年龄升序排序:" + newList3);
        System.out.println("先按工资再按年龄自定义降序排序:" + newList4);
        //输出:
        //按工资升序排序:[aa, dd, bb, cc]
        //按工资降序排序:[cc, bb, dd, aa]
        //先按工资再按年龄升序排序:[aa, dd, bb, cc]
        //先按工资再按年龄自定义降序排序:[cc, bb, dd, aa]
    }
}
class Person {
    private String name;
    private String gander;
    private Integer age;
    private String address;
    private Double salary;

    public Person(String name, String gander, Integer age, String address, Double salary) {
        this.name = name;
        this.gander = gander;
        this.age = age;
        this.address = address;
        this.salary = salary;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getGander() {
        return gander;
    }
    public void setGander(String gander) {
        this.gander = gander;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public Double getSalary() {
        return salary;
    }
    public void setSalary(Double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return name;
    }
}

流收集

把Stream流的数据转回成集合。

Stream的作用是:把集合转换成一根传送带,借用Stream流的强大功能进行的操作。但是实际开发中数据最终的形式还是应该是集合,最终Stream流操作完毕以后还是要转换成集合。这就是收集Stream流。

Stream流:手段。

集合:才是目的。

Collectors 具体方法

  1. toList List 把流中元素收集到List
  2. toSet Set 把流中元素收集到Set
  3. toCollection Coolection 把流中元素收集到Collection中
  4. groupingBy Map<K,List> 根据K属性对流进行分组
  5. partitioningBy Map<boolean, List> 根据boolean值进行分组

收集到容器中

全部由Collectors工具类调用:

  1. public static <T> Collector<T, ?, Collection<T>> toCollection()  收集到一个集合中
  2. public static <T> Collector<T, ?, List<T>> toList()  流转化成List
  3. public static <T> Collector<T, ?, Set<T>> toSet()  流转化成Set
  4. public static <T> Collector<T, ?, Map<T>> toMap ()  流转化成Map
  5. Object[] toArray();  流转成Object数组
  6. <A> A[] toArray(IntFunction<A[]> generator)  流转成A[]数组,指定类型A
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args)  {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
        list.add("张三丰");

        // 把stream流转换成Set集合。
        Stream<String> zhangLists = list.stream().filter(s -> s.startsWith("张"));
        Set<String> sets = zhangLists.collect(Collectors.toSet());
        System.out.println(sets);

        // 把stream流转换成List集合。
        Stream<String> zhangLists1 = list.stream().filter(s -> s.startsWith("张"));
        List<String> lists= zhangLists1.collect(Collectors.toList());
        System.out.println(lists);

        // 把stream流转换成数组。
        Stream<String> zhangLists2 = list.stream().filter(s -> s.startsWith("张"));
        Object[] arrs = zhangLists2.toArray();
        // 可以借用构造器引用申明转换成的数组类型!!!
        String[] arrs1 = zhangLists2.toArray(String[]::new);

        // 把Stream流转换为LinkedList
        List<String> resultLinkedList = list.stream()
                .collect(Collectors.toCollection(LinkedList::new));
        //IDEA代码优化提示:
        //  Can be replaced with 'java.util.LinkedList' constructor
        //  优化后:List<String> resultLinkedList = new LinkedList<>(list);
    }
}

收集时统计

  • 计数:count
  • 平均值:averagingInt、averagingLong、averagingDouble
  • 最值:maxBy、minBy
  • 求和:summingInt、summingLong、summingDouble
  • 统计以上所有:summarizingInt、summarizingLong、summarizingDouble
import java.util.ArrayList;
import java.util.DoubleSummaryStatistics;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class MainTest {
    public static void main(String[] args)  {
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("aa","woman",22,588D));
        personList.add(new Person("bb","man",23,8820D));
        personList.add(new Person("cc","woman",42,8889D));
        personList.add(new Person("dd","woman",21,8778D));

        //计数
        Long count = personList.stream().collect(Collectors.counting());
        //求Person类salary属性的平均值
        Double avg = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
        //根据age求最小值
        Optional<Integer> minAge = personList.stream().map(Person::getAge).collect(Collectors.minBy(Integer::compare));
        //求age的和
        Long sum = personList.stream().collect(Collectors.summingLong(Person::getAge));
        //求和所有数据为double
        DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));

        System.out.println(count); //输出:4
        System.out.println(avg); //输出:6768.75
        System.out.println(minAge); //输出;Optional[21]
        System.out.println(sum); //输出:108
        System.out.println(collect); //输出:DoubleSummaryStatistics{count=4, sum=27075.000000, min=588.000000, average=6768.750000, max=8889.000000}
    }
}
class Person {
    private String name;
    private String gander;
    private Integer age;
    private Double salary;

    public Person(String name, String gander, Integer age, Double salary) {
        this.name = name;
        this.gander = gander;
        this.age = age;
        this.salary = salary;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getGander() {
        return gander;
    }
    public void setGander(String gander) {
        this.gander = gander;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public Double getSalary() {
        return salary;
    }
    public void setSalary(Double salary) {
        this.salary = salary;
    }
}

收集时分区、分组

  • 分区:将stream按条件分为两个Map,比如员工按薪资是否高于8000分为两部分。
  • 分组:将集合分为多个Map,比如员工按性别分组。有单级分组和多级分组。

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class MainTest {
    public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("aa", "woman", 22, "Shanghai",588D));
        personList.add(new Person("bb", "man", 23, "Zhejiang",8820D));
        personList.add(new Person("cc", "woman", 42, "Shanghai", 8889D));
        personList.add(new Person("dd", "woman", 21, "Kunming", 8778D));

        // 将员工按薪资是否高于8000分组
        Map<Boolean, List<Person>> part = personList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
        // 将员工按性别分组
        Map<String, List<Person>> group = personList.stream().collect(Collectors.groupingBy(Person::getGander));
        // 将员工先按性别分组,再按地区分组
        Map<String, Map<String, List<Person>>> group2 = personList.stream().collect(Collectors.groupingBy(Person::getGander, Collectors.groupingBy(Person::getAddress)));
        System.out.println("员工按薪资是否大于8000分组情况:" + part);
        System.out.println("员工按性别分组情况:" + group);
        System.out.println("员工按性别、地区:" + group2);
        //输出:
        //员工按薪资是否大于8000分组情况:{false=[aa], true=[bb, cc, dd]}
        //员工按性别分组情况:{woman=[aa, cc, dd], man=[bb]}
        //员工按性别、地区:{woman={Kunming=[dd], Shanghai=[aa, cc]}, man={Zhejiang=[bb]}}
    }
}
class Person {
    private String name;
    private String gander;
    private Integer age;
    private String address;
    private Double salary;

    public Person(String name, String gander, Integer age, String address, Double salary) {
        this.name = name;
        this.gander = gander;
        this.age = age;
        this.address = address;
        this.salary = salary;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getGander() {
        return gander;
    }
    public void setGander(String gander) {
        this.gander = gander;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public Double getSalary() {
        return salary;
    }
    public void setSalary(Double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return name;
    }
}

收集时接合

joining可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class MainTest {
    public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("aa", "woman", 22, "Shanghai",588D));
        personList.add(new Person("bb", "man", 23, "Zhejiang",8820D));
        personList.add(new Person("cc", "woman", 42, "Shanghai", 8889D));
        personList.add(new Person("dd", "woman", 21, "Kunming", 8778D));

        String names = personList.stream().map(person -> person.getName()).collect(Collectors.joining(", "));
        System.out.println(names); //输出:aa, bb, cc, dd
    }
}
class Person {
    private String name;
    private String gander;
    private Integer age;
    private String address;
    private Double salary;

    public Person(String name, String gander, Integer age, String address, Double salary) {
        this.name = name;
        this.gander = gander;
        this.age = age;
        this.address = address;
        this.salary = salary;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getGander() {
        return gander;
    }
    public void setGander(String gander) {
        this.gander = gander;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public Double getSalary() {
        return salary;
    }
    public void setSalary(Double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return name;
    }
}

收集时归约(reducing)

Collectors类提供的reducing方法,相比于stream本身的reduce方法,增加了对自定义归约的支持。

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class MainTest {
    public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("aa", "woman", 22, "Shanghai",588D));
        personList.add(new Person("bb", "man", 23, "Zhejiang",8820D));
        personList.add(new Person("cc", "woman", 42, "Shanghai", 8889D));
        personList.add(new Person("dd", "woman", 21, "Kunming", 8778D));

        Optional<Double> sum2 = personList.stream().map(person -> person.getSalary()).reduce(Double::sum);
        System.out.println("员工薪资总和:" + sum2.get()); //输出:员工薪资总和:27075.0
    }
}
class Person {
    private String name;
    private String gander;
    private Integer age;
    private String address;
    private Double salary;

    public Person(String name, String gander, Integer age, String address, Double salary) {
        this.name = name;
        this.gander = gander;
        this.age = age;
        this.address = address;
        this.salary = salary;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getGander() {
        return gander;
    }
    public void setGander(String gander) {
        this.gander = gander;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public Double getSalary() {
        return salary;
    }
    public void setSalary(Double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return name;
    }
}

 

流终止

什么是流终止:

  • 就像是一根水管一样,流获取对应水管的开头,流处理在水管的中间段,流终止在水管的结尾处。
  • 流终止一般的操作有:遍历、聚合、查找、匹配、规约。

一旦Stream调用了终结方法,流的操作就全部终结了,不能继续使用,只能创建新的Stream操作。

遍历forEach

  1. void forEach(Consumer<? super T> action);  顺序遍历
  2. void forEachOrdered(Consumer<? super T> action);  遍历,和forEach的区别是forEachOrdered在多线程parallelStream执行,其顺序也不会乱
import java.util.Arrays;
import java.util.List;

public class MainTest {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 1, 2);
        list.stream().forEach(e -> System.out.print(e+" ")); //输出:1 1 2 
    }
}

聚合min/max/count

  1. Optional<T> min(Comparator<? super T> comparator)  获取最小值,Comparator用于指定比较器
  2. Optional<T> max(Comparator<? super T> comparator)  获取最大值
  3. long count() 返回流中的数量
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;

public class MainTest {
    public static void main(String[] args)  {
        List<Integer> list = new ArrayList<>();
        list.add(100);list.add(222);list.add(35432);
        list.add(234);list.add(34);list.add(89);

        //传入比较器的原始写法
        Optional<Integer> max = list.stream().max(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1.compareTo(o2);
            }
        });
        System.out.println(max);

        //传入比较器的简化写法
        Optional<Integer> min01 = list.stream().min(Comparator.reverseOrder());
        //Optional<Integer> min01 = list.stream().min((Integer a, Integer b) -> b.compareTo(a)); //传入比较器的Lambda写法
        Optional<Integer> min02 = list.stream().min(Integer::compareTo); //自然排序、字典排序(Lambda写法)
        System.out.println(min01+" "+min02);

        long count = list.stream().count(); //返回数量
        System.out.println(count);
    }
}

查找匹配find/match

匹配:

  1. boolean anyMatch(Predicate<? super T> predicate)  任意一个匹配
  2. boolean allMatch(Predicate<? super T> predicate)  全部匹配
  3. boolean noneMatch(Predicate<? super T> predicate)  不匹配

搜索查找:

  1. 在集合中的搜索意味着根据一个条件查找元素或验证元素的存在性,这个条件也叫做谓词或断言。搜索元素可能有返回值,也可能没有,所以接口返回的是一个Optional;验证元素存在性时返回是的一个布尔值。
  2. Optional<T> findFirst();  查找第一个
  3. Optional<T> findAny();  任意一个
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class MainTest {
    public static void main(String[] args)  {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
        list.add("张三丰");

        Optional<String> op01 = list.stream().findFirst(); //获取第一个数
        Optional<String> op02 = list.parallelStream().findAny(); //获取任意一个数
        System.out.println(op01);
        System.out.println(op02);

        //常规迭代、判断
        //for (String string : list) {
        //    if (string.contains("a")) {
        //        System.out.println("YES");
        //    }
        //}

        boolean isExist = list.stream().anyMatch(element -> element.contains("赵敏")); //流判断
        System.out.println("是否包含:"+isExist);
        //输出:
        //Optional[张无忌]
        //Optional[张强]
        //是否包含:true

    }
}

规约reduce

归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。

操作API:

  1. Optional<T> reduce(BinaryOperator<T> accumulator)  两两合并
  2. T reduce(T identity, BinaryOperator<T> accumulator)  两两合并,带初始值的
  3. <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)  先转化元素类型再两两合并,带初始值的
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class MainTest {
    public static void main(String[] args)  {
        List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);

        Optional<Integer> sum = list.stream().reduce((x, y) -> x + y); // 求和方式1
        Optional<Integer> sum2 = list.stream().reduce(Integer::sum); // 求和方式2
        Integer sum3 = list.stream().reduce(0, Integer::sum);// 求和方式3

        Optional<Integer> product = list.stream().reduce((x, y) -> x * y);// 求乘积
        Optional<Integer> max = list.stream().reduce((x, y) -> x > y ? x : y);// 求最大值方式1
        Integer max2 = list.stream().reduce(1, Integer::max);// 求最大值写法2

        System.out.println("list求和:" + sum.get() + "," + sum2.get() + "," + sum3);
        System.out.println("list求积:" + product.get());
        System.out.println("list求和:" + max.get() + "," + max2);
    }
}

总结:

  • 流的基本概念
  • Optional
  • 获取流:空流、从集合与数组中获取流、生成流、序列流、基本数据类型流
  • 流处理:过滤、元素变换、去重、排序等
  • 流收集:收集到不同类型的容器中,收集时统计、分组、接合、规约
  • 流终止:遍历、聚合、查找、匹配、规约

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值