欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java8Stream流Api学习

程序员文章站 2022-12-20 21:17:57
欢迎访问我的博客,专注于Java技术分享,共同进步。❤注意:parallelStream 并行流,线程不安全。而且执行效率要看你的电脑cup总数,默认线程数和cpu总数相当。stream 单管,效率不及parallelStream常见函数:filter()、map()、flatMap()、mapToInt()、sorted()、reduce()、limit()、skip()、groupingBy()、allMatch()、findFirst()、sum()、max()、min()、avera...

欢迎访问我的博客,专注于Java技术分享,共同进步。❤

注意:

  • parallelStream 并行流,线程不安全。而且执行效率要看你的电脑cup总数,默认线程数和cpu总数相当。
  • stream 单管,效率不及parallelStream

常见函数:

  • filter()、map()、flatMap()、mapToInt()、sorted()、reduce()、limit()、skip()、groupingBy()、allMatch()、findFirst()、sum()、max()、min()、average()、distinct()、count(返回流中元素个数)等…

首先我们先创建基础支撑数据。

public class Main {

    public static void main(String[] args) {
        Employee e1 = new Employee(3, 43, "M", null);
        Employee e2 = new Employee(2, 23, "F", null);
        Employee e3 = new Employee(4, 53, "M", null);
        Employee e4 = new Employee(8, 33, "M", null);
        Employee e5 = new Employee(9, 23, "F", null);
        List<Employee> list = Arrays.asList(e1, e2, e3, e4, e5);
    }
}

/**
 * Employee类
 **/
public class Employee {
    private Integer id;
    private Integer age;
    private String gender;
    List<Employee> children;

    /**
     * filter的谓词逻辑:
     * 年龄大于30
     */
    public static Predicate<Employee> ageGreaterThan30 = x -> x.getAge() > 30;
    public static Predicate<Employee> genderM = x -> x.getGender().equals("M");


    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", children=" + children +
                '}';
    }

    public Employee(Integer id, Integer age, String gender, List<Employee> children) {
        this.id = id;
        this.age = age;
        this.gender = gender;
        this.children = children;
    }

    public List<Employee> getChildren() {
        return children;
    }

    public void setChildren(List<Employee> children) {
        this.children = children;
    }

    public void setId(Integer id) {
        this.id = id;
    }

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

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Integer getId() {
        return id;
    }

    public Integer getAge() {
        return age;
    }

    public String getGender() {
        return gender;
    }

}

数组创建流

构造流:Stream.of()、Arrays.stream()、Arrays.asList()

1、Stream.of()是通用的,而Arrays.stream()不是

2、Arrays.stream():适用于int [],long []和double []类型的原始数组,并分别返回IntStream,LongStream和DoubleStream

3、Stream.of():返回类型T的通用Stream(Stream)

String[] arr = {"Geek", "for", "Geeks"};
// 可以用与遍历数组
Stream<String> array = Arrays.stream(arr);
Stream<String> stream = Stream.of(arr);
// %s:字符串的占位符
array.forEach(str -> System.out.printf("%s \t", str));
stream.forEach(str -> System.out.printf("%s ", str));
// 将数组转换成集合
List<String> collect = Arrays.asList(arr).parallelStream().collect(Collectors.toList());
System.err.println(new ArrayList<>(Arrays.asList(arr)));
// 快于new ArrayList<>(Arrays.asList(arr))
System.out.println("多线程:" + collect);

Java 装箱流

1、数值流

三个原始类型流:

  • IntStream

  • DoubleStream

  • LongStream

以IntStream为例:

  • IntStream.of()
  • IntStream.range()前闭后开、不包含结束位,startInclusive >= endExclusive,range(1, 100) :[1, 100)
  • IntStream.rangeClosed()闭区间、包含结束位,startInclusive > endExclusive,rangeClosed(1, 100) :[1, 100]
// 1,2,3
IntStream.of(new int[]{1, 2, 3}).forEach(e -> {
    System.out.println("IntStream.of:" + e);
});
// 1,2: 不包含结束位,startInclusive >= endExclusive
IntStream.range(1, 3).forEach(e -> {
    System.out.println("IntStream.range:" + e);
});
// 包含结束位,startInclusive > endExclusive
// 1,2,3:IntStream rangeClosed (int startInclusive,int endInclusive)
// IntStream:原始int值元素的序列。startInclusive:包含的初始值。endInclusive:包含的上限。
IntStream.rangeClosed(1, 3).forEach(e -> {
    System.out.println("IntStream.rangeClosed:" + e);
});
//计算1~100之间的数字中偶数的个数
long count = IntStream.rangeClosed(1, 100).filter(n -> n % 2 == 0).count();
System.out.println(count);

//将stream转换为IntStream
int totalScore = list.stream().mapToInt(Employee::getAge).sum();
System.out.println(totalScore);

//计算平均年龄
OptionalDouble avgAge = list.stream().mapToInt(Employee::getAge).average();
System.out.println(avgAge.getAsDouble());

2、Collectors类处理

将字符串流转换为列表

List<String> strings = Stream.of("this", "is", "a", "list", "of", "strings")
    .collect(Collectors.toList());

