通常,我认为Groovy的inject
方法等同于Java8的reduce
,但我似乎遇到了一种不寻常的情况。
假设我有一个名为Book
的POJO (或POGO
class Book {
int id
String name
}
如果我有一个书的集合,并且希望将它们转换为一个映射,其中键是it,值是书,那么在Groovy中编写就足够简单了:
Map bookMap = books.inject([:]) { map, b ->
map[b.id] = b
map
}
例如,对于每本书,将其添加到地图中的图书id下,并返回地图。
在Java8中,相同的操作将采用完全不同的方法。这两种情况:
Map<Integer, Book> bookMap = books.stream()
.collect(Collectors.toMap(Book::getId, b -> b));
或者,等同地,
bookMap = books.stream()
.collect(Collectors.toMap(Book::getId, Function.identity()));
区别在于风格的问题。
然而,我想知道的是,Java8中是否有类似于Groovy中的inject
的reduce
操作。我不能简单地模仿我在Groovy中所做的事情,因为在Java8中,reduce
的签名是:
T reduce(T identity, BinaryOperator<T> accumulator)
BinaryOperator
意味着lambda表达式的两个元素必须属于同一类型。如果它是一个BiFunction
,我可以将lambda的第一个参数设为HashMap<Integer, Book>
,将第二个参数设为Book
,但我不能用BinaryOperator
做到这一点。我知道reduce
有一个有三个参数的版本,但这似乎也没有帮助。
我是不是漏掉了什么明显的东西?仅仅是因为inject
比reduce
更通用吗?因为我已经有了用Java解决这个问题的惯用方法,所以这并不重要,但这里的差异给我留下了深刻的印象。
发布于 2016-09-18 01:05:45
Yo Ken!:-D
您需要3 parameter form of reduce
,因此给定:
List<Book> books = Arrays.asList(
new Book(1, "Book One"),
new Book(2, "Tim's memoirs"),
new Book(3, "Harry Potter and the sarcastic cat")
);
您可以执行以下操作:
Map<Integer, Book> reduce = books.stream().reduce(
new HashMap<Integer, Book>(),
(map, value) -> {
map.put(value.id, value);
return map;
},
(a, b) -> {
a.putAll(b);
return a;
}
);
给予:
{
1=Book{id=1, name='Book One'},
2=Book{id=2, name='Tim's memoirs'},
3=Book{id=3, name='Harry Potter and the sarcastic cat'}
}
第一个参数是要收集的内容:
new HashMap<Integer, Book>(),
第二个参数是一个BiFunction,它接受当前累加器和流中的下一个元素,并以某种方式组合它们:
(map, value) -> {
map.put(value.id, value);
return map;
},
reduce调用中第三个二元运算符:
(a, b) -> {
a.putAll(b);
return a;
}
是如何将所有结果映射连接在一起,假设您正在运行并行流...
put
和putAll
returning void
把它弄得一团糟:-(但我猜链接在90年代末还不是一件流行的事情……
https://stackoverflow.com/questions/39549168
复制相似问题