Java8新特性——Stream API
一、概述
1.1 理解
Stream流是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用StreamAPI对集合数据进行操作,就类似于使用SQL执行的数据库查询。也可以使用StreamAPI来并行执行操作。简而言之,StreamAPI提供了一种高效且易于使用的处理数据的方式。
1.2 什么是流
- 定义:流(Stream)是一种数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。集合讲的是数据,而流讲的是计算。
- Stream特性:
- Stream自己不会存储元素。
- Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream。
- Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
二、Stream的使用
2.1 创建Stream流
public static void main(String[] args) {
// 1.通过Collection系列集合提供的stream()方法或parallelStream()方法来创建
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
// 2.通过Arrays的静态方法stream()创建数流
String[] strArray = new String[10];
Stream<String> stream2 = Arrays.stream(strArray);
// 3.通过Stream类的静态方法of()来创建
Stream<String> stream3 = Stream.of("tom","jack","merry");
// 4.通过Stream类的迭代器iterate()创建无限流
Stream<Integer> stream4 = Stream.iterate(0,x -> x + 2);
stream4.limit(5).forEach(System.out::println); // 测试打印5条无限流(0开始的偶数)
// 5.通过Stream类的generate()方法生成
Stream<Integer> stream5 = Stream.generate(()->(int)(Math.random()*100));
stream5.limit(5).forEach(System.out::println); // 测试生成5条100以内的随机整数
}
2.2 中间操作
多个中间操作可以通过链式编程连起来形成一条流水线,除非流水线上触发了终止操作,否则中间操作不会执行任何处理,当触发了终止操作时,会一次性全部进行处理,称为"惰性求值"。
2.2.1 筛选与切片
- filter():接收Lambda(Predicate),从流中排除某些元素;
- limit():截断流,使流的元素不超过给定的数量;
- skip(n):跳过前n个元素,返回跳过之后的元素流.若流中元素不足n个,则返回空流;
- distinct():筛选,通过流中元素的hashCode()和equals()去除重复的元素。
public class MiddleOperate {
//伪造数据库数据
static List<User> users = Arrays.asList(
new User(1001,"张三",18,3500.0),
new User(1002,"李四",28,7500.0),
new User(1003,"王五",38,12000.0),
new User(1004,"赵六",48,20000.0),
new User(1004,"赵六",48,20000.0),
new User(1004,"赵六",48,20000.0)
);
public static void main(String[] args) {
Stream<User> stream = users.stream(); //通过List集合的stream方法获取流
stream.filter(user -> {return user.getAge()>25;}) //结果显示6条数据中的后5条数据
.limit(4) //结果显示5条数据中的前4条数据
.skip(1) //结果显示4条数据中的后3条数据
.distinct() //去重(重写hashCode和equals方法):结果显示3条数据中的前2条数据
.forEach(System.out::println); //终止操作用来展示结果
// 根据集合中对象的某一个属性去重,并组成新的List集合。本例以用户对象的用户名和年龄属性去重。
users.stream()
.collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(f -> f.getUserName() + f.getAge()))), ArrayList::new))
.forEach(System.out::println); //结果显示6条数据中的前4条数据
}
}
2.2.2 映射
- map():接收Lambda(Function),将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到流中每个元素上,并将其映射成一个新的元素。
- flatMap():接收Lambda(Function)作为参数,将流中的每一个元素都转换成另一个流,然后把所有流连成一个流.
public static void main(String[] args) {
List<String> list = Arrays.asList("aaa","bbb","ccc","ddd");
list.stream().map(str->str.toUpperCase()) // 转换成其他形式,结果:AAA BBB CCC DDD
.forEach(System.out::println);
users.stream().map(User::getUserName) // 提取信息,结果:张三 李四 王五 赵六 赵六 赵六
.forEach(System.out::println);
list.stream().flatMap(MiddleOperate::filterCharacter) //结果:a a a b b b c c c d d d
.forEach(System.out::println);
}
public static Stream<Character> filterCharacter(String str){
List<Character> list = new ArrayList<>();
for (Character c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
2.2.3 排序
- sorted:自然排序.
- sorted(Comparator com):定制排序. Comparator方法:int compare(T o1, T o2);
public static void main(String[] args) {
List<String> list = Arrays.asList("cc","aa","dd","bb");
list.stream().sorted() // 自然排序,结果:aa bb cc dd
.forEach(System.out::println);
list.stream().sorted(Comparator.reverseOrder()) // 自然排序(降序),结果:dd cc bb aa
.forEach(System.out::println);
users.stream().sorted((u1,u2)->{ // 定制排序,按年龄排序,年龄一样的再按薪资排序
if (u1.getAge().equals(u2.getAge())){
return u1.getSalary().compareTo(u2.getSalary());
}else{
return u1.getAge().compareTo(u2.getAge());
}
}).forEach(System.out::println);
}
2.3 终止操作
2.3.1 查找与匹配(Predicate)
- allMatch():检查是否匹配所有元素,返回boolean
- anyMatch():检查是否至少匹配一个元素,返回boolean
- noneMatch():检查是否没有元素能匹配制定规则,返回boolean
- findFirst():返回第一个元素
- findAny():返回当前流中的任意一个元素
- count():返回流中元素的个数
- max():返回流中最大值
- min():返回流中最小值
- forEach():遍历,内部迭代
public static void main(String[] args) {
System.out.println(users.stream().allMatch(user -> user.getUserName().equals("赵六"))); // 结果:false
System.out.println(users.stream().anyMatch(user -> user.getUserName().equals("赵六"))); // 结果:true
System.out.println(users.stream().noneMatch(user -> user.getUserName().equals("赵六")));// 结果:false
System.out.println(users.stream().findFirst().get()); // 结果:第一条数据
System.out.println(users.stream().findAny().get()); // 结果:返回任意一个元素
System.out.println(users.stream().count()); // 结果:6
System.out.println(users.stream().max((u1,u2)->Double.compare(u1.getSalary(),u2.getSalary()))); // 结果:工资最高的数据
System.out.println(users.stream().map(User::getSalary).min(Double::compare).get()); //结果:3500.0
users.stream().forEach(System.out::println); // 结果: 全部6条数据
}
2.3.2 规约
- reduce(T startValue, BinaryOperator b):将流中元素反复结合起来,得到一个值,返回T
- reduce(BinaryOperator b):将流中元素反复结合起来,得到一个值,返回Optional
public void reduceStream(){
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
System.out.println(list.stream().reduce(0, Integer::sum)); // 计算前10位数和,结果:55
System.out.println(users.stream().map(User::getSalary).reduce(Double::sum).get()); // 计算工资总和,结果:83000.0
}
2.3.3 收集
- collect(Collector c):将流转换为其他形式(List,Map,Set等),参数为Collectors枚举类的值。
public void collectStream(){
System.out.println("=================终止操作-收集===================");
// 收集为List集合
List<User> collect = users.stream().filter(user -> user.getSalary() > 5000)
.collect(Collectors.toList());
collect.forEach(System.out::println);
System.out.println();
// 收集为Map集合,(oldValue, newValue) -> newValue)表示当key值冲突时选择第二个key值覆盖第一个key值
Map<Integer, User> map = users.stream().filter(user -> user.getSalary() > 5000)
.collect(Collectors.toMap(User::getUserId, Function.identity(), (oldValue, newValue) -> newValue));
map.forEach((key, value) -> {
System.out.println("key:"+key+",value:"+value);
});
System.out.println();
// 收集为Set集合,无序且不含重复值
Set<User> set = users.stream().filter(user -> user.getSalary() > 5000)
.collect(Collectors.toSet());
set.forEach(System.out::println);
System.out.println();
// 按用户编号分组,即将用户编号作为键,相同键的用户对象组成List集合作为值
Map<Integer, List<User>> map1 = users.stream().filter(user -> user.getSalary() > 5000)
.collect(Collectors.groupingBy(User::getUserId));
map1.forEach((key, value) -> {
System.out.println("key:"+key+",value.length:"+value.size());
});
}