lambda表达式详解
1、 lambda表达式
JVM内部是通过invokedynamic指令来实现Lambda表达式的
Lambda中允许将一个函数作为方法的参数,即函数作为参数传递进方法中
使用Lambda表达式可以使代码更加简洁
2、变量作用域
Lambda表达式只能引用标记了final的外层局部变量.即不能在Lambda表达式内部修改定义在作用域外的局部变量,否则会导致报错
Lambda表达式中可以直接访问外层的局部变量
Lambda表达式中外层局部变量可以不用声明为final, 但是必须不可被后面的代码修改,即隐性地具有final的语义
Lambda表达式中不允许声明一个与外层局部变量同名的参数或者局部变量
3、使用示例
匿名内部类
匿名内部类: 匿名内部类仍然是一个类,不需要指定类名,编译器会自动为该类取名
Java中的匿名内部类:
public class MainAnonymousClass {public static void main(String[] args) {new Thread(new Runnable(){@Overridepublic void run(){System.out.println("Anonymous Class Thread run()");}}).start();;}
}
使用Lambda表达式实现匿名内部类:
public class MainLambda {public static void main(String[] args) {new Thread(() -> System.out.println("Lambda Thread run()")).start();;}
}
4、带参函数
带参函数的简写:
List<String> list = Arrays.asList("I", "love", "you", "too");
Collections.sort(list, new Comparator<String>() { // 接口名@Overridepublic int compare(String s1, String s2) { // 方法名if(s1 == null)return -1;if(s2 == null)return 1;return s1.length() - s2.length(); }
});
上述代码通过内部类重载了Comparator接口的compare() 方法来实现比较逻辑. 采用Lambda表达式可简写如下:
List<String> list = Arrays.asList("I", "love", "you", "too");
Collections.sort(list, (s1, s2) -> { // 省略参数表类型if (s1 == null)return -1;if (s2 == null)return 1;return s1.length() - s2.length();
});
上述代码根内部类的作用一样
除了省略了接口名和方法名,代码中的参数类型也可以省略
因为javac的类型推断机制,编译器能够根据上下文信息推断出参数的类型
5、 Collection
5.1、forEach
增强型for循环:
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
for(String str : list) {if (str.length() > 3)System.out.println(str);
}
使用forEach() 方法结合匿名内部类实现:
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.forEach(new Consumer<String>(){@Overridepublic void accept(String str) {if (str.length() > 3) {System.out.println(str);}}
});
使用forEach()结合Lambda表达式迭代
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.forEach(str -> {if (str.length() > 3) {Systemm.out.println(str);}
});
上述代码给forEach() 方法传入一个Lambda表达式,不需要知道accept() 方法,也不需要知道Consumer接口,类型推导已经完成了这些
5.2、removeIf
该方法签名: boolean removeIf(Predicate<? super E> filter);
删除容器中所有满足filter指定条件的元素
Predicate是一个函数接口,里面有一个待实现的方法boolean test(T t)
如果需要在迭代过程中对容器进行删除操作必须使用迭代器, 否则会抛出ConcurrentModificationException.
使用迭代器删除列表元素:
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
Iterator<String> it = list.iterator();
while (it.hasNext()) {if (it.next().length > 3) {it.remove();}
}
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.removeIf(new Predicate<String>(){@Overridepublic boolean test(String str) {return str.length() > 3;}
});
使用removeIf结合Lambda表达式实现:
Array<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.removeIf(str -> str.length() > 3);
使用Lambda表达式不需要记忆Predicate接口名,也不需要记忆test() 方法名,只需要此处需要一个返回布尔类型的Lambda表达式
5.3、replaceAll
该方法签名: void replaceAll(UnaryOperator<E> operator);
对每个元素执行operator指定的操作,并用操作结果来替换原来的元素
UnaryOperator是一个函数接口,里面有待实现的方法T apply(T t)
使用下标实现元素替换:
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
for (int i = 0; i < list.size(); i ++) {String str = list.get(i)if (str.length() > 3) {list.set(i, str.toUpperCase());}
}
使用replaceAll结合匿名内部类实现:
ArrayList<String> list =new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.replaceAll(new UnaryOperator<>(String){@Overridepublic String apply(String str) {if (str.length() > 3) {return str.toUpperCase();}return str;}
});
代码调用replaceAll() 方法,并使用匿名内部类实现UnaryOperator接口
使用Lambda表达式实现:
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.replaceAll(str -> {if (str.length > 3) {return str.toUpperCase();}return str;
});
5.4、sort
该方法定义在List接口中,方法签名: void sort(Comparator<? super E> c);
根据c指定的比较规则对容器进行排序
Comparator接口中需要实现接口int compare(T o1, T o2)
使用Collections的sort() 方法:
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
Collections.sort(list, new Comparator<String>() {@Overridepublic int compare(String str1, String str2) {return str1.length() - str2.length();}
});
直接使用List.sort() 方法,结合Lambda表达式:
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.sort((str1, str2) -> str1.length() - str2.length());
5.5 、spliterator
该方法签名: Spliterator<E> spliterator();
Spliterator既可以像Iterator那样逐个迭代,也可以批量迭代,批量迭代可以降低迭代的开销
Spliterator是可拆分的,一个Spliterator可以通过调用Spliterator<T> trySplit() 方法来尝试分成两个.一个是this, 一个是新返回的元素.这两个迭代器代表的元素没有重叠
可通过多次调用Spliterator.trySplit() 方法来分解负载,以便于多线程处理
5.6、stream和parallStream
Stream() 和parallStream() 分别返回该容器的Stream视图表示
parallStream() 返回并行的Stream
Stream是Java函数式编程的核心类
6、 Map
6.1、forEach
该方法签名: void forEach(BiConsumer<? super K,? super V> action);
对Map中的每个映射执行action操作
BiConsumer是一个函数接口,里面有一个待实现方法 void accept(T t, U u);
使用Java 7之前的方式输出Map中所有的对应关系:
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
for (Map.Entry<Integer, String> entry : map.entrySet()) {system.out.println(entry.getKey() + "=" + entry.getValue());
}
使用Map的forEach() 方法,结合匿名内部类:
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.forEach(new BiConsumer<Integer, String>() {@Overridepublic void accept(Integer k, String v) {System.out.println(k + "=" + v);}
});
使用Lambda表达式:
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.forEach((k, v) -> System.out.println(k + "=" + v));
6.2、getOrDefault
该方法签名: V getOrDefault(Object key, V defaultValue);
按照给定的key查询Map中对应的value, 如果没有找到则返回defaultValue
查询Map中指定键所对应的值,如果不存在则返回NoValue:
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
System.out.println(map.getOrDefault(4,"NoValue"));
6.3、putIfAbsent
该方法签名: V putIfAbsent(K key, V value);
只有在不存在key值的映射或映射值为null时,才将value指定的值放入到Map中,否则不对Map做修改
该方法将判断和赋值合二为一,使用起来更加方便
6.4、remove
该方法签名: remove(Object key);
根据指定的key值删除Map中映射关系
该方法签名: remove(Object key, Object value);
只有在当前Map中key正好映射到value时才删除该映射
6.5、replace
该方法签名: replace(K key, V value);
只有在当前Map中key的映射存在时才用value去替换原来的值
该方法签名: replace(K key, V oldValue, V newValue);
只有在当前Map中key的映射存在且等于oldValue时,才用newValue去替换原来的值,否则不做任何操作
6.5、replaceAll
该方法签名: replaceAll(BiFunction<? super K, ? super V, ? extends V> function);
对Map中的每个映射执行function操作,并用function的执行结果替换原来的value
其中BiFunction是一个函数接口,里面有一个待实现的方法R apply(T t, U u)
使用Java 7以前的方式将Map中的映射关系的单词都转换成大写:
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
for (Map.Entry<Integer, String> entry : map.entrySet()) {entry.setValue(entry.getValue().toUpperCase());
}
使用replaceAll方法结合匿名内部类:
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.replaceAll(new BiFunction<Integer, String, String>(){@Overridepublic String apply(Integer k, String v) {return v.toUpperCase();}
});
使用Lambda表达式实现:
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.replaceAll(<k, v> -> v.toUpperCase());
6.6、merge
>该方法签名: merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction);
如果Map中的key对应的映射不存在或者为null, 则将value, value不可能为null关联到key上
否则执行remappingFunction, 如果执行结果非null, 则用该结果与key关联,否则在Map中删除key的映射
其中BiFunction是一个函数接口,里面有一个待实现方法R apply(T t, U u)
merge()方法语义复杂,但使用的方式明确,经典的使用场景: 将新的错误信息拼接到原来的信息上: