【Java】流(Stream)快速入门

Java 注解说的明明白白,清清楚楚,记得点赞关注 注解是开发框架的必备技术,也是Java中相对高级的技术 Java 注解用于为 Java 代码提供元数据,看完这句话也许你还是一脸懵逼,用人话说就是注解不直接影响你的代码执行,仅提供信息。接下我将从注解的定义、元注解、注解属性、自定义注解、注解解析JDK 提供的注解这几个方面再次了解注解(.. 阅读详情

本文是面向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
  • 获取流:空流、从集合与数组中获取流、生成流、序列流、基本数据类型流
  • 流处理:过滤、元素变换、去重、排序等
  • 流收集:收集到不同类型的容器中,收集时统计、分组、接合、规约
  • 流终止:遍历、聚合、查找、匹配、规约

逆向实战:喜马拉雅新版xm-sign算法解析与本地化生成方案 本文深入解析了喜马拉雅新版xm-sign算法的逆向过程与本地化生成方案。通过详细拆解browserid和sessionid的生成机制,提供了基于Node.js的核心代码实现,帮助开发者在不依赖官方环境的情况下生成有效签名。文章还涵盖了参数获取技巧、常见问题排查及性能优化建议,为爬虫开发者提供了实用的技术参考。 阅读详情

相关推荐

保姆级教程:在摩尔线程MTT S80上快速部署Ollama运行DeepSeek R1模型(附性能测试)

本文提供了一份详细的保姆级教程,指导用户在摩尔线程MTT S80显卡上部署Ollama并运行DeepSeek R1模型。内容涵盖从驱动安装、Docker环境配置到模型拉取、性能测试的全流程,并分享了实测数据与优化技巧,旨在帮助开发者快速在国产GPU平台上实现高效的大语言模型推理。

weixin_29091837的博客 521

Java学习笔记#07 类型擦除和桥接方法

类型擦除 Java编译器通过类型擦除(type erasure)对泛型提供支持: 1. 将所有类型参数替换为他们的界(bound),若类型参数是不受限的(unbounded),则替换为Object,因此产生的字节码中只含有普通类、接口、方法 例如有如下的Node类: public class Node<T> { public T data; public Node(T data) { this.data = data; } public void setData(T data) { Sy

嘻嘻呵呵的博客 542

【老生谈算法】可视化通道流中瞬时涡量场的算法实现与MATLAB源码分析——瞬时涡量场

本文旨在探讨可视化通道流中瞬时涡量场的方法,并通过MATLAB编程实现这一过程。涡量场作为描述流体旋涡强度和方向的重要物理量,对于理解复杂流体运动具有重要意义。本文首先介绍了涡量场的基本概念,随后详细阐述了从测量数据收集到涡量场可视化的完整流程,并给出了相应的MATLAB源码。通过实际运行该源码,我们展示了涡量场的可视化结果,并讨论了其在流动结构识别和动力学行为理解中的应用。关键词: 涡量场;流体运动;MATLAB;可视化;PIV一、引言。

m0_53407570的博客 1124

深入理解Java Lambda表达式(全网之最)

本文将结合书本和网络教程,阐述自己对于Lambda表达式的理解,如有偏差,欢迎指正... 目录 什么是Lambda表达式? 为什么要引入Lambda表达式? Lambda表达式的语法: Lambda表达式的分类 什么是函数式接口? 函数式接口有什么作用? 自定义一个函数式接口: 四大函数式接口: 方法引用: 技术的进步,循序渐进;慢下来,扎扎实实;用过...

isea_you's blog 4994

python123题目——判断火车票座位B(异常)

类型:流程控制。

m0_62488776的博客 3569

【mip-NeRF】使用一组二维图像渲染3D场景视频(mip-NeRF论文复现)

本文对mip-NeRF模型的pytorch实现进行了复现

Ryan2k的博客 1841

java.lang.IllegalArgumentException: invalid comparison: java.util.ArrayList---无效比较

Causejava.lang.IllegalArgumentExceptioninvalidcomparisonjava.util.ArrayList。

qq_42926722的博客 3368

Visual Studio 命令提示符工具区别与使用场景

Visual Studio会提供一系列命令提示符工具,这些工具应用在哪些场景呢?本文进行了相关介绍。

cicada15的博客 3493

Cannot construct instance of `java.util.ArrayList` 报错 String value (‘‘);

因为fastjson 在json解析的的时候 无法将''转换成jsonarray。

dfengleicsdn的博客 6631

十三种通信接口芯片——《器件手册--通信接口芯片》

通信接口芯片用于实现差分信号的传输,具有抗干扰能力强、传输距离远等优点。RS485支持多点通信,可连接多个收发器,而RS422支持单向多点通信。广泛应用于工业自动化、过程控制、仪器仪表、楼宇自动化(如暖通空调、保安系统)、电机控制等领域。ST3485EBDRTP8485ESR用于实现串行通信,支持点对点通信,传输距离相对较短,数据速率较低。常用于计算机与外设(如打印机、调制解调器)之间的通信。如芯力特的SIT3232,可作为ST3232的替代品。

XU157303764的博客 4271

爬虫进阶必备:深入理解requests.Session(),提升爬虫效率与稳定性

前两天,有个朋友跟我吐槽,说自己写了个爬虫爬取某个电商网站的商品信息,刚开始跑得好好的,爬了大概几百页之后,突然就开始报错了。HTTP状态码一会儿429,一会儿403,最离谱的是有些页面明明登录了却显示未登录状态。他排查了半天也没找到原因,最后把代码发给我看。 我扫了一眼代码,发现一个典型问题:他每次请求都新建一个requests.Session(),甚至有些请求直接用了requests.get()。这就好比每次进超市都要重新注册会员,每次都重新跟服务器“握手”,不翻车才怪。 其实,这就是我们今天要聊的

2201_76125261的博客 1228

Java8 Stream流快速入门

一篇带你搞定Stream常见用法。

weixin_46739493的博客 5262

真题库系列之 CCF GESP 2025年12月认证 Python 1级真题含正确答案与解析

学生通过计算机基础知识的学习,了解计算机的构成与操作,以及计算机的发 展历程。通过编程基础知识以及语句的掌握,可以独立完成简单功能的顺序结构、 分支结构、循环结构的程序。

iCloudEnd的博客 316

java8 stream 教程_java8 Stream 流快速入门

Stream 流简介Java 8 中,得益于 lambda 带来的函数式编程,引入了一个全新的 Stream流 概念,用于解决集合已有的弊端。好处我们先来看一个例子:筛选出 names 中以 '张' 开头的字符串得到子集1,再筛选出 子集1 中长度为 3 的字符串,然后遍历输出。// 传统集合的方式public static void main(String[] args) {List name...

weixin_39692847的博客 318

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

java 8 stream入门_java8 Stream 流快速入门

Stream 流简介Java 8 中,得益于 lambda 带来的函数式编程,引入了一个全新的 Stream流 概念,用于解决集合已有的弊端。好处我们先来看一个例子:筛选出 names 中以 '张' 开头的字符串得到子集1,再筛选出 子集1 中长度为 3 的字符串,然后遍历输出。// 传统集合的方式public static void main(String[] args){List names...

weixin_39841825的博客 164

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

java Stream快速入门02-Stream流的获取

Stream流的获取 1.单列集合 如何获取 可以使用Collection接口中的默认方法stream()生成流 default Stream<E>stream() //使用集合可以直接调用stream方法生成流 Stream<String> stream = list.stream(); 我们可以创建集合,给集合添加元素。通过集合直接使用stream()方法。创建集合很简单,List、ArrayList等等,我们创建ArrayList,在idea中右键 diagrams,会看到

Sirow的博客 772

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Java基础快速入门: Stream流核心方法详解

本文介绍了Java中的Stream流操作,对比了传统集合操作与Stream流方式的差异。主要内容包括: Stream流的核心思想:通过流水线式处理(获取流→中间操作→终结操作)简化集合处理 四种获取Stream流的方式:单列集合.stream()、双列集合的keySet/entrySet、Arrays.stream()、Stream.of() 常用中间操作方法:filter过滤、limit截取、skip跳过、concat合并、distinct去重 终结方法:forEach遍历、count计数 Stream的

Wang的专栏 218

「 Java 8 新特性 」函数式编程 Stream流 快速入门

Stream是Java 8 的新特性,熟练使用Stream流可以使代码更加简洁精炼。

frozenpenguin的博客 1181

Java8新特性lambda表达式+stream流!!!快速入门篇~

lambda表达式 lambada表达式的使用条件:函数式接口只能有一个抽象方法,但有两个特例,有default方法和Object类对应的方法除外 lambada表达式可以理解为:匿名内部类的升级版 在这里插入代码片 stream流 stream流是Java8推出的新特性, 实际上它和java.io 包下的 InputStream 和 OutputStream真的没有关系,唯一的作用就是允许你以声明性方式处理数据集合,简单的理解就是用类似SQL 语句从数据库查询数据,以这种直观的方式处理数据集合。 str

weixin_42806958的博客 301

【学习笔记】Java函数式编程02——Stream流

Stream流是JDK8提供的新特性。使用的是函数式编程的模式。它可以被用来对集合或数组进行链状流式的操作。和之前的IO流进行区分,IO流是针对文件和数据操作可以更方便的对集合和数据进行操作。

Xcong_Zhu的博客 1122

Stream流

Stream是Java8API的新成员,它允许以声明性方式处理数据集合。代码简洁函数式编程写出的代码简洁且意图明确,使用stream接口让你从此告别for循环。多核友好:Java函数式编程使得编写并行程序从未如此简单,你需要的全部就是调用一下方法。

weixin_46073538的博客 883

Java8 Stream API全面解析——高效流式编程的秘诀

Java8 Stream API全面解析——高效流式编程的秘诀

赵士杰——成功并非一蹴而就,而是在持之以恒的努力中逐渐实现。 4791

2、函数式编程 --- Stream流

函数式编程 --- Stream流

Java 领域技术分享 169

Stream流基本使用

Stream流基本使用

兮动人 1040

IO流和File类

List list2 = names.stream().filter( s-> s.startsWith("张")).fillter(a->a.lenth()==3).collect(Collectors.toList());List s1 = set.stream().filter(s -> s.startsWith("张")).limit(2).collect(Collectors.toList());最终把这个十进制作为返回值,这个十进制的数据也表示在字符集上的数字。

我の名字 248

【函数式编程】Java函数式编程学习

Java8的Steam使用的是函数式编程模式,如他名字一样,它可以用来对集合或数字进行链状流式操作,可以更加方便的对集合或者数组进行操作;

行无所至,学无所尽。 351
上一篇: 【Java】Lambda入门
下一篇: 【Java】反射入门
ALGO阿狗
博客等级 码龄9年 64粉丝 149原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值