我想找到一种简单的方法来用Java 8流式传输Map 。猜猜是什么? 没有!
为了方便起见,我期望的是以下方法:
public interface Map<K, V> {
default Stream<Entry<K, V>> stream() {
return entrySet().stream();
}
}
但是没有这种方法。 不应使用这种方法的原因可能多种多样,例如:
- 没有选择
keySet()
或values()
作为流源的entrySet()
“明确”首选项 -
Map
并不是真正的收藏。 它甚至都不是可Iterable
- 那不是设计目标
- EG没有足够的时间
好吧,对于Map
进行改型以提供entrySet().stream()
并最终实现Iterable<Entry<K, V>>
,这是一个非常令人信服的理由。 那就是我们现在有了Map.forEach()
的事实:
default void forEach(
BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
}
在这种情况下, forEach()
接受BiConsumer
,该BiConsumer
实际上消耗了映射中的条目。 如果您搜索JDK源代码,则Map.forEach()
之外的BiConsumer
类型的引用实际上很少,也许还有几个CompletableFuture
方法和几个流收集方法。
因此,几乎可以假定BiConsumer
受到此forEach()
方法的强烈驱动,这对于使Map.Entry
在整个collection API中成为更重要的类型是一个很好的情况(我们更倾向于使用Tuple2类型,课程)。
让我们继续这种思路。 还有Iterable.forEach()
:
public interface Iterable<T> {
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
}
尽管有细微的差别,但Map.forEach()
和Iterable.forEach()
直观地迭代了各自收集模型的“条目”。
-
Iterable.forEach()
期望Consumer
获得单个值 -
Map.forEach()
期望BiConsumer
两个值:键和值( 不是Map.Entry
!)
这样考虑:
这使得这两种方法在“鸭子键入意义上”不兼容,这使得这两种类型更加不同
mm!
用jOOλ改善地图
我们发现这很古怪且违反直觉。 实际上, forEach()
并不是Map
遍历和转换的唯一用例。 我们希望有一个Stream<Entry<K, V>>
甚至更好的是Stream<Tuple2<T1, T2>>
。 因此,我们在jOOλ中实现了该功能 ,这是我们为jOOQ的集成测试开发的库。 使用jOOλ,您现在可以将Map
封装为Seq
类型(“ Seq”表示顺序流,即具有更多功能特征的流):
Map<Integer, String> map = new LinkedHashMap<>();
map.put(1, "a");
map.put(2, "b");
map.put(3, "c");
assertEquals(
Arrays.asList(
tuple(1, "a"),
tuple(2, "b"),
tuple(3, "c")
),
Seq.seq(map).toList()
);
你能用它做什么? 如何创建一个新的Map
,一次性交换键和值:
System.out.println(
Seq.seq(map)
.map(Tuple2::swap)
.toMap(Tuple2::v1, Tuple2::v2)
);
System.out.println(
Seq.seq(map)
.toMap(Tuple2::v2, Tuple2::v1)
);
以上两种都会产生:
{a=1, b=2, c=3}
仅作记录,以下是使用标准JDK API交换键和值的方法:
System.out.println(
map.entrySet()
.stream()
.collect(Collectors.toMap(
Map.Entry::getValue,
Map.Entry::getKey
))
);
可以做到,但是每天标准Java API的冗长性使事情变得很难读/写。
翻译自: https://www.javacodegeeks.com/2014/10/lets-stream-a-map-in-java-8-with-jooλ.html