Java8Stream流Api学习
注意:
- 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
下一篇: 微信公众号怎么查看视频地址?