• 首页

  • 归档

  • 标签

  • 分类

  • 友链
M S B l o g
M S B l o g

ms

获取中...

06
01
java
总结
教程

java8:Stream

发表于 2021-06-01 • java 总结 java8 • 被 1,433 人看爆

简介:

Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

和以前的Collection操作不同, Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

stram和parallelStream(顺序流和并行流)

stream和parallelStream的简单区分: stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。例如筛选集合中的奇数,两者的处理不同之处:
image.png

如果流中的数据量足够大,并行流可以加快处速度。

除了直接创建并行流,还可以通过parallel()把顺序流转换成并行流:

Optional<Integer> findFirst = list.stream().parallel().filter(x->x>6).findFirst();

使用:

Stream的创建

1,使用Collection下的 stream() 和 parallelStream() 方法

List<String> list = new ArrayList<String>();
Stream<String> stream = list.stream();//获取一个顺序流
Stream<String> parallelStream = list.parallelStream();//获取一个并行流

2,使用Array中的stream()方法,将数组转成流

Integer[] nums = new Integer[10];
Stream<Integer> stream = Arrays.stream(nums);

3,使用Stream中的静态方法:of(),iterate(),generate()

Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
 
Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 2).limit(6);
stream2.forEach(System.out::println); // 0 2 4 6 8 10
 
Stream<Double> stream3 = Stream.generate(Math::random).limit(2);
stream3.forEach(System.out::println);

4,使用BufferedReader.lines()方法,将每行内容转成流

BufferedReader reader = new BufferedReader(new FileReader("F:\\test_stream.txt"));
Stream<String> lineStream = reader.lines();
lineStream.forEach(System.out::println);

5,使用Pattern.spliAsStream()方法,将字符串分隔成流

Pattern pattern = Pattern.compile(",");
Stream<String> stringStream = pattern.splitAsStream("a,b,c,d");
stringStream.forEach(System.out::println);

流的操作

Optional类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

1,遍历/匹配(foreach/find/match)

Stream也是支持类似集合的遍历和匹配元素的,只是Stream中的元素是以Optional类型存在

List<Integer> list = Arrays.asList(3,6,13,5,8,10,7,2,1);

//遍历输出符合条件的元素
list.stream().filter(x -> x > 3).forEach(System.out::println);
//匹配第一个
Optional<Integer> findFirst = list.stream().filter(x -> x > 3).findFirst();
//匹配任意(适用于并行流)
Optional<Integer> findAny = list.parallelStream().filter(x -> x > 3).findAny();
//是否包含符合特定条件的元素
boolean anyMatch = list.stream().anyMatch(x -> x > 3);

System.out.println("匹配第一个值:" + findFirst.get());
System.out.println("匹配任意一个值:" + findAny.get());
System.out.println("是否存在大于6的值:" + anyMatch);

结果为:
image.png

2,筛选(filter)

filter 方法用于通过设置的条件过滤出元素

List<Integer> list = Arrays.asList(3,6,13,5,8,10,7,2,1);

//获取比5大的数
list.stream().filter(x -> x > 5).forEach(System.out::println);

结果为:
image.png

3,映射(map/flatMap)

映射,可以将一个流的元素按照一定的映射规则映射到另一个流中。分为map和flatMap:

  • map: 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成新的元素
  • flatMap: 接收一个函数作为参数,将流中的每一值都换成另一个流,然后把所有流连接成一个流
List<String> list = Arrays.asList("a,b,c","1,2,3");

//将每个元素转成一个新的且不带逗号的元素
Stream<String> s1 = list.stream().map(s -> s.replace(",",""));
s1.forEach(System.out::println);

Stream<String> s3 = list.stream().flatMap(s -> {
	//将每个元素转换成一个stream
	String[] split = s.split(",");
	Stream<String> s2 = Arrays.stream(split);
	return s2;
});
System.out.println("--------------------");
s3.forEach(System.out::println);

