Java8相关特性——Lambda 表达式 和 Stream API
底层的数据结构(Hashmap)
垃圾回收机制(内存结构)减少 内存溢出的内存
最大化减少空指针异常 Optional
并行的使用
1. Lambda表达式
Lambda 是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。
可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
Lambda 表达式语法
Lambda 表达式在Java 语言中引入了一个新的语法元 素和操作符。这个操作符为 “->” , 该操作符被称 为 Lambda 操作符或剪头操作符。它将 Lambda 分为 两个部分:左侧:指定了 Lambda 表达式需要的所有参数
右侧:指定了 Lambda 体,即 Lambda 表达式要执行 的功能。> 无参数, 无返回值 : () -> 有一个参数,无返回值:(x) -> ; x -> 有两个或以上的参数,有返回值并且有多条语句 (x,y) -> { 语句一; 语句二; return 返回值; }
//无参数 无返回值
@Test
public void test5() {
Runnable r = new Runnable() {
@Override
public void run() {
}
};
r.run();
Runnable r1 = () -> System.out.println("hahah");
r1.run();
}
//有一个参数,无返回值
@Test
public void test6() {
Consumer<String> con = (x) -> System.out.println(x);
con.accept("hahaha");
}
//多个入参,有返回值
@Test
public void test7() {
Comparator<Integer> comparator = (x, y) -> {
System.out.println("w6283");
return Integer.compare(x, y);
};
}
注意:如果Lambda体中只有一条语句, {} 和returne可以省略
2. 函数式接口
接口中只有一个抽象方法
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer 消费型接口 | T | void | 对类型为T的对象应用操 作,包含方法: void accept(T t) |
Supplier 供给型接口 | 无 | T | 返回类型为T的对象,包 含方法:T get(); |
Function 函数型接口 | T | R | 对类型为T的对象应用操 作,并返回结果。结果 是R类型的对象。包含方 法:R apply(T t); |
Predicate 断定型接口 | T | boolean | 确定类型为T的对象是否 满足某约束,并返回 boolean 值。包含方法 boolean test(T t); |
BiFunction 消费型接口 | T, U | R | 对类型为 T, U 参数应用 操作,返回 R 类型的结 果。包含方法为 R apply(T t, U u) |
UnaryOperator (Function子接口) | T | T | 对类型为T的对象进行一 元运算,并返回T类型的 结果。包含方法为 T apply(T t) |
BinaryOperator (BiFunction 子接口) | T,T | T | 对类型为T的对象进行二 元运算,并返回T类型的 结果。包含方法为 T apply(T t1, T t2); |
BiConsumer | T, U | void | 对类型为T, U 参数应用 操作。包含方法为 void accept(T t, U u) |
ToIntFunction | T | int | 计算int的函数值 |
ToLongFunction | T ,T | long | 计算long的函数值 |
ToDoubleFunction | T | double | 计算double函数值 |
IntFunction | int | R | 参数int类型的函数值 |
LongFunction | long | R | 参数long类型的函数值 |
DoubleFunction | double | R | 参数double类型的函数值 |
//Predicate<T> 断言型接口:
@Test
public void test8() {
List<String> list = Arrays.asList("Hello", "atguigu", "Lambda", "www", "ok");
List<String> strList = filterStr(list, (s) -> s.length() > 3);
for (String str : strList) {
System.out.println(str);
}
}
//需求:将满足条件的字符串,放入集合中
public List<String> filterStr(List<String> list, Predicate<String> pre) {
List<String> strList = new ArrayList<>();
for (String str : list) {
if (pre.test(str)) {
strList.add(str);
}
}
return strList;
}
//Function<T, R> 函数型接口:
@Test
public void test9() {
String newStr = strHandler("\t\t\t lalalalal ", (str) -> str.trim());
System.out.println(newStr);
String subStr = strHandler("hahahahhahah", (str) -> str.substring(2, 5));
System.out.println(subStr);
}
//需求:用于处理字符串
public String strHandler(String str, Function<String, String> fun) {
return fun.apply(str);
}
//Supplier<T> 供给型接口 :
@Test
public void test10() {
List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 100));
for (Integer num : numList) {
System.out.println(num);
}
}
//需求:产生指定个数的整数,并放入集合中
public List<Integer> getNumList(int num, Supplier<Integer> sup) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
Integer n = sup.get();
list.add(n);
}
return list;
}
//Consumer<T> 消费型接口 :
@Test
public void test11() {
happy(10000, (m) -> System.out.println("fdkjfkds"));
}
public void happy(double money, Consumer<Double> con) {
con.accept(money);
}
3. 方法与构造器引用
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
(实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!)
方法引用:使用操作符 “::” 将方法名和对象或类的名字分隔开来
- 对象::实例方法
Consumer<String> con= System.out::println;
con.accept("hahahaha");
User user=new User();
Supplier<Integer> supplier=user::getAge;
supplier.get();
- 类::静态方法
Comparator<Integer> com=Integer::compare;
com.compare(14,15);
- 类::实例方法
BiPredicate<String,String> bp=String::endsWith;
bp.test("hahah","12");
- 构造器
Supplier<User> sup=User::new;
User user1=sup.get();
- 数组引用
Function<Integer,String[]> function=String[]::new;
String [] str=function.apply(16);
注意:
1.需要实现的抽象方法参数列表和返回值类型与需要实现的参数列列表和返回类型一致
2.调用的构造器取决于函数式方法抽象接口的入参列表
4. Stream API
Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一 个则是 Stream API(java.util.stream.*)。Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式
- 创建 Stream
//创建流
public void test1(){
//1. Collection 提供了两个方法 stream() 与 parallelStream()
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流
//2. 通过 Arrays 中的 stream() 获取一个数组流
Integer[] nums = new Integer[10];
Stream<Integer> stream1 = Arrays.stream(nums);
//3. 通过 Stream 类中静态方法 of()
Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6);
//4. 创建无限流 Use Stream.iterate()
//迭代
Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(10);
stream3.forEach(System.out::println);
// Use Stream.generate() 生成
Stream<Double> stream4 = Stream.generate(Math::random).limit(2);
stream4.forEach(System.out::println);
}
中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!
而在终 止操作时一次性全部处理,称为“惰性求值(1). 筛选与切片
方 法 描述 filter(Predicate p) 接收 Lambda , 从流中排除某些元素。 distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去 除重复元素 limit(long maxSize) 截断流,使其元素不超过给定数量 skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素 不足 n 个,则返回一个空流。与 limit(n) 互补 public void test2(){ //过滤 Stream stream=userList.stream().filter(user -> user.getAge()>15); stream.forEach(System.out::println); System.out.println("++++++++++++++1"); //limit Stream stream1=userList.stream().filter(user -> user.getAge()>15).limit(2); stream1.forEach(System.out::println); System.out.println("++++++++++++++2"); //skip 跳过2个 Stream stream2=userList.stream().filter(user -> user.getAge()>15).skip(2); stream2.forEach(System.out::println); System.out.println("++++++++++++++3"); //distinct 去重 Stream stream3=userList.stream().filter(user -> user.getAge()>15).distinct(); stream3.forEach(System.out::println); System.out.println("++++++++++++++"); }
(2). 映射
方 法 描述 map(Function f) 接收一个函数作为参数,该函数会被应用到每个元 素上,并将其映射成一个新的元素。 flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另 一个流,然后把所有流连接成一个 @Test public void test3(){ List<String> strList=Arrays.asList("aaa","bbb","ccc","ddd"); Stream stream = strList.stream().map(String::toUpperCase); stream.forEach(System.out::println); Stream stream1 = userList.stream().map(User::getAge); stream1.forEach(System.out::println); System.out.println("---------------------------------------"); Stream stream2 =strList.stream().flatMap(word -> Arrays.stream(word.split(""))); stream2.forEach(System.out::println); }
(3).排序
方 法 描 述 sorted() 产生一个新流,其中按自然顺序排序 sorted(Comparator comp) 产生一个新流,其中按比较器顺序排序 @Test public void test4(){ Stream stream =userList.stream().sorted(); stream.forEach(System.out::println); System.out.println("---------------------------------------"); Stream stream1 =userList.stream().sorted((u1,u2) -> Integer.compare(u1.getAge(),u2.getAge())); stream1.forEach(System.out::println); }
终止操作
终端操作会从流的流水线生成结果。其结果可以是任何不是流的 值,例如:List、Integer,甚至是 void
(1). 查找与匹配
方法 描述 allMatch(Predicate p) 检查是否匹配所有元素 row 2 col 1 row 2 col 2 anyMatch(Predicate p) 检查是否至少匹配一个元素 noneMatch(Predicate p) 检查是否没有匹配所有元素 findFirst() 返回第一个元素 findAny() 返回当前流中的任意元素 count() 返回流中元素总数 max(Comparator c) 返回流中最大值 min(Comparator c) 返回流中最小值 forEach(Consumer c) 内部迭代(使用 Collection 接口需要用户去做迭 代,称为外部迭代。相反,Stream API 使用内部 迭代——它帮你把迭代做了) public void test6(){ boolean b1= userList.stream().allMatch(user -> user.getAge()>20); System.out.println("b1---------"+b1); boolean b2= userList.stream().anyMatch(user -> user.getAge()>20); System.out.println("b2---------"+b2); boolean b3= userList.stream().noneMatch(user -> user.getAge()>20); System.out.println("b3---------"+b3); User b4 = userList.stream().findFirst().get(); System.out.println("b4---------"+b4); User b5 = userList.stream().findAny().get(); System.out.println("b5---------"+b5); //串行 User b6 = userList.parallelStream().findAny().get(); System.out.println("b6---------"+b6); }
(2). 归约
map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它 来进行网络搜索而出名
方法 描述 reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。 返回 reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。 返回 Optional public void test7(){ List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10); Integer sum = list.stream() .reduce(0, (x, y) -> x + y); System.out.println(sum); System.out.println("----------------------------------------"); //相当于 Integer sum1=0; for (Integer integer : list) { sum1+=integer; } System.out.println(sum1); System.out.println("----------------------------------------"); Optional<Double> op = userList.stream() .map(User::getSalary) .reduce(Double::sum); System.out.println(op.get()); }
(3). 收集
方法 描述 collect(Collector c) 将流转换为其他形式。接收一个 Collector接口的 实现,用于给Stream中元素做汇总的方法 Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例
```
//收集
@Test
public void test8(){
List<String> strs=userList.stream().map(User::getUserName).collect(Collectors.toList());
Set<String> strs1=userList.stream().map(User::getUserName).collect(Collectors.toSet());
HashSet<String> strs2=userList.stream().map(User::getUserName).collect(Collectors.toCollection(HashSet::new));
Map<String,User> strs3=userList.stream().collect(Collectors.toMap(User::getUserName, Function.identity()));
}
//分组
@Test
public void test9(){
Map<String, List<User>> map = userList.stream()
.collect(Collectors.groupingBy(User::getUserName));
System.out.println(map);
}
//多级分组
@Test
public void test10(){
Map<String, Map<String, List<User>>> map = userList.stream()
.collect(Collectors.groupingBy(User::getUserName, Collectors.groupingBy((e) -> {
if(e.getAge() >= 60)
return "老年";
else if(e.getAge() >= 35)
return "中年";
else
return "成年";
})));
System.out.println(map);
}
//分区
@Test
public void test11(){
Map<Boolean, List<User>> map = userList.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000));
System.out.println(map);
}
```