Java中的Lambda表达式详细解析!详细解析Collection和Map中的表达式的使用方式
【摘要】 本篇文章中对Java中的Lambda表达式的使用进行详细的分析说明。从介绍Lambda表达式开始,分析了Java中的变量作用域和匿名内部类。然后通过一个个具体的使用实例说明了Java中的Lambda表达式的具体使用方式。通过这篇文章,基本上能够熟练掌握Java Lambda的具体使用方式。
Lambda表达式
- JVM内部是通过invokedynamic指令来实现Lambda表达式的
- Lambda中允许将一个函数作为方法的参数,即函数作为参数传递进方法中
- 使用Lambda表达式可以使代码更加简洁
变量作用域
- Lambda表达式只能引用标记了final的外层局部变量.即不能在Lambda表达式内部修改定义在作用域外的局部变量,否则会导致报错
- Lambda表达式中可以直接访问外层的局部变量
- Lambda表达式中外层局部变量可以不用声明为final, 但是必须不可被后面的代码修改,即隐性地具有final的语义
- Lambda表达式中不允许声明一个与外层局部变量同名的参数或者局部变量
使用示例
匿名内部类
- 匿名内部类: 匿名内部类仍然是一个类,不需要指定类名,编译器会自动为该类取名
- Java中的匿名内部类:
public class MainAnonymousClass { public static void main(String[] args) { new Thread(new Runnable(){ @Override public 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();; } }
带参函数
- 带参函数的简写:
List<String> list = Arrays.asList("I", "love", "you", "too");
Collections.sort(list, new Comparator<String>() { // 接口名
@Override
public 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的类型推断机制,编译器能够根据上下文信息推断出参数的类型
Java四大内置函数
Consumer
- 消费型接口Consumer< T > : void accept(T t);
- 供给型接口Supplier< T > : T get();
- 函数式接口Function< T,R > : R apply(T t);
- 断言型接口Predicate< T >: boolean test(T t);
Collection
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>(){
@Override
public void accept(String str) {
if (str.length() > 3) {
System.out.println(str);
}
}
});
- 使用Lambda表达式实现如下:
// 使用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接口,类型推导已经完成了这些
removeIf
- 该方法签名: boolean removeIf(Predicate<? super E> filter);
- 删除容器中所有满足filter指定条件的元素
- Predicate是一个函数接口,里面有一个待实现的方法boolean test(T t)
- 删除容器中所有满足filter指定条件的元素
- 如果需要在迭代过程中对容器进行删除操作必须使用迭代器, 否则会抛出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();
}
}
- 使用removeIf() 方法结合匿名内部类实现:
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.removeIf(new Predicate<String>(){
@Override
public 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表达式
replaceAll
- 该方法签名: void replaceAll(UnaryOperator<E> operator);
- 对每个元素执行operator指定的操作,并用操作结果来替换原来的元素
- UnaryOperator是一个函数接口,里面有待实现的方法T apply(T t)
- 对每个元素执行operator指定的操作,并用操作结果来替换原来的元素
- 使用下标实现元素替换:
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){
@Override
public 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;
});
sort
- 该方法定义在List接口中,方法签名: void sort(Comparator<? super E> c);
- 根据c指定的比较规则对容器进行排序
- Comparator接口中需要实现接口int compare(T o1, T o2)
- 根据c指定的比较规则对容器进行排序
- 使用Collections的sort() 方法:
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
Collections.sort(list, new Comparator<String>() {
@Override
public 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());
spliterator
- 该方法签名: Spliterator<E> spliterator();
- Spliterator既可以像Iterator那样逐个迭代,也可以批量迭代,批量迭代可以降低迭代的开销
- Spliterator是可拆分的,一个Spliterator可以通过调用Spliterator<T> trySplit() 方法来尝试分成两个.一个是this, 一个是新返回的元素.这两个迭代器代表的元素没有重叠
- 可通过多次调用Spliterator.trySplit() 方法来分解负载,以便于多线程处理
stream和parallStream
- Stream() 和parallStream() 分别返回该容器的Stream视图表示
- parallStream() 返回并行的Stream
- Stream是Java函数式编程的核心类
Map
forEach
- 该方法签名: void forEach(BiConsumer<? super K,? super V> action);
- 对Map中的每个映射执行action操作
- BiConsumer是一个函数接口,里面有一个待实现方法 void accept(T t, U u);
- 对Map中的每个映射执行action操作
- 使用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>() {
@Override
public 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));
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"));
putIfAbsent
- 该方法签名: V putIfAbsent(K key, V value);
- 只有在不存在key值的映射或映射值为null时,才将value指定的值放入到Map中,否则不对Map做修改
- 该方法将判断和赋值合二为一,使用起来更加方便
remove
- 该方法签名: remove(Object key);
- 根据指定的key值删除Map中映射关系
- 该方法签名: remove(Object key, Object value);
- 只有在当前Map中key正好映射到value时才删除该映射
replace
- 该方法签名: replace(K key, V value);
- 只有在当前Map中key的映射存在时才用value去替换原来的值
- 该方法签名: replace(K key, V oldValue, V newValue);
- 只有在当前Map中key的映射存在且等于oldValue时,才用newValue去替换原来的值,否则不做任何操作
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>(){
@Override
public 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());
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()方法语义复杂,但使用的方式明确,经典的使用场景: 将新的错误信息拼接到原来的信息上:
map.merge(key, newMsg, (v1, v2) -> v1 + v2);
compute
- 该方法签名: compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction);
- 将remappingFunction计算的结果关联到key上,如果计算结果为null, 则在Map中删除key的映射
- 使用compute实现将新的错误信息拼接到原来的信息上:
map.compute(key, (k, v) -> v == null ? newMsg : v.concat(newMsg));
computeIfAbsent
- 该方法签名: V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction);
- 只有在当前Map中不存在key值的映射或映射值为null时,才调用mappingFunction, 并在mappingFunction执行结果非null时,将结果跟key关联
- Function是一个函数接口,里面有待实现方法R apply(T t)
- computeIfAbsent() 常用来对Map的某个key值建立初始化映射.比如在实现一个多值映射时 ,Map的定义可能是Map< K, Set< V > >, 要向Map中插入新值:
Map<Integer, Set<String>> map = new HashMap<>();
if (map.containsKey(1)) {
map.get(1).add("one");
} else {
Set<String> valueSet = new HashSet<String>();
valueSet.add("one");
map.put(1, valueSet);
}
- 使用Lambda表达式实现:
Map<Integer, Set<String>> map = new HashMap<>();
map.computeIfAbsent(1, v -> new HashSet<String>()).add("one");
computeIfPresent
- 该方法签名: V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction);
- 作用与computeIfAbsent() 相反
- 只有当前Map中存在key值的映射且非null时,才调用remappingFunction, 如果remappingFunction执行结果为null, 则删除key的映射,否则使用该结果替换key原来的映射
- Java7之前的等效代码:
if (map.get(key) != null) {
V oldValue = map.get(key);
V newValue = remappingFunction.apply(key, oldValue);
if (newValue !=null) {
map.put(key, newValue);
} else {
map.remove(key);
}
return newValue;
}
return null;
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)