结果为
image.png

4,聚合(max/min/count)

  • count: 返回流中元素的总个数
  • min: 返回流中元素的最小值
  • max: 返回流中元素的最大值
public class Test {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

        long count = list.stream().count();
        Integer max = list.stream().max(Integer::compareTo).get();
        Integer min = list.stream().min(Integer::compareTo).get();

        System.out.println("元素的总个数:"+count);
        System.out.println("元素中最大值:"+max);
        System.out.println("元素中最小值:"+min);
    }
}

结果为
image.png

5,归约(reduce)

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

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

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

        //求乘积
        Optional<Integer> product = list.stream().reduce((x,y) -> x * y);

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

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

结果为:
image.png

6,排序(sorted)

  • sorted():自然排序,流中元素需实现Comparable接口
  • sorted(Comparator com):定制排序,自定义Comparator排序器

student类

public class Student {
    private String name;
    private Integer age;

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("aa", "ff", "dd");

        //String类自身实现Comparable接口
        list.stream().sorted().forEach(System.out::println);

        Student s1 = new Student("aa", 10);
        Student s2 = new Student("bb", 20);
        Student s3 = new Student("aa", 30);
        Student s4 = new Student("dd", 40);
        List<Student> sList = Arrays.asList(s1,s2,s3,s4);

        //自定义排序:先按名字升序,名字相同则按年龄升序
        sList.stream().sorted(
                (o1, o2) -> {
                    if(o1.getName().equals(o2.getName())){
                        return o1.getAge() - o2.getAge();
                    }else{
                        return o1.getName().compareTo(o2.getName());
                    }
                }
        ).forEach(System.out::println);
    }
}

结果为:
image.png

7,收集(collect)

collect主要依赖java.util.stream.Collectors类内置的静态方法。

7.1 归集(toList/toSet/toMap)
因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。toList、toSet和toMap比较常用,另外还有toCollection、toConcurrentMap等复杂一些的用法

public class Test {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 6, 3, 4, 6, 7, 9, 6, 20);
        List<Integer> listNew = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
        Set<Integer> set = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toSet());

        System.out.println("toList:" + listNew);
        System.out.println("toSet:" + set);
    }
}

结果为:
image.png

7.2统计(count/averaging)
Collectors提供了一系列用于数据统计的静态方法:

  • 计数:count
  • 平均值:averagingInt、averagingLong、averagingDouble
  • 最值:maxBy、minBy
  • 求和:summingInt、summingLong、summingDouble
  • 统计以上所有:summarizingInt、summarizingLong、summarizingDouble

Person类

public class Person {
    private String name;
    private double salary;

    public Person(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                '}';
    }
}
public class Test {
    public static void main(String[] args) {
        List<Person> pList = new ArrayList<>();
        pList.add(new Person("Tom",7000));
        pList.add(new Person("Jack",9000));
        pList.add(new Person("Jerry",6500));

        //求总和
        long count = pList.stream().collect(Collectors.counting());
        //求平均工资
        Double average = pList.stream().collect(Collectors.averagingDouble(Person::getSalary));
        //求最高工资
        Optional<Double> max = pList.stream().map(Person::getSalary).collect(Collectors.maxBy(Double::compare));
        //求工资之和
        Double sum = pList.stream().collect(Collectors.summingDouble(Person::getSalary));
        //一次性统计所有信
        DoubleSummaryStatistics collect = pList.stream().collect(Collectors.summarizingDouble(Person::getSalary));

        System.out.println("员工总数:" + count);
        System.out.println("员工平均工资:" + average);
        System.out.println("员工最高工资:"+ max);
        System.out.println("员工工资总和:" + sum);
        System.out.println("员工工资所有统计:" + collect);
    }
}

结果为:
image.png

7.3分组(partitioningBy/groupingBy)

  • 分区:将stream按条件分为两个Map,比如员工按薪资是否高于8000分为两部分。
  • 分组:将集合分为多个Map,比如员工按性别分组。有单级分组和多级分组。
public class Test {
    public static void main(String[] args) {
        List<Person> pList = new ArrayList<>();
        pList.add(new Person("Tom",7000));
        pList.add(new Person("Jack",9000));
        pList.add(new Person("Jerry",6500));
        pList.add(new Person("Anni",8000));
        pList.add(new Person("Owen",10000));
        pList.add(new Person("Alisa",7900));

        //将员工按薪资是否高于8000分组
        Map<Boolean, List<Person>> part = pList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));

        System.out.println("员工按薪资是否大于8000分组情况:" + part);
    }
}

结果为:

员工按薪资是否大于8000分组情况:{false=[Person{name='Tom', salary=7000.0}, Person{name='Jerry', salary=6500.0}, Person{name='Anni', salary=8000.0}, Person{name='Alisa', salary=7900.0}], true=[Person{name='Jack', salary=9000.0}, Person{name='Owen', salary=10000.0}]}

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

public class Test {
    public static void main(String[] args) {
        List<Person> pList = new ArrayList<>();
        pList.add(new Person("Tom",7000));
        pList.add(new Person("Jack",9000));
        pList.add(new Person("Jerry",6500));
        pList.add(new Person("Anni",8000));
        pList.add(new Person("Owen",10000));
        pList.add(new Person("Alisa",7900));

        String names = pList.stream().map(p -> p.getName()).collect(Collectors.joining(","));
        System.out.println("所有员工的姓名:" + names);
        List<String> list = Arrays.asList("A", "B", "C");
        String string = list.stream().collect(Collectors.joining("-"));
        System.out.println("拼接后的字符串:" + string);
    }
}

结果为:
image.png

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

public class Test {
    public static void main(String[] args) {
        List<Person> pList = new ArrayList<>();
        pList.add(new Person("Tom",7000));
        pList.add(new Person("Jack",9000));
        pList.add(new Person("Jerry",6500));
        pList.add(new Person("Anni",8000));
        pList.add(new Person("Owen",10000));
        pList.add(new Person("Alisa",7900));

        //员工工资总和
        Double sum = pList.stream().map(Person::getSalary).collect(Collectors.reducing(Double::sum)).get();
        System.out.println("员工工资总和:"+sum);
    }
}

结果为:
image.png

8.提取/组合

流也可以进行合并、去重、限制、跳过等操作。

public class Test {
    public static void main(String[] args) {
        String[] arr1 = { "a", "b", "c", "d" };
        String[] arr2 = { "d", "e", "f", "g" };

        Stream<String> s1 = Stream.of(arr1);
        Stream<String> s2 = Stream.of(arr2);
        //concat:合并两个流 distinct:去重
        List<String> newList = Stream.concat(s1, s2).distinct().collect(Collectors.toList());
        //limit:限制从流中获得前n个数据
        List<Integer> collect = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
        //skip:跳过前n个数据
        List<Integer> collect2 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());

        System.out.println("流合并:" + newList);
        System.out.println("limit:" + collect);
        System.out.println("skip:" + collect2);
    }
}

结果为:
image.png

分享到:
Certbot查看证书过期时间,手动续期以及自动续期
windows查看指定端口占用情况
  • 文章目录
  • 站点概览
ms

MSms

⚓️HelloWorld⚓️

QQ Email RSS
看爆 Top5
  • MyBatis-Plus分页查询 5,936次看爆
  • @Autowired与@Resource的区别 4,754次看爆
  • feign远程调用及异步调用丢失请求头问题 4,526次看爆
  • spring cloud中OpenFeign整合Sentinel启动报错 4,423次看爆
  • Certbot查看证书过期时间,手动续期以及自动续期 3,302次看爆

Copyright © 2025 ms · 湘ICP备20015239号

Proudly published with Halo · Theme by fyang · 站点地图