然而,同样的过程并不适合处理 基本类型流

解决方法:

一:利用 boxed 方法,将 IntStream 转换为 Stream

List<Integer> ints = IntStream.of(3, 1, 4, 1, 5, 9)
    .boxed()
    .collect(Collectors.toList());

二:利用 mapToObj 方法,将基本类型流中的每个元素转换为包装类的一个实例

List<Integer> ints = IntStream.of(3, 1, 4, 1, 5, 9)
    .mapToObj(Integer::valueOf)
    .collect(Collectors.toList())

三:采用 collect 方法的三参数形式

Supplier: 是 ArrayList<Integer> 的构造函数。
accumulator: 累加器(accumulator)为 add 方法,表示如何为列表添加单个元素。
combiner: 仅在并行操作中使用的组合器(combiner)是 addAll 方法,它能将两个列表合二为一
-----------------------------------------
<R> R collect(Supplier<R> supplier,
              ObjIntConsumer<R> accumulator,
              BiConsumer<R,R> combiner)

List<Integer> ints = IntStream.of(3, 1, 4, 1, 5, 9)
    .collect(ArrayList<Integer>::new, ArrayList::add, ArrayList::addAll);

延伸:将 IntStream 转换为 intArray

int[] intArray = IntStream.of(3, 1, 4, 1, 5, 9).toArray();

流转换成其他数据结构

// 1. Array
String[] strArray1 = stream.toArray(String[]::new);
// 2. Collection
List<String> list1 = stream.collect(Collectors.toList());
List<String> list2 = stream.collect(Collectors.toCollection(ArrayList::new));
Set set1 = stream.collect(Collectors.toSet());
Stack stack1 = stream.collect(Collectors.toCollection(Stack::new));
// 3. String
String str = stream.collect(Collectors.joining()).toString();

过滤、排序:filter、sorted

List<Employee> demo = list.parallelStream()
        // 枚举类型:x -> x.getAge() > 30
        .filter(Employee.ageGreaterThan30)
        // Employee::getId => e -> e.getId()
        // comparing(Employee::getId) 正序
        // comparing(Employee::getId).reversed:倒序
        .sorted(comparing(Employee::getId))
        // 将stream流转换成list集合
        .collect(Collectors.toList());

排序、map映射:filter、map

List<Integer> demo = list.stream()
    .filter(Employee.ageGreaterThan30)
    .map(e -> {
        // 平方数
        return e.getId() * e.getId();
    })
    .collect(Collectors.toList());

求取操作后的id的和:mapToInt、sum

int sum = list.stream()
    // Integer -> int
    .mapToInt(Employee::getId)
    .sum();

一对多的扁平化处理成一行数据:flatMap

flatMap 把 input Stream 中的 层级结构扁平化,就是将最底层元素抽出来放到一起,最终 output 的。新 Stream 里面已经没有 List 了,都是直接的数字,然后通过 collect(Collectors.toList()转成list集合。

Stream<List<Integer>> inputStream = Stream.of(
    Collections.singletonList(1),
    Arrays.asList(2, 3),
    Arrays.asList(4, 5, 6)
);

List<Integer> outputStream = inputStream.
        flatMap(Collection::stream).collect(Collectors.toList());
// [1, 2, 3, 4, 5, 6]

留下偶数

Integer[] sixNums = {1, 2, 3, 4, 5, 6};
String[] evens =
        Stream.of(sixNums).filter(n -> n % 2 == 0)
                // Number,String的父级都是Object
                .map(Object::toString).
                toArray(String[]::new);
// 不然就是hashcode地址
System.out.println(Arrays.toString(evens));

findFirst()

findFirst():总是返回 Stream 的第一个元素,或者空
findAny():找到其中一个元素 (使用 stream() 时找到的是第一个元素;使用 parallelStream() 并行时找到的是其中一个元素)。

使用 Optional 代码的可读性更好,而且它提供的是 编译时检查,能极大的降低 NPE 这种 Runtime Exception 对程序的影响,或者迫使程序员更早的在编码阶段处理空值问题,而不是留到运行时再发现和调试。

Optional<Employee> optional = list.parallelStream().findFirst();
// optional.get(): 获取到optional中的对象
Optional.of(optional.get()).ifPresent(System.out::println);

reduce()

这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。例如 Stream 的 sum 就相当于

Integer sum = integers.reduce(0, (a, b) -> a+b); 或

Integer sum = integers.reduce(0, Integer::sum);

没有起始值的情况,这时会把 Stream 的前面两个元素组合起来,返回的是 Optional, 通过 .get()获取数据类型

// 字符串连接,concat = "ABCD"
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
// 求最小值,minValue = -3.0
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
// 求和,sumValue = 10, 有起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
// 求和,sumValue = 10, 无起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
// 过滤,字符串连接,concat = "ace"
concat = Stream.of("a", "B", "c", "D", "e", "F")
    // compareTo:比参数对象大,则返回+1,如果相等,则返回0,如果比参数小,则返回-1
    .filter(x -> x.compareTo("Z") > 0)
    .reduce("", String::concat);

limit()、skip()

  • limit 返回 Stream 的前面 n 个元素
  • skip 则是扔掉前 n 个元素(它是由一个叫 subStream 的方法改名而来)。
List<Employee> employees = new ArrayList();
for (int i = 1; i <= 10000; i++) {
    // [emp4, emp5, emp6, emp7, emp8, emp9, emp10]
    Employee employee = new Employee((int) System.currentTimeMillis(), i, "emp" + i, null);
    employees.add(employee);
}
// 一万条数据中取10条,扔掉前3条数据,返回集合。
List<String> personList2 = employees.stream().
    map(Employee::getGender).limit(10).skip(3)
    .collect(Collectors.toList());

找出最长一行的长度:max

BufferedReader br = new BufferedReader(new FileReader("c:\\SUService.log"));
int longest = br.lines().
 mapToInt(String::length).
 max().
 getAsInt();
br.close();
System.out.println(longest);

match()

  • allMatch:Stream 中全部元素符合传入的 predicate,返回 true。不需要遍历所有的,只要一个元素不满足条件,就 skip 剩下的所有元素,返回 false

  • anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true

  • noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true

// true
boolean isAllAdult = list.stream().
    allMatch(p -> p.getAge() > 18);
System.out.println("All are adult? " + isAllAdult);

// false
boolean isThereAnyChild = list.stream().
    anyMatch(p -> p.getAge() < 12);
System.out.println("Any child? " + isThereAnyChild);

// false
boolean noneMatch = list.stream()
    .noneMatch(p -> p.getAge() > 0);
System.out.println("Any child? " + noneMatch);

生成随机整数:random

Random random = new Random();
Supplier<Integer> num = random::nextInt;
Stream.generate(num).limit(10).forEach(System.out::println);
//Another way
System.out.println((int) (Math.random() * 5));

// // long streamSize生成的元素个数, int randomNumberOrigin 起始位,int randomNumberBound 结束位
// 五个位于 1(包括)和 10(不包括)之间的整型随机数
random.ints(5, 1, 10)
        .sorted()
        .forEach(System.out::print);

// 例如:[11, 16, 14, 19, 10] int、doubles、longs也可
List<Integer> listOfInts = random.ints(5, 10, 20)
        .collect(LinkedList::new, LinkedList::add, LinkedList::addAll);

Random random = new Random();
// 
List<Integer> listOfInts = random.ints(5, 10, 20)
        .collect(LinkedList::new, LinkedList::add, LinkedList::addAll);
// 将Integer转为int型,去前三条求和
int sum2 = listOfInts.stream().mapToInt(Integer::intValue).limit(3).sum();

函数生成流

iterate 跟 reduce 操作很像,接受一个种子值,和一个 UnaryOperator(例如 f)。然后种子值成为 Stream 的第一个元素,f(seed) 为第二个,f(f(seed)) 第三个,以此类推。

/* 生成一个等差数列 */
// 生成流,第一个为0,后面的依次加1
Stream.iterate(0, n -> n + 1)

// 0 3 6 9 12 15 18 21 24 27
Stream.iterate(0, n -> n + 3).limit(10).forEach(x -> System.out.print(x + " "));

// 生成流,为 0 到 1 的随机双精度数
Stream.generate(Math :: random)

// 生成流,元素全为 1
Stream.generate(() -> 1)

按条件归组

  • groupingBy:根据什么分组

  • partitioningBy:其实是一种 特殊的 groupingBy,它依照条件测试的是否两种结果来构造返回的数据结构,get( true) 和 get(false) 能即为全部的元素对象

// 按照年龄分组
// {33=[Employee{id=8, age=33, gender='M', children=null}], 53=[Employee{id=4, age=53, gender='M', children=null}], 23=[Employee{id=2, age=23, gender='F', children=null}, Employee{id=9, age=23, gender='F', children=null}], 43=[Employee{id=3, age=43, gender='M', children=null}]}
Map<Integer, List<Employee>> groupingBy = list.stream()
        .collect(Collectors.groupingBy(Employee::getAge));
System.out.println("groupingBy:" + groupingBy);

// 通过key值获取23岁的集合列表
// [Employee{id=2, age=23, gender='F', children=null}, Employee{id=9, age=23, gender='F', children=null}]
List<Employee> employeesOf23 = groupingBy.get(23);
System.out.println("employeesOf23:" + employeesOf23);

// 按照是否大于30岁分组
// {false=[Employee{id=2, age=23, gender='F', children=null}, Employee{id=9, age=23, gender='F', children=null}], 
//  true=[Employee{id=3, age=43, gender='M', children=null}, Employee{id=4, age=53, gender='M', children=null}, Employee{id=8, age=33, gender='M', children=null}]}
Map<Boolean, List<Employee>> collect = list.stream()
        .collect(Collectors.partitioningBy(p -> p.getAge() > 30));
System.out.println("partitioningBy:" + collect);

参考链接:
IBM Developer:java8streamapi学习
IBM Developer:stream学习

本文地址:https://blog.csdn.net/qq_43476465/article/details/109558748

相关标签: Java Java8新特性