本想着自己总结记录汇总下这些新方法的,结果一搜,一点,乖乖,真的是太详细了,然后我就搬过来了。。原文地址
其中当然也涉及Lambda表达式,简化代码,何乐不为。
Collection
forEach()
该方法的签名为void forEach(Consumer<? super E> action)
,作用是对容器中的每个元素执行action
指定的动作,其中Consumer
是个函数接口,里面只有一个待实现方法void accept(T t)
(后面我们会看到,这个方法叫什么不重要,甚至不需要记住它的名字)。
需求:假设有一个字符串列表,需要打印出其中所有长度大于3的字符串
Java7及以前我们可以用增强的for循环实现:
1 | // 使用曾强for循环迭代 |
现在使用forEach()方法结合匿名内部类,可以这样实现:
1 | // 使用forEach()结合匿名内部类迭代 |
上述代码调用forEach()
方法,并使用匿名内部类实现Comsumer
接口。到目前为止我们没看到这种设计有什么好处,但是不要忘记Lambda表达式,使用Lambda表达式实现如下:
1 | // 使用forEach()结合Lambda表达式迭代 |
上述代码给forEach()
方法传入一个Lambda表达式,我们不需要知道accept()
方法,也不需要知道Consumer
接口,类型推导帮我们做了一切。
removeIf()
该方法签名为boolean removeIf(Predicate<? super E> filter)
,作用是删除容器中所有满足filter
指定条件的元素,其中Perdicate
是一个函数接口,里面只有一个待实现方法boolean test(T t)
,同样的这个方法的名字根本不重要,因为用的时候不需要书写这个名字。
需求:假设有一个字符串列表,需要删除其中所有长度大于3的字符串。
我们知道如果需要在迭代过程中对容器进行删除操作必须使用迭代器,否则会抛出ConcurrentModificationException
,所以上述任务传统的写法是:
1 | // 使用迭代器删除列表元素 |
现在使用removeIf()
方法结合匿名内部类,我们可以这样实现:
1 | // 使用removeIf()结合匿名名内部类实现 |
上述代码使用removeIf()
方法,并使用匿名内部类实现Precicate
接口。相信你已经想到用Lambda表达式该怎么写了:
1 | // 使用removeIf()结合Lambda表达式实现 |
使用Lambda表达式不需要记住Predicate
接口名,也不需要记住test()
方法名,只需要知道此处需要返回一个boolean类型的Lambda表达式就行了。
replaceAll()
该方法签名为void replaceAll(UnaryOperator<E> operator)
,作用是对每个元素执行operator
指定的操作,并用操作结果来替换原来的元素。其中UnaryOperator
是一个函数接口,里面只有一个待实现函数T apply(T t)
。
需求:假设有一个字符串列表,将其中所有长度大于3的元素转换成大写,其余元素不变。
Java7及之前似乎没有优雅的办法:
1 | // 使用下标实现元素替换 |
使用replaceAll()
方法结合匿名内部类可以实现如下:
1 | // 使用匿名内部类实现 |
上述代码调用replaceAll()
方法,并使用匿名内部类实现UnaryOperator
接口。我们知道可以用更为简洁的Lambda表达式实现:
1 | // 使用Lambda表达式实现 |
sort()
该方法定义在List
接口中,方法签名为void sort(Comparator<? super E> c)
,该方法根据c
指定的比较规则对容器元素进行排序。Comparator
接口我们并不陌生,其中有一个方法int compare(T o1, To2)
需要实现,显然该接口是个函数接口。
需求:假设有一个字符串列表,按照字符串长度增序对元素排序。
由于Java7以及之前sort()
方法在Collections
工具类,所以代码要这样写:
1 | // Collections.sort()方法 |
现在可以直接使用List.sort()
方法,结合Lambda表达式,可以这样写:
1 | // List.sort()方法结合Lambda表达式 |
spliterator()
方法签名为Spliterator<E> spliterator()
,该方法返回容器的可拆分迭代器。从名字来看该方法跟iterator()
方法有点像,我们知道Iterator
是用来迭代容器的,Spliterator
也有类似作用,但二者有如下不同:
Spliterator
即可以像Iterator
那样逐个迭代,也可以批量迭代。批量迭代可以降低迭代的开销。Spliterator
是可以拆分的,一个Spliterator
可以通过调用Spliterator<T> trySplit()
方法来尝试分成两个。一个是this
,另一个是新返回的那个,这两个迭代器代表的元素没有重叠。
可通过(多次)调用Spliterator.trySplit()
方法来分解负载,以便多线程处理。
stream() / parallelStream()
stream()
和parallelStream()
分别返回容器的stream
视图表示,不同之处在于parallelStream()
返回并行的Stream
。**Stream
是Java函数式编程的核心类,我之前也有文章详细介绍过**。
Map
相比Collection
,Map
中加入了更多的方法,我们以HashMap
为例来逐一探秘。
forEach()
该方法签名为void forEach(BiConsumer<? super K, ? super V> action)
,作用是对Map
中每个映射执行action
指定的操作,其中BiConsumer
是一个函数接口,里面有一个待实现方法void accept(T t, U u)
。BinConsumer
接口名字和accept()
方法名字都不重要,不须记住。
需求:假设有一个数字到对应英文单词的Map,请输出Map中的所有映射关系
Java7以及之前经典的代码如下:
1 | // Java7以及之前迭代Map |
使用Map.forEach()
方法,结合匿名内部类,实现如下:
1 | // 使用forEach()结合匿名内部类迭代Map |
上述代码调用forEach()
方法,并使用匿名内部类实现BiConsumer
接口。当然,实际场景中没人使用匿名内部类写法,因为有Lambda表达式:
1 | // 使用forEach()结合Lambda表达式迭代Map |
getOrDefault()
该方法跟Lambda表达式没关系,但是很有用。方法签名为V getOrDefault(Object key, V defaultValue)
,作用是**按照给定的key
查询Map
中对应的value
,如果没有找到则返回defaultValue
**。使用该方法程序员可以省去查询指定键值是否存在的麻烦。
需求:假设有一个数字到对应英文单词的Map,输出4对应的英文单词,如果不存在则输出NoValue
1 | // 查询Map中指定的值,不存在时使用默认值 |
putIfAbsent()
该方法跟Lambda表达式没关系,但是很有用。方法签名为V putIfAbsent(K key, V value)
,作用是只有在不存在key
值的映射或映射值为null时,才将value
指定的值放入到Map
中,否则不对Map
做更改。该方法将条件判断和赋值合二为一,使用起来更加方便。
remove()
我们都知道Map
中有一个remove(Object key)
方法,来根据指定key
值删除Map
中的映射关系;Java8新增了remove(Object key, Object value)
方法,只有在当前Map
中**key
正好映射到value
时**才删除该映射,否则什么也不做。
replace()
在Java7及以前,要想替换Map
中的映射关系可通过put(K key, V value)
方法来实现,该方法总是会用新值替换原来的值,为了更精确的控制替换行为,Java8在Map
中加入了两个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)
,不要被如此多的函数接口吓到,因为使用的时候根本不需要知道他们的名字。
需求:假设有一个数字到对应英文单词的Map,请将原来映射关系中的单词都转换成大写
Java7以及之前经典的代码如下:
1 | // Java7以及之前替换所有Map中所有映射关系 |
使用replaceAll()
方法结合匿名内部类,实现如下:
1 | // 使用replaceAll()结合匿名内部类实现 |
上述代码调用replaceAll()
方法,并使用匿名内部类实现BiFunction
接口。更进一步的,使用Lambda表达式实现如下:
1 | // 使用replaceAll()结合Lambda表达式实现 |
简洁到让人难以置信。
merge()
该方法签名为merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)
,作用是:
- 如果
Map
中key
对应的映射不存在或者为null
,则将value
(不能是null
)关联到key
上; - 否则执行
remappingFunction
,如果执行结果非null
则用该结果跟key
关联,否则在Map
中删除key
的映射。
参数中BiFunction
函数接口前面已经介绍过,里面有一个待实现方法R apply(T t, U u)
。merge()
方法虽然语义有些复杂,但该方法的用法很明确,一个比较常见的场景是将新的错误信息拼接到原来的信息上,比如:
1 | map.merge(key, newMeg, (v1, v2) -> v1 + v2); |
compute()
该方法签名为compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
,作用是把remappingFunction
的计算结果关联到key
上,如果计算结果为null
,则在Map
中删除key
的映射。
要实现上述merge()
方法中错误信息拼接的例子,使用compute()
代码如下:
1 | 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
中放入新值,可通过如下代码实现:
1 | Map<Integer, Set<String>> map = new HashMap<>(); |
使用computeIfAbsent()
将条件判断和添加操作合二为一,使代码更简洁。
computeIfPresent()
该方法签名为V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
,作用跟computeIfAbsent()
相反,即,只有在当前Map
中存在key
值的映射且非null
时,才调用remappingFunction
,如果remappingFunction
执行结果为null
,则删除key
的映射,否则使用该结果替换key
原来的映射。
这个函数的功能跟如下代码是等效的:
1 | // Java7及以前跟computeIfPresent()等效的代码 |
End.