Lambda表达式 Lambda 表达式的基础语法
Java8中引入了一个新的操作符 “->” 该操作符称为箭头操作符或 Lambda 操作符
箭头操作符将 Lambda 表达式拆分成两部分:
语法格式
1 () -> System.out.println("Hello Lambda!");
1 (x) -> System.out.println(x)
1 x -> System.out.println(x)
语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句
1 2 3 4 Comparator<Integer> com = (x, y) -> { System.out.println("函数式接口"); return Integer.compare(x, y); };
语法格式五:若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写
1 Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”
1 (Integer x, Integer y) -> Integer.compare(x, y);
上联:左右遇一括号省 下联:左侧推断类型省 横批:能省则省
Lambda 需要“函数式接口”的支持
函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。
可以使用注解 @FunctionalInterface 修饰可以检查是否是函数式接口
Java8 内置的四大核心函数式接口 1 2 Consumer<T> : 消费型接口 void accept(T t);
1 2 Supplier<T> : 供给型接口 T get();
1 2 Function<T, R> : 函数型接口 R apply(T t);
1 2 Predicate<T> : 断言型接口 boolean test(T t);
方法引用
三种语法格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ## 原lambda表达式写法 Consumer<String> con = (x)->System.out.println(x); ## 改写 PrintStream ps = System.out; Consumer<String> con = (x)->ps.println(x); ##其中,println方法已经由System.out对象实现 PrintStream ps = System.out; //创建对象 #ps是System.out对象的引用 Consumer<String> con = ps::println; //对象的引用 :: 实例方法名 ##再次简写 Consumer<String> con = System.out::println; con.accept("abcd") Employee emp = new Employee(); Supplier<String> sup = () -> emp.getName(); //✅ Supplier<String> sup = () -> emp.getAge(); //❌,返回类型不匹一致 ## getAge()已经由emp对象实现,可以简写 Supplier<Integer> sup = emp::getAge();
1 2 3 4 5 6 7 8 9 10 Comparator<Integer> com = (x, y) -> Integer.compare(x, y); ## 其中,compare 在 Integer 类中为静态方法 public static int compare(int x, int y) { return (x < y) ? -1 : ((x == y) ? 0 : 1); } ## Integer 中 compare方法两参一返 ## Comparator 中,int compare(T o1, T o2),两参一返 ## 简写 Comparator<Integer> com = (x, y) -> Integer::compare;
1 2 3 4 5 6 7 8 9 public interface BiPredicate<T, U> { boolean test(T t, U u); } ## 原lambda表达式 BiPredicate<String, String> bp = (x, y) -> x.equals(y); ## equals()在String中已实现, ## 第一个参数是方法的调用者,第二个参数是方法的参数,可以简写 ## 在x.equals(y)中,x 是equal()的调用者,y是equal()的参数 BiPredicate<String, String> bp = String::equals
⚠️注意
构造器引用 语法格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public interface Supplier<T> { T get(); //无参有返 } ## 原lambda Supplier<Employee> sup = () -> new Employee(); //返回类型为Employee ## 简写 Supplier<Employee> sup = Employee::new; //自动匹配无参构造器 //sup.get(); public interface Function<T, R> { R apply(T t); //一参一返 } ## 原lambda Function<Integer,Employee> fe = (x)-> new Employee(x) ## 简写 Function<Integer,Employee> fe = Employee::new;//自动匹配一个参数构造器 //fe.apply(11); //BiFunction 两参一返 BiFunction<String, Integer, Employee> fun2 = Employee::new; //此时Employee必须要有两个参数的构造器
⚠️注意 构造器的参数列表,需要与函数式接口中参数列表保持一致!
数组引用 语法格式
1 2 3 4 5 6 7 8 9 ## 原lambda Function<Integer, String[]> fc = (x) -> new String[x]; String[] strs = fc.apply(10); System.out.println(strs.length); //String数组长度为10 ## 简写 Function<Integer, Employee[]> fc = Employee[] :: new; Employee[] emps = fc.apply(20); System.out.println(emps.length); //长度为20
Stream
Stream 自己不会存储元素。
Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream。
Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
Stream API 的操作步骤:
创建 Stream
中间操作
终止操作(终端操作)
创建 Stream 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 //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. 创建无限流 //迭代 Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(10); stream3.forEach(System.out::println); //生成 Stream<Double> stream4 = Stream.generate(Math::random).limit(2); stream4.forEach(System.out::println);
中间操作 筛选与切片
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
filter:接收Lambda,从流中排除某些元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 //内部迭代:由Stream API完成迭代 public void test(){ List<Person> people = Arrays.asList( new Person("张三", 18, 9999.99), new Person("李四", 58, 5555.55), new Person("王五", 26, 3333.33), new Person("赵六", 36, 6666.66), new Person("田七", 12, 8888.88) ); //中间操作 ,没有终止操作,所有中间操作不会执行 Stream<Person> stream = people.stream().filter(person -> { System.out.println("中间操作"); return person.getAge() > 5; }); //终止操作 stream.forEach(System.out::println); }
limit:截断流,使其元素不超过给定数量
短路:满足limit限制后不再执行
1 2 3 4 people.stream() .filter(person -> person.getScore() > 5000) .limit(2) //中间操作⬆️ .forEach(System.out::println); //终止操作
skip(n):跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit互补
1 2 3 4 people.stream() .filter(person -> person.getScore() > 5000) .skip(2) .forEach(System.out::println);
distinct:筛选,通过流所生成元素的hashcode() 和equals() 去除重复元素,
要想实现,需重写这两个方法 !
1 2 3 people.stream() .distinct() .forEach(System.out::println);
映射 map:接收Lambda ,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上 ,并将其映射成一个新的元素。
1 2 3 4 5 @Test public void test() { List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee", "fff"); list.stream().map(str -> str.toUpperCase()).forEach(System.out::println); }
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class test { @Test public void test() { List<String> list = Arrays.asList("aaa", "bbb"); list.stream().flatMap(test::filterCharacter).forEach(System.out::println); } public static Stream<Character> filterCharacter(String str){ List<Character> list = new ArrayList<>(); for (char c : str.toCharArray()) { list.add(c); } return list.stream(); } }
排序 sorted()——自然排序(Comparable)
sorted(Comparator com)——定制排序(Comparator)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Test public void test(){ //自然排序 List<String> list = Arrays.asList("aaa", "eee", "ddd", "bbb"); list.stream().sorted().forEach(System.out::println); //定制排序 List<Person> list1 = Arrays.asList( new Person("张三", 18, 2000.0), new Person("李四", 18, 5000.0), new Person("王五", 45, 8700.0), new Person("赵六", 42, 4200.0), new Person("陈七", 56, 13100.0) ); //如果年龄一样则按照姓名排序 list1.stream().sorted((p1,p2) -> { if (p1.getAge().equals(p2.getAge())){ return p1.getName().compareTo(p2.getName()); }else { return p1.getAge().compareTo(p2.getAge()); } }).forEach(System.out::println); }
终止操作 匹配与查找 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 @Test public void test(){ List<Person> list = Arrays.asList( new Person("张三", 18, 2000.0, Person.Status.BUSY), new Person("李四", 18, 5000.0,Person.Status.FREE), new Person("王五", 45, 8700.0,Person.Status.VOCATION), new Person("赵六", 42, 4200.0,Person.Status.BUSY), new Person("陈七", 56, 13100.0,Person.Status.BUSY) ); //allMatch 检查是否匹配所有元素,返回值为Boolean类型 boolean b0 = list.stream().allMatch(e -> e.getStatus().equals(Person.Status.BUSY)); System.out.println(b0); // false //anyMatch 检查是否匹配至少一个元素,返回值为Boolean类型 boolean b1 = list.stream().anyMatch(e -> e.getStatus().equals(Person.Status.BUSY)); System.out.println(b1); // true //noneMatch 检查是否没有匹配所有元素,返回值为Boolean类型 boolean b2 = list.stream().noneMatch(e -> e.getStatus().equals(Person.Status.BUSY)); System.out.println(b2); // false //findFirst 返回第一个元素 //Optional 防止空指针异常的类型,如果first为null,可以使用.orelse()方法指定一个不为空的对象 Optional<Person> op1 = list.stream() .sorted((e1, e2) -> Double.compare(e1.getSale(), e2.getSale())).findFirst(); System.out.println(op1.get()); // Person(name=张三, age=18, sale=2000.0, status=BUSY) //findAny 返回当前流中的任意元素 //parallelStream 并行流,多个进程同时去进行filter、findAny,谁先找到算谁的 Optional<Person> op2 = list.parallelStream().filter(e -> e.getStatus().equals(Person.Status.FREE)).findAny(); System.out.println(op2.get()); // Person(name=李四, age=18, sale=5000.0, status=FREE) //count 返回流中元素的总个数 long count = list.stream().count(); System.out.println(count); // 5 //max 返回流中的最大值 Optional<Person> max = list.stream().max((e1, e2) -> Double.compare(e1.getSale(), e2.getSale())); System.out.println(max.get()); // Person(name=陈七, age=56, sale=13100.0, status=BUSY) //min 返回流中的最小值 //返回list中的最小工资数 System.out.println(list.stream().map(Person::getSale).min(Double::compare).get()); // 2000.0 }
归约 reduce(T identity,BinaryOperator) / reduce(BinarOperator):可以将流中元素反复结合起来,得到一个值。
map和reduce的连接通常被成为map-reduce模式,因Google用它来进行网络搜索而出名
1 2 3 4 5 6 7 8 9 10 11 @Test public void test(){ List<Integer> list1 = Arrays.asList(1,2,3,4,5,6,7,8,9,10); //从0开始作为起始值,0作为x,先取y=1,执行x+y=1,再将x作为1,y取流中下一个元素2,一直执行下去 Integer sum = list1.stream().reduce(0, (x, y) -> x + y); System.out.println(sum); //上面那个返回值是Integer,是因为有起始值,不可能为空,而这条语句没有起始值,有可能为空! Optional<Double> reduce = list.stream().map(Person::getSale).reduce(Double::sum); System.out.println(reduce.get()); }
收集 collect:将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法。
Collectors类中提供了很多静态方法,能方便的创建常见收集器实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Test public void test(){ List<String> nameList = list.stream().map(Person::getName).collect(Collectors.toList()); //放入set可以进行去重 Set<String> collect = list.stream().map(Person::getName).collect(Collectors.toSet()); //要想放入我们指定的收集器中,可以采取以下方式 // Collectors.toCollection( xxx ::new) xxx为我们想要放入的集合类型 // 例如:想将list中的人员name收集放入一个LinkedList中 LinkedList<String> collect1 = list.stream().map(Person::getName).collect(Collectors.toCollection(LinkedList::new)); //总数 Long count = list.stream().collect(Collectors.counting()); System.out.println(count); // 5 //平均值 Double avg = list.stream().collect(Collectors.averagingDouble(Person::getSale)); System.out.println(avg); // 6600.0 //总和 Double sum = list.stream().collect(Collectors.summingDouble(Person::getSale)); System.out.println(sum); // 33000.0 //最大值:最大salary的员工信息 Optional<Person> max = list.stream().collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSale(), e2.getSale()))); System.out.println(max.get()); // Person(name=陈七, age=56, sale=13100.0, status=BUSY) //最小值:最小的salary Optional<Double> min = list.stream().map(Person::getSale).collect(Collectors.minBy(Double::compare)); System.out.println(min.get()); // 2000.0 }
分组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 //简单分组 Map<Person.Status, List<Person>> map = list.stream().collect(Collectors.groupingBy(Person::getStatus)); System.out.println(map); // {FREE=[Person(name=张三, age=18, sale=5000.0, status=FREE)], // BUSY=[Person(name=张三, age=18, sale=2000.0, status=BUSY), Person(name=赵六, age=42, sale=4200.0, status=BUSY), Person(name=陈七, age=56, sale=13100.0, status=BUSY)], // VOCATION=[Person(name=王五, age=45, sale=8700.0, status=VOCATION)]} //多级分组 Map<Person.Status, Map<String, List<Person>>> map2 = list.stream().collect(Collectors.groupingBy(Person::getStatus, Collectors.groupingBy(e -> { if (e.getAge() <= 35) { return "青年"; } else if (e.getAge() <= 50) { return "中年"; } else { return "老年"; } }))); //{FREE={青年=[Person(name=张三, age=18, sale=5000.0, status=FREE)]}, // BUSY={青年=[Person(name=张三, age=18, sale=2000.0, status=BUSY)], 老年=[Person(name=陈七, age=56, sale=13100.0, status=BUSY)], 中年=[Person(name=赵六, age=42, sale=4200.0, status=BUSY)]}, // VOCATION={中年=[Person(name=王五, age=45, sale=8700.0, status=VOCATION)]}} System.out.println(map2); }
分区
1 2 3 4 Map<Boolean, List<Person>> map = list.stream().collect(Collectors.partitioningBy(e -> e.getSale() > 9000.0)); System.out.println(map); //{false=[Person(name=张三, age=18, sale=2000.0, status=BUSY), Person(name=张三, age=18, sale=5000.0, status=FREE), Person(name=王五, age=45, sale=8700.0, status=VOCATION), Person(name=赵六, age=42, sale=4200.0, status=BUSY)], // true=[Person(name=陈七, age=56, sale=13100.0, status=BUSY)]}
其他
1 2 3 4 5 6 7 8 9 10 //对数据进行统计 DoubleSummaryStatistics dss = list.stream().collect(Collectors.summarizingDouble(Person::getSale)); System.out.println(dss.getAverage()); // 6600.0 System.out.println(dss.getCount()); // 5 System.out.println(dss.getMax()); // 13100.0 //对数据进行连接 //joining(",", "**", "=="),中间加, 前面加**,后面加== String str = list.stream().map(Person::getName).collect(Collectors.joining(",", "**", "==")); System.out.println(str); // **张三,张三,王五,赵六,陈七==