一、Java8新特性
- Lambda表达式:允许把函数作为一个方法的参数传递到方法中。
- 方法引用:可以直接引用已有Java类或对象(实例)的方法或构造器。
- 默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。
- 新工具 :Nashorn引擎 jjs、类依赖分析器jdeps。
- Stream API :函数式编程风格引入。
- Date Time API:加强对日期与时间的处理。
- Optional 类 :用来解决空指针异常。
- Nashorn, JavaScript 引擎 :允许我们在JVM上运行特定的javascript应用。
二、Stream API 介绍(特性之一)
- 将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
- 不会存储元素,按需计算。
- 数据源流的来源可以是集合,数组,I/O channel, 产生器generator 等。
- 类似SQL语句一样的聚合操作, 比如filter, map, reduce, find, match, sorted等。
- 内部迭代:以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代,而Stream提供了内部迭代的方式。
三、具体使用
stream的操作符大体上分为两种:中间操作符和终止操作符
1. 生成流
- stream() − 为集合创建串行流。
- parallelStream() − 为集合创建并行流。
2. 中间操作符
-
filter(T-> boolean):过滤数据,保留 boolean 为 true 的元素。
//假设:list(20, 23, 25, 28, 30, 33, 37, 40); //从指定数据集合中过滤出大于等于30的数据集合 //collect(Collectors.toList())可以把流转换为 List 类型, //collect实际上是一个终止操作。 List<Integer> filterList= list.stream().filter(x -> x >= fil30).collect(Collectors.toList()); //结果:[30, 33, 37, 40] System.out.println(filterList);
-
map(T -> R):转换操作符,可以做数据转换。
//假设:list("1", "2", "3", "4", "5", "6"); List<Long> collect1 = list.stream() .map(x -> Long.parseLong(x)) .collect(Collectors.toList()); //结果:[1, 2, 3, 4, 5, 6] //结果:111111 list.stream() .mapToInt(x -> x.length()) .forEach(System.out::print); //结果:1.01.01.01.01.01.0 list.stream() .mapToDouble(x -> x.length()) .forEach(System.out::print);
-
flatMap(T -> Stream):将元素T映射成流,再把每一个流连接成为一个整体流。
List<List<String>> list = new ArrayList<List<String>>(){{ add(Lists.newArrayList("a","b","c")); add(Lists.newArrayList("d","e","f")); add(Lists.newArrayList("j","k","y")); }}; //结果:[[a, b, c], [d, e, f], [j, k, y]] List<String> collect = list.stream().flatMap(List::stream).collect(Collectors.toList()); //结果:[a, b, c, d, e, f, j, k, y]
-
distinct:去重,也可使用Collectors.toSet()达到去重目的。
List<Integer> strings =new ArrayList<>(Arrays.asList(1,2,2,8,4,7)) ; //去除重复的4 List<Integer> collect = strings.stream().map(i -> i * i).distinct().collect(Collectors.toList()); //结果:[1, 4, 64, 16, 49]
-
sorted:排序,前提是实现Comparable接口,也可自定义比较器。
//假设:list(5, 3, 7, 1, 4, 6); List<Integer> collect = list.stream().sorted((a, b) -> a.compareTo(b)).collect(Collectors.toList()); //结果:[1, 3, 4, 5, 6, 7]
-
limit:限流操作,比如有5个元素,只取前3个元素。
List<Integer> list = new ArrayList<>(Arrays.asList(1,2,2,8,4,7)); List<Integer> collect = list.stream() .limit(3) .collect(Collectors.toList()); //结果:[1,2,2]
-
skip:跳过操作,比如:有个10个元素,跳过5个元素,从第6个元素开始遍历。
List<Integer> strings = new ArrayList<>(Arrays.asList(1,2,2,8,4,7)); List<Integer> collect = list.stream().skip(2).collect(Collectors.toList()); //结果:[2,8,4,7]
-
peek:挑出操作,返回的stream仍旧是原来的stream。
List<String> strings = new ArrayList<>(Arrays.asList("a","b")); strings.stream().peek(x -> x.toUpperCase()).forEach(System.out::print); //结果:["a","b"] strings.stream().map(x -> x.toUpperCase()).forEach(System.out::print); //结果:["A","B"]
3. 终止操作符
-
forEach:支持并行处理。forEachOrdered:按顺序处理的,遍历速度较慢。
List<String> strings = new ArrayList<>(Arrays.asList("a","b")); list.stream().forEach(x-> System.out.print(x+' ')); //结果:["a","b"] //简化 list.forEach(x-> System.out.print(x+' ')); list.stream().forEach(x-> System.out.print(x+' ')); //结果:["a","b"]
-
collect:收集操作,collector提供了很多的收集器。如:toMap、toSet、toList、joining等操作。
//setter、getter、构造函数省略 class User { private String name; private Integer age; } //假设user1={"jj",123},user2={"fs",1},user3={"te",1} //toSet去重,结果:[1, 123] Set<Integer> collect = users .stream() .map(User::getAge) .collect(Collectors.toSet()); //toList,结果:[User{name='fs', age=1}, User{name='te', age=1}] //orElse(null):如果一个都没有,返回null List<User> collect = users .stream() .filter(user -> user.getAge() == 1) .collect(Collectors.toList()) .orElse(null); //toMap,结果:{jj=User{name='jj', age=123}, te=User{name='te', age=1}, fs=User{name='fs', age=1}} Map<String, User> map = users .stream() .collect(Collectors.toMap(User::getName, Function.identity())); //joining,假设list=["a","b"],结果:a,b,c String collect3 = list.stream().collect(Collectors.joining(",")); //counting,结果:3(类似list.size()) Long collect = users.stream().collect(Collectors.counting());
-
find:有如下两个查找方法。
findFirst:找到第一个
findAny:使用 stream() 时找到的是第一个元素,使用 parallelStream() 并行时找到的是其中一个元素。//结果:User{name='jj', age=123} User user = users.stream().findFirst().orElse(null); //结果:User{name='jj', age=123} User user = users.stream().findAny().orElse(null);
-
match:有如下三个匹配方法。
allMatch:所有元素都满足条件,返回boolean类型
anyMatch:任意一个元素满足条件,返回boolean类型
noneMatch:所有元素都不满足条件,返回boolean类型//所有对象的年龄都大于0? 结果:true boolean b = users.stream().allMatch(user -> user.getAge() > 0); //存在对象的年龄小于0? 结果:false boolean b1 = users.stream().anyMatch(user -> user.getAge() < 0); //不存在对象的年龄大于200? 结果:true boolean b2 = users.stream().noneMatch(user -> user.getAge() > 200);
-
count:统计操作,与集合的size()方法类似。
//结果:3 long count = users.stream().count();
4. min、max:获取最大,最小值。
//结果:User{name='jj', age=123}
Optional<User> max = users
.stream()
.max((User a, User b) -> a.getAge().compareTo(b.getAge()));
System.out.println(max.get());
5. reduce:规约操作,将整个流的值规约为一个值。
//结果:24
List<Integer> list = new ArrayList<>(Arrays.asList(1,2,2,8,4,7));
Optional<Integer> reduce = list.stream().reduce((a, b) -> a + b);
System.out.println(reduce.get());
6. toArray:数组操作,将数据流的元素转换成数组。
//结果:[1,2,2,8,4,7]
List<Integer> list = new ArrayList<>(Arrays.asList(1,2,2,8,4,7));
Integer[] integers = list.stream().toArray(Integer[]::new);
四、实际应用场景
1.从两个集合中找相同的元素。一般用户批量导入。先查询出数据,再比对,再进行新增或修改。
List<String> list1 = new ArrayList<>(Arrays.asList("ab","a","ad"));
List<String> list2 = new ArrayList<>(Arrays.asList("ac","a","ad"));
List<String> collect = list1.stream().filter(s -> list2.stream().anyMatch(s2 -> s2.equals(s))).collect(Collectors.toList());
System.out.println(collect);
2.统计指定集合中,姓名相同的人中年龄最小的年龄。
//假设user1={"jj",123},user2={"jj",1},user3={"te",1}
User user = users
.stream()
.sorted((User u1, User u2) -> u1.getAge().compareTo(u2.getAge()))
.findFirst()
.orElse(null);
//结果:User{name='jj', age=1}
System.out.println(user);