目录
第一篇:JDK8新特性一:引入介绍
第二篇:JDK8新特性二:Lambda表达式引入介绍
第三篇:JDK8新特性二:Lambda表达式规范和语法
第四篇:JDK8新特性二:方法引入详述
第五篇:JDK8新特性二:foreach、排序、线程调用简单示例
第六篇:JDK8新特性三:Stream流+常见方法演示
第七篇:JDK8新特性四:Optional类的学习
Stream流
1 什么是Stream流
- java.util.stream.Stream
- Stream是JDK1.8中处理集合的关键抽象概念,Lambda和Stream是JDK1.8新增的函数式编程最有亮点的特性,它可以指定我们需要对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用StreamAPI对集合数据进行操作,类似于使用SQL执行数据库查询。Stream使用一种类似用SQL语句从数据库查询数据的直观方式来提供一种对Java集合运算和表达的抽象。Stream API可以极大提高Java编程的生产力,让程序员写出高效率、干净、简洁的代码。
- 这种风格将要处理的元素集合看做一种流,流在管道中传输,并且可以在管道的节点上进行处理,比如筛选、排序、聚合等。
- 元素流在管道中经过中间操作的处理,最后由最终操作得到前面处理之后的结果。
- 示例图
- Stream操作的步骤
- 创建Stream:一个数据源(如集合、数组),获取一个流
- 中间操作:一个操作链,对数据源的数据进行处理
- 终止操作:一个终止操作,执行中间操作链并产生结果
2 Stream创建方式
-
两种Stream流创建方式
- stream:是串行流,采用单线程执行
- parallelStream:是并行流,采用多线程执行
-
获取流的方式
-
根据Collection获取流
- 从Collection接口源码中可以看到,其中添加了default方法stream用来获取流对象,因此该接口的所有实现类都可以通过这种方式获取流
// 1. Collection获取流 List<String> list = new ArrayList<>(); Stream<String> stream1 = list.stream(); Set<String> set = new HashSet<>(); Stream<String> stream2 = set.stream(); Vector<String> vector = new Vector<>(); Stream<String> stream3 = vector.stream();
-
根据Map获取流
- Map接口并不是Collection接口的子接口,因此没法直接通过stream()方式获取流,此外因为Map是K-V形式的结构数据,不符合流元素的单一特征,因此还要划分Key、Value、entry等方面
// 2. 根据Map获取流 Map<String, String> map = new HashMap<>(); Stream<String> keyStream = map.keySet().stream(); Stream<String> valueStream = map.values().stream(); Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
-
根据数组获取流
- 数组对象不存在默认方法,在Stream接口中的静态方法of可以用来操作数据获取流
// 3. 通过数组获取流 String[] strArr = {"111","222","333"}; Stream<String> strStream = Stream.of(strArr);
- Stream.of的形参是可变参数,可以接收数组为参数,也可以给定一个元素序列,返回一个流对象
- 对于数组的操作,还可以使用Arrays.stream(T arr)来操作
-
-
流的常见方法,后面案例会用一部分来演示
-
分类
- 延迟方法:返回值仍然是Stream接口自身类型的方法,因此支持链式调用。(除了终结方法之外的方法都是延迟方法)
- 终结方法:返回值类型不再是Stream接口自身类型的方法,因此不再支持类似StringBuilder那样的链式调用。终结方法包括count和forEach等方法。
-
举例
forEach:遍历处理 filter:过滤 map:处理映射 reduce:元素反复结合,生成一个值 count:统计个数 limit:截取前几个 skip:忽略前几个 concat:流合并为一个流 sorted:排序 allMatch:检查是否匹配所有元素 anyMatch:检查是否至少匹配一个元素 noneMatch:检查是否一个都没有匹配 findFirst:返回第一个元素 findAny:返回任意一个元素 max:返回最大值 min:返回最小值 collect:将流转换其他形式
-
3 Stream将List转换为Set
-
使用到的方法
- stream.collect(Collectors.toSet())
-
测试实体类
@Data @AllArgsConstructor public class TestEntity { private String name; private Integer age; }
-
测试
List<TestEntity> list = new ArrayList<>(); TestEntity tom2 = new TestEntity("Tom2", 2); list.add(new TestEntity("Tom1",1)); list.add(tom2); list.add(new TestEntity("Tom3",3)); list.add(tom2); list.add(new TestEntity("Tom5",5)); // 创建Stream流 Stream<TestEntity> stream = list.stream(); // 1. 将List集合转换为Set,乱序|不允许重复|返回Set为HashSet Set<TestEntity> set = stream.collect(Collectors.toSet()); System.out.println(set);
-
说明
- 集合中的泛型实体类必须要重写hashCode方法,因为stream.collect(Collectors.toSet())方法返回的Set是HashSet类型,它对速度进行了优化,存入HashSet的元素必须定义hashCode()方法。同时在良好编程风格来看,同样需要重写equals方法
- 测试代码中,添加的两个tom2指向同一个对象,因此转换为Set对象之后,只会保留一个
- 可以进入toSet()方法源码,这里指明了方法返回值是什么具体的Set类型
4 Stream将List转换为Map
-
测试实体类
@Data @AllArgsConstructor @NoArgsConstructor public class TestEntity { private String name; private Integer age; }
-
测试代码
List<TestEntity> list = new ArrayList<>(); list.add(new TestEntity("Tom1",1)); list.add(new TestEntity("Tom2",2)); list.add(new TestEntity("Tom3",3)); list.add(new TestEntity("Tom4",4)); list.add(new TestEntity("Tom5",5)); // 创建Stream流 Stream<TestEntity> stream = list.stream(); // 将List集合转换为Map // Map<String, TestEntity> collect = stream.collect(Collectors.toMap(new Function<TestEntity, String>() { // @Override // public String apply(TestEntity testEntity) { // return testEntity.getName(); // } // }, new Function<TestEntity, TestEntity>() { // @Override // public TestEntity apply(TestEntity testEntity) { // return testEntity; // } // })); // Lambda优化 Map<String, TestEntity> collect = stream.collect(Collectors.toMap(TestEntity::getName, testEntity -> testEntity)); // 遍历 // collect.forEach(new BiConsumer<String, TestEntity>() { // @Override // public void accept(String s, TestEntity testEntity) { // System.out.println("s:"+s+","+testEntity.toString()); // } // }); // Lambda表达式优化 collect.forEach((s, testEntity) -> System.out.println("s:"+s+","+testEntity.toString()));
-
说明
- 上述测试代码中,生成Map的K-V规则是:K为name,V为整个实体对象
- Collectors.toMap()方法的两个形参分别是用来产生Map中Key和Value的映射函数,这里结合Function函数接口来看一下
- 根据我们生成Map的规则,在生成K的时候,使用name作为K值,所以在使用Function的时候,传递的两个泛型分别为TestEntity和String,TestEntity作为apply的形参,而String作为apply的返回值;同理toMap的第二个形参映射函数用来处理Value,使用TestEntity作为apply形参,然后将TestEntity作为Value,因此返回值也是TestEntity
- 在对toMap方法使用Lambda表达式进行精简的时候,第一个参数的形参是TestEntity对象,返回值也是TestEntity类中的方法,此时就可以使用对象方法引入的方式简化代码
- toMap方法返回的具体Map类型是HashMap
5 Reduce
-
将流中的元素反复根据指定规则进行整合,最终得到一个值
-
实体
@Data @AllArgsConstructor @NoArgsConstructor public class TestEntity { private String name; private Integer age; }
-
测试代码
// 1. 创建一个流 Stream<Integer> integerStream = Stream.of(40, 40, 80, 70, 60); Optional<Integer> reduce = null; // 匿名内部类形式 //reduce = integerStream.reduce(new BinaryOperator<Integer>() { // @Override // public Integer apply(Integer a, Integer b) { // return a - b; // } //}); // Lambda表达式形式 reduce = integerStream.reduce((a, b) -> a - b); System.out.println(reduce.get()); // 2. reduce操作数组,计算年龄和 List<TestEntity> list = new ArrayList<>(); list.add(new TestEntity("Tom1", 23)); list.add(new TestEntity("Tom2", 43)); list.add(new TestEntity("Tom3", 2)); // 根据Collection获取流 Stream<TestEntity> listStream = list.stream(); Optional<TestEntity> reduce1 = null; //reduce1 = listStream.reduce(new BinaryOperator<TestEntity>() { // @Override // public TestEntity apply(TestEntity e1, TestEntity e2) { // e1.setAge(e1.getAge() + e2.getAge()); // return e1; // } //}); // Lambda表达式形式 reduce1 = listStream.reduce((e1,e2) -> { e1.setAge(e1.getAge() + e2.getAge()); return e1; }); System.out.println(reduce1.get());
6 Stream Max和Min
-
Max:将流中的元素按照比较规则进行比较,最后获取含有最大值的数据
-
Min:将流中的元素按照比较规则进行比较,最后获取含有最小值的数据
-
实体
@Data @AllArgsConstructor @NoArgsConstructor public class TestEntity { private String name; private Integer age; }
-
示例代码-Max
// Max演示 // 1. 构建集合,创建流对象 List<TestEntity> list = new ArrayList<>(); list.add(new TestEntity("Tom1", 12)); list.add(new TestEntity("Tom2", 23)); list.add(new TestEntity("Tom3", 45)); list.add(new TestEntity("Tom4", 13)); Stream<TestEntity> listStream = list.stream(); // 使用Max,计算集合中年龄最大值 Optional<TestEntity> maxList = null; //max = listStream.max(new Comparator<TestEntity>() { // @Override // public int compare(TestEntity o1, TestEntity o2) { // return o1.getAge() - o2.getAge(); // } //}); // Lambda表达式 maxList = listStream.max((o1, o2) -> o1.getAge() - o2.getAge()); System.out.println(maxList.get()); // 2. 通过数组构建流,测试 int[] arr = {22,55,21,56,3}; int maxArr = Arrays.stream(arr).max().getAsInt(); System.out.println(maxArr);
-
示例代码-Min
// Min演示 // 1. 构建集合,创建流对象 List<TestEntity> list = new ArrayList<>(); list.add(new TestEntity("Tom1", 12)); list.add(new TestEntity("Tom2", 23)); list.add(new TestEntity("Tom3", 45)); list.add(new TestEntity("Tom4", 13)); Stream<TestEntity> listStream = list.stream(); // 使用Min,计算集合中年龄最大值 Optional<TestEntity> minList = null; //min = listStream.min(new Comparator<TestEntity>() { // @Override // public int compare(TestEntity o1, TestEntity o2) { // return o1.getAge() - o2.getAge(); // } //}); // Lambda表达式 minList = listStream.min((o1, o2) -> o1.getAge() - o2.getAge()); System.out.println(minList.get()); // 2. 通过数组构建流,测试 int[] arr = {22,55,21,56,3}; int minArr = Arrays.stream(arr).min().getAsInt(); System.out.println(minArr);
7 Stream Match匹配
-
三类
- anyMatch:在给定判断条件中,任意一个元素匹配成功,则返回true
- allMatch:在给定的判断条件中,所有元素都匹配成功,则返回true
- nonoMatch:在给定的判断条件中,所有元素都匹配不成功,则返回true
-
实体
@Data @AllArgsConstructor @NoArgsConstructor public class TestEntity { private String name; private Integer age; }
-
示例代码
-
anyMatch
// anyMatch:在给定判断条件中,任意一个元素匹配成功,则返回true // 构建集合,创建流 List<TestEntity> list = new ArrayList<>(); list.add(new TestEntity("Tom1", 12)); list.add(new TestEntity("Tom2", 243)); list.add(new TestEntity("Tom3", 23)); list.add(new TestEntity("Tom4", 15)); Stream<TestEntity> listStream = list.stream(); // 判断是否存在年龄大于100 //boolean result = listStream.anyMatch(new Predicate<TestEntity>() { // @Override // public boolean test(TestEntity testEntity) { // return testEntity.getAge() > 100; // } //}); boolean result = listStream.anyMatch(t -> t.getAge() > 100); System.out.println(result);
-
allMatch
// allMatch:在给定的判断条件中,所有元素都匹配成功,则返回true // 构建集合,创建流 List<TestEntity> list = new ArrayList<>(); list.add(new TestEntity("Tom1", 12)); list.add(new TestEntity("Tom2", 243)); list.add(new TestEntity("Tom3", 23)); list.add(new TestEntity("Tom4", 15)); Stream<TestEntity> listStream = list.stream(); // 判断是否年龄都大于100 //boolean result = listStream.allMatch(new Predicate<TestEntity>() { // @Override // public boolean test(TestEntity testEntity) { // return testEntity.getAge() > 100; // } //}); boolean result = listStream.allMatch(t -> t.getAge() > 100); System.out.println(result);
-
noneMatch
// nonoMatch:在给定的判断条件中,所有元素都匹配不成功,则返回true // 构建集合,创建流 List<TestEntity> list = new ArrayList<>(); list.add(new TestEntity("Tom1", 12)); list.add(new TestEntity("Tom2", 243)); list.add(new TestEntity("Tom3", 23)); list.add(new TestEntity("Tom4", 15)); Stream<TestEntity> listStream = list.stream(); // 判断是否所有年龄都不大于100 //boolean result = listStream.noneMatch(new Predicate<TestEntity>() { // @Override // public boolean test(TestEntity testEntity) { // return testEntity.getAge() > 100; // } //}); boolean result = listStream.noneMatch(t -> t.getAge() > 100); System.out.println(result);
-
8 Stream For循环
-
使用流对象的forEach方法,List接口继承Collection接口,Collection接口又继承了Iterable接口,在Iterable接口中也存在一个默认方法forEach,所以也可以通过 集合对象.forEach对集合进行遍历
-
实体
@Data @AllArgsConstructor @NoArgsConstructor public class TestEntity { private String name; private Integer age; }
-
示例代码
List<TestEntity> list = new ArrayList<>(); list.add(new TestEntity("Tom1", 323)); list.add(new TestEntity("Tom2", 23)); list.add(new TestEntity("Tom3", 43)); Stream<TestEntity> listStream = list.stream(); // 使用for遍历输出集合中实体 //listStream.forEach(new Consumer<TestEntity>() { // @Override // public void accept(TestEntity testEntity) { // System.out.println(testEntity.toString()); // } //}); // Lambda表达式 对象方法引入 listStream.forEach(testEntity -> System.out.println(testEntity.toString()));
9 Stream 过滤器
-
根据指定的过滤条件对流对象中数据进行过滤,并返回过滤之后的结果
-
实体
@Data @AllArgsConstructor @NoArgsConstructor public class TestEntity { private String name; private Integer age; }
-
示例代码
List<TestEntity> list = new ArrayList<>(); list.add(new TestEntity("Tom1", 323)); list.add(new TestEntity("Tom2", 23)); list.add(new TestEntity("Tom3", 43)); // 获取流对象 Stream<TestEntity> listStream = list.stream(); // 过滤 保留年龄小于50的实体对象 // filter方法的返回值依旧是Stream流对象,因此可以对其继续操作 //Stream<TestEntity> result = listStream.filter(new Predicate<TestEntity>() { // @Override // public boolean test(TestEntity testEntity) { // return testEntity.getAge() < 50; // } //}); Stream<TestEntity> result = listStream.filter(testEntity -> testEntity.getAge() < 50); // 遍历经过过滤之后的内容 //result.forEach(new Consumer<TestEntity>() { // @Override // public void accept(TestEntity testEntity) { // System.out.println(testEntity.toString()); // } //}); result.forEach(testEntity -> System.out.println(testEntity.toString()));
10 Stream 排序 sorted
-
使用sorted,根据指定比较规则来进行排序
-
对象
@Data @AllArgsConstructor @NoArgsConstructor public class TestEntity { private String name; private Integer age; }
-
示例代码
// 按年龄从大到小进行排序,使用sorted排序,然后forEach输出 List<TestEntity> list = new ArrayList<>(); list.add(new TestEntity("Tom1", 12)); list.add(new TestEntity("Tom2", 243)); list.add(new TestEntity("Tom3", 23)); list.add(new TestEntity("Tom4", 15)); Stream<TestEntity> listStream = list.stream(); // 进行sort排序 //listStream.sorted(new Comparator<TestEntity>() { // @Override // public int compare(TestEntity o1, TestEntity o2) { // // 这里需要注意顺序,前面减后面,最终结果是升序,反之降序 // return o2.getAge() - o1.getAge(); // } //}).forEach(new Consumer<TestEntity>() { // @Override // public void accept(TestEntity testEntity) { // System.out.println(testEntity.toString()); // } //}); // Lambda表达式精简 listStream.sorted((o1, o2) -> o2.getAge() - o1.getAge()).forEach(testEntity -> System.out.println(testEntity.toString()));
11 Stream limit和skip
-
limit:表示对流进行截取,只取前n个(从1开始,包括第n个),n大于集合长度时,就表示截取全部
-
skip:表示跳过前n个,截取剩下的(从1开始,不包括第n个),n大于集合长度时,将获得长度为0的空流
-
实体
@Data @AllArgsConstructor @NoArgsConstructor public class TestEntity { private String name; private Integer age; }
-
示例代码
-
limit
// 截取集合中前1一个数据,并输出 List<TestEntity> list = new ArrayList<>(); list.add(new TestEntity("Tom1", 323)); list.add(new TestEntity("Tom2", 23)); list.add(new TestEntity("Tom3", 43)); Stream<TestEntity> listStream = list.stream(); listStream.limit(1).forEach(testEntity -> System.out.println(testEntity.toString()));
-
skip
// 跳过集合中前两个数据,并输出 List<TestEntity> list = new ArrayList<>(); list.add(new TestEntity("Tom1", 323)); list.add(new TestEntity("Tom2", 23)); list.add(new TestEntity("Tom3", 43)); Stream<TestEntity> listStream = list.stream(); // Limit,表示从那个起始位置开始获取 listStream.skip(2).forEach(testEntity -> System.out.println(testEntity.toString()));
-
12 Stream 综合案例
-
功能
- 将集合中的实体根据年龄降序排序,然后过滤出姓名含有fclever的实体,并取出前两个
-
代码
List<TestEntity> list = new ArrayList<>(); list.add(new TestEntity("fclever", 20)); list.add(new TestEntity("meite", 28)); list.add(new TestEntity("zhangsan", 35)); list.add(new TestEntity("xiaowei", 16)); list.add(new TestEntity("fclever_list", 109)); list.add(new TestEntity("fclever_zhangsan", 110)); list.add(new TestEntity("lisi", 109)); // 要求:对数据流的数据实现降序排列、且名称包含fclever 获取前两位 // 1. 获取流对象 Stream<TestEntity> listStream = list.stream(); listStream.sorted((t1, t2) -> t2.getAge()-t1.getAge()) .filter(testEntity -> testEntity.getName() .contains("fclever")) .limit(2) .forEach(testEntity -> System.out.println(testEntity.toString()));
13 并行流和串行流区别
- 串行流
- 单线程的方式操作,适合数据量比较少的情况
- 并行流
- 多线程方式操作,数据量比较大的时候。
- 原理:
- Fork join将一个大的任务拆分n个小人物并行执行,最后在统计结果,有可能非常耗费CPU资源,但是确实可以提高效率。
- Fork join框架图
四 Optional
- Optional类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象
- Optional是一个容器,可以保存类型T的值,或者仅仅保存null。Optional提供了空值检测方式,很好地解决了空指针异常。
1 创建Optional
-
方法
- Optional.ofNullable()
- 可以传递空对象
- Optional.of()
- 不可以传递空对象,会直接抛出NullPointerException异常
- get()
- 获取值,如果值为空,会抛出NoSuchElementException异常
- isPresent()
- 判断值是否不等于null,不等于null返回true,否则为false
- Optional.ofNullable()
-
示例代码
Integer a = 1; Integer b = null; Optional<Integer> optional1 = Optional.ofNullable(a); System.out.println(optional1.get()); Optional<Integer> optional2 = Optional.ofNullable(b); // 如果值为null,调用get()方法会抛出new NoSuchElementException("No value present") //System.out.println(optional2.get()); System.out.println(optional2.isPresent()); Optional<Integer> optional3 = Optional.of(a); System.out.println(optional3.get()); // Optional.of()方法无法接收null对象 //Optional<Integer> optional4 = Optional.of(b); //System.out.println(optional4.get());
2 给空参数设置默认值
-
方法
- orElse()
- 当值为不为null的时候,返回原值,否则返回orElse中给定的其他值
- orElse()
-
示例代码
Integer a = 1; Integer b = null; // 输出1 System.out.println(Optional.ofNullable(a).orElse(null)); // 输出null System.out.println(Optional.ofNullable(b).orElse(null));
3 参数过滤
-
方法
- filter()
- 如果值存在并且能够匹配所给定的断言,就会返回一个Optional对象,否则返回一个空的Optional对象
- filter()
-
示例代码
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); // 判断是否含有3,如果有则返回整个List,否则返回null List<Integer> result = Optional.ofNullable(integerList).filter(f -> f.contains(3)).orElse(null); System.out.println(result);
4 结合Lambda表达式案例
4.1 案例1-对于String
-
示例代码
// 优化前 String name = "meitian"; if (name != null) { logger.info(name); } // 优化后 Optional<String> optionalName = Optional.ofNullable(name); // 判断值是否为null,不为null才输出 optionalName.ifPresent(System.out::println);
4.2 案例2-实体类非空检验
-
实体
@Data @AllArgsConstructor @NoArgsConstructor public class TestEntity { private String id; private String name; }
-
示例代码
private static TestEntity testEntity = null; public static void main(String[] args) { TestEntity testEntity = TestTwo.getEntity(); System.out.println(testEntity); } private static TestEntity getEntity() { // 优化前 //if (testEntity == null) { // return creteEntity(); //} //return testEntity; // 优化后 return Optional.ofNullable(testEntity).orElseGet(() -> creteEntity()); } private static TestEntity creteEntity() { return new TestEntity("1", "Tom"); }
-
方法
- orElseGet()
- 如果值不为null,则直接返回,否则会返回Supplier匿名内部类的调用,通过调用get() 方法返回值
- orElseGet()
4.3 优化3
-
实体
@Data @AllArgsConstructor @NoArgsConstructor public class TestEntity { private String id; private String name; }
-
示例代码
// 优化前 TestEntity order = new TestEntity("1", "Tom"); //if (order != null) { // String orderName = order.getName(); // if (orderName != null) { // return orderName.toLowerCase(); // } //} //return null; //优化后 //return Optional.ofNullable(order).map(orderEntity -> { // return orderEntity.getName(); //}).map(name -> { // return name.toLowerCase(); //}).orElse(null); //Optional<TestEntity> order1 = Optional.ofNullable(order); //Optional<String> s = order1.map(TestEntity::getName); //Optional<String> s1 = s.map(String::toLowerCase); //String s2 = s1.orElse(null); //System.out.println(s2); return Optional.ofNullable(order).map(TestEntity::getName).map(String::toLowerCase).orElse(null);
-
方法
- map()
- 如果值存在,就继续使用映射函数对值进行处理,当结果不为null的时候,会返回一个Optional包装的对象,否则返回空的Optional
- map()
null) {
// return orderName.toLowerCase();
// }
//}
//return null;
//优化后
//return Optional.ofNullable(order).map(orderEntity -> {
// return orderEntity.getName();
//}).map(name -> {
// return name.toLowerCase();
//}).orElse(null);
//Optional order1 = Optional.ofNullable(order);
//Optional s = order1.map(TestEntity::getName);
//Optional s1 = s.map(String::toLowerCase);
//String s2 = s1.orElse(null);
//System.out.println(s2);
return Optional.ofNullable(order).map(TestEntity::getName).map(String::toLowerCase).orElse(null);
- 方法
- map()
- 如果值存在,就继续使用映射函数对值进行处理,当结果不为null的时候,会返回一个Optional包装的对象,否则返回空的Optional