Lambda
1. 核心原则
- 类型推断
- Lambda 表达式中的参数类型都是由编译器推断得出的。
- Lambda 表达式中无需指定类型,程序依然能够编译
- 函数式接口
- 只包含一个抽象方法的接口,称为函数式接口。
- 在一个接口上使用 @FunctionalInterface 注解,这样作能够检 查它是不是一个函数式接口。
可推导,可省略
( 参数列表 ) -> { 代码 }
2. 关注点
- 方法的参数
- 具体的方法体
3. 省略规则
- 参数类型可以省略
- 方法题只有一句代码时花括号
{}
、return、该行代码的分号;
可以省略
- 方法只有一个参数时,小括号
()
可以省略
例 :
- 参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)与(a)效果相同
- 所有参数需包含在圆括号内,参数之间用逗号相隔。例如:(a, b) 或 (int a, int b) 或 (String a, int b, float c)
- 空圆括号代表参数集为空。例如:() -> 42
- 当只有一个参数,且其类型可推导时,圆括号()可省略。例如:a -> return a*a
- Lambda 表达式的主体可包含零条或多条语句,如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)
4. 双冒号操作符
双冒号
::
运算符实际是Lambda表达式的一种简写x -> System.out.println(x) 等价于 System.out::print
双冒号操作符一般有这几种用法
5. 代码示例
- Map集合遍历
Map<Integer,String> map = new HashMap<>(); map.put(1,"a"); map.put(2,"b"); map.put(3,"c"); //普通遍历 for (Integer key : map.keySet()) { System.out.println("k = " + key + " | v = " + map.get(key)); } //使用Lambda表达式遍历 map.forEach((k, v) -> System.out.println("k = " + k + " | v = " + v));
- List 集合遍历
List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); //普通遍历方式 for (int value : list) System.out.print(value + " "); //使用Lambda遍历 list.forEach( i -> System.out.print(i + " ") ); //使用双冒号运算符遍历 list.forEach(System.out::print);
- 替代匿名内部类
Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("匿名内部内不同实现方式"); } }); Thread thread = new Thread(() -> System.out.println("Lambda表达式方法实现"));
Lambda 表达式与匿名类的区别
使用匿名类与 Lambda 表达式的一大区别在于关键词的使用。对于匿名类,关键词 this 解读为匿名类,而对于 Lambda 表达式,关键词 this 解读为写就 Lambda 的外部类。
2. Stream
Stream(流)是一个来自数据源的元素队列并支持聚合操作
Stream 是 JDK8 中的新特性,可以使使用者以一种声明的方式处理数据,Stream将要处理的元素集合看作是一种流,并且可以在管道的节点上进行处理,比如删选,排序,聚合等。
- 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
- 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
所有 Stream 的操作必须以 lambda 表达式为参数。
2.1. 创建流
- 数组
//对于数组 Integer[] array = {1,2,3,4,5,6}; // 1.使用Arrays.stream(array); Stream<Integer> stream = Arrays.stream(array); // 2.使用Stream.of(array) Stream<Integer> stream1 = Stream.of(array);
- 单列集合
Person p1 = new Person("Jone",18); Person p2 = new Person("Bob",22); //对于单列集合 List<Person> people = new ArrayList<>(Arrays.asList(p1, p2)); Stream<Person> stream2 = people.stream();
- 双列集合转成单列集合后再创建
//双列集合 Map<Integer,String> map = new HashMap<>(); map.put(1, "Tom"); map.put(2, "Jack"); // Set<Map.Entry<Integer,String>> entrySet = map.entrySet(); // Stream<Map.Entry<Integer,String>> stream3 = entrySet.stream(); Stream<Map.Entry<Integer,String>> stream3 = map.entrySet().stream();
2.2. 中间操作
2.2.1 Filter
过滤流中的元素,为 true 的将会留在流中
List<String> list = new ArrayList<>(); list.add("car"); list.add("can"); list.add("care"); //普通方式过滤元素 List<String> array = new ArrayList<>(); for (String s : list) { if (!s.equals("can")) array.add(s); } //使用Steam流的方式过滤元素 List<String> result; result = list.stream() .filter(name -> !"can".equals(name)) .collect(Collectors.toList());
List<Integer> ls = new ArrayList<>(Arrays.asList(1,2,8,9,2,3,4,8,9,0)); //过滤掉 ls 集合中的奇数 ls.stream() //集合转化为流 .filter(x -> x % 2 == 0) //过滤掉奇数 .distinct() //去重 .forEach(System.out::println); //输出
[Debug]
2.2.2 Map
map 方法用于映射每个元素到对应的结果
使用匿名内部内
//将person对象映射为person对象的姓名 persons.stream() .map(new Function<Person, String>() { @Override public String apply(Person person) { return person.getName(); } }) .forEach(new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } });
使用Lambda表达式
//将person对象映射为person对象的姓名 persons.stream() .map(person -> person.getName()) .forEach(s -> System.out.println(s));
使用双冒号表达式
//将person对象映射为person对象的姓名 persons.stream() .map(Person::getName) .forEach(System.out::println);
persons.stream() .map(Person::getAge) .map(age -> age + 10) .forEach(System.out::println);
2.2.3 Distinct
去除流中的重复元素
distinct方法是依赖 Object 的 equals 方法来判断是否为相同对象的,所以需要注意重写 equals 方法
Integer[] array = {1, 1, 2, 3, 3, 4, 5, 6}; Stream.of(array) .distinct() .forEach(System.out::print); //123456
2.2.4 Sorted
对流中元素进行排序
调用空参的
sorted()
的方法,需要流中的元素实现了 Comparable 接口使用
sorted()
有参方法public static void main(String[] args) { Person p1 = new Person("Jone",15); Person p2 = new Person("Bob",22); Person p3 = new Person("Jack",13); Person p4 = new Person("Tom",18); List<Person> persons = new ArrayList<>(Arrays.asList(p1, p2, p3, p4)); persons.stream() .sorted((o1, o2) -> o1.getAge() - o2.getAge()) .forEach(x -> System.out.println(x.getName())); //Jack //Jone //Tom //Bob
2.2.5 Limit
limit 方法用于获取指定数量的流,超出的部分将会丢弃
persons.stream() .sorted((o1, o2) -> o1.getAge() - o2.getAge()) .limit(3) .forEach(x -> System.out.println(x.getName())); //Jack //Jone //Tom
2.2.6 Skip
跳过流中前 n 个元素,返回剩下的元素
persons.stream() .sorted((o1, o2) -> o1.getAge() - o2.getAge()) .skip(3) .forEach(x -> System.out.println(x.getName())); //Bob
2.2.7 FlatMap
map只能把一个对象映射为另一个对象来作为流中的元素,而flatMap可以把一个对象映射为多个对象作为流中的元素
Person p1 = new Person("Jone",15); Person p2 = new Person("Bob",22); List<Person> persons1 = new ArrayList<>(Arrays.asList(p1, p2)); Person p3 = new Person("Jack",13); Person p4 = new Person("Tom",18); List<Person> persons2 = new ArrayList<>(Arrays.asList(p1, p2)); Grade grade1 = new Grade(2018, persons1, "一年级"); Grade grade2 = new Grade(2019, persons1, "二年级"); List<Grade> grades = new ArrayList<>(Arrays.asList(grade1, grade2)); //从年级中拿到学生名字 grades.stream() .flatMap(new Function<Grade, Stream<Person>>() { @Override public Stream<Person> apply(Grade grade) { return grade.getPersons().stream(); } }) .forEach(new Consumer<Person>() { @Override public void accept(Person p) { System.out.println(p.getName()); } });
2.3 终结操作
2.3.1 ForEach
List<String> list = new ArrayList<>(); list.add("car"); list.add("can"); list.add("care"); list.forEach( s -> System.out.println(s));
2.3.2 Count
统计流中元素个数,返回一个long类型值
int c = (int)list.stream() .distinct() .count(); System.out.println(c);
2.3.3 Max&Min
获取流中的最大值和最小值,返回值类型为
Optional<T>
Person p1 = new Person("Jone",15); Person p2 = new Person("Bob",22); Person p3 = new Person("Jack",13); Person p4 = new Person("Tom",18); List<Person> persons = new ArrayList<>(Arrays.asList(p1, p2, p3, p4)); Optional<Integer> minAge = persons.stream() .map(Person::getAge) .min( (x, y) -> x - y); Optional<Integer> maxAge = persons.stream() .map(Person::getAge) .max(Comparator.comparingInt(x -> x)); System.out.println(minAge.get()); // 13 System.out.println(maxAge.get()); // 22
2.3.4 Collect
将当前流转化为一个集合
Person p1 = new Person("Bob",22); Person p2 = new Person("Jack",13); Person p3 = new Person("Tom",18); Person p4 = new Person("Tom",20); List<Person> persons = new ArrayList<>(Arrays.asList(p1, p2, p3)); //转化为List集合 List<String> nameList = persons.stream() .map(Person::getName) .collect(Collectors.toList()); //转化为Set集合 Set<String> nameSet = persons.stream() .map(Person::getName) .collect(Collectors.toSet()); //转化为Map集合 Map<String, Integer> nameMap = persons.stream() .collect(Collectors.toMap(new Function<Person, String>() { @Override public String apply(Person person) { return person.getName(); } }, new Function<Person, Integer>() { @Override public Integer apply(Person person) { return person.getAge(); } })); /* //转化为Map集合 Map<String, Integer> nameMap = persons.stream() .collect(Collectors.toMap(person -> person.getName(), person -> person.getAge())); //转化为Map集合 Map<String, Integer> nameMap = persons.stream() .collect(Collectors.toMap(Person::getName, Person::getAge)); *
Loading Comments...