Java8 带附加信息的计数收集器
Java8 counting collector with additional information
我正在使用 java 8 的计数收集器来获取有关值计数的信息。
例如;
如果我有一堆像
这样的流
Stream<String> doc1 = Stream.of("a", "b", "c", "b", "c");
Stream<String> doc2 = Stream.of("b", "c", "d");
Stream<Stream<String>> docs = Stream.of(doc1, doc2);
我可以计算文档中每个单词的出现次数
List<Map<String, Long>> collect = docs
.map(doc -> doc.collect(Collectors.groupingBy(Function.identity(), Collectors.counting())))
.collect(Collectors.toList());
这导致结构为
[
{a=1, b=2, c=2},
{b=1, c=1, d=1}
]
但是,我希望将计数与其来源的 docId 相关联。例如我想要一个结构
[
{a=(randId1, 1), b=(randId1, 2), c=(randId1, 2)},
{b=(randId2, 1), c=(randId2, 1), d=(randId2, 1)}
]
其中 randId1
和 randId2
可以在运行时生成(我只需要一种方法可以追溯到唯一的来源),而 ()
代表一个 Pair class 来自 Apache。
我试图将文档包装在 (docId, doc)
的 Pair
中,但我坚持修改 Collectors.counting()
替换
List<Map<String, Long>> collect = docs.map(doc -> Pair.of(UUID.randomUUID(), doc))
.map(p -> p.getRight().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())))
.collect(Collectors.toList());
如何获得所需格式的输出?
这不是很可读...我用 AbstractMap.SimpleEntry
替换了 Pair
因为它做同样的事情而且我已经在我的类路径中了。
List<Map<String, AbstractMap.SimpleEntry<Long, UUID>>> result = docs.map(doc -> doc.collect(Collectors.collectingAndThen(
Collectors.groupingBy(Function.identity(), Collectors.counting()),
map -> {
UUID rand = UUID.randomUUID();
return map.entrySet().stream().collect(Collectors.toMap(
Entry::getKey,
e -> new AbstractMap.SimpleEntry<>(e.getValue(), rand)));
})))
.collect(Collectors.toList());
System.out.println(result);
而这个的输出:
[{a=1=890d7276-efb7-41cc-bda7-f2dd2859e740,
b=2=890d7276-efb7-41cc-bda7-f2dd2859e740,
c=2=890d7276-efb7-41cc-bda7-f2dd2859e740},
{b=1=888d78a5-0dea-4cb2-8686-c06c784d4c66,
c=1=888d78a5-0dea-4cb2-8686-c06c784d4c66,
d=1=888d78a5-0dea-4cb2-8686-c06c784d4c66}]
我想你可以这样做:
List<Map<String, Pair<UUID, Long>>> result = docs
.map(doc -> Pair.of(UUID.randomUUID(), doc))
.map(p -> p.getRight() // right: doc stream
.map(word -> Pair.of(word, p.getLeft()))) // left: uuid
.map(stream -> stream.collect(Collectors.toMap(
Pair::getLeft, // word
p -> Pair.of(p.getRight(), 1L), // right: uuid
(p1, p2) -> Pair.of(p1.getLeft(), p1.getRight() + p2.getRight())))) // merge
.collect(Collectors.toList());
我已经多次使用 Pair.of
来传递单词和随机文档 ID。最后,我将 Collectors.toMap
与一个函数结合使用,以便在键发生冲突时合并值。结果完全如你所愿,即:
[{a=(fa843dec-3e02-4811-b34f-79949340b4c5,1),
b=(fa843dec-3e02-4811-b34f-79949340b4c5,2),
c=(fa843dec-3e02-4811-b34f-79949340b4c5,2)},
{b=(dc2ad8c7-298a-433e-8b27-88bd3c8eaebb,1),
c=(dc2ad8c7-298a-433e-8b27-88bd3c8eaebb,1),
d=(dc2ad8c7-298a-433e-8b27-88bd3c8eaebb,1)}]
也许这可以通过将收集内部流的代码移至辅助方法来改进:
private Map<String, Pair<UUID, Long>> collectInnerDoc(
Stream<Pair<String, UUID>> stream) {
return stream.collect(Collectors.toMap(
Pair::getLeft, // word
p -> Pair.of(p.getRight(), 1L), // random doc id
(p1, p2) -> Pair.of(p1.getLeft(), p1.getRight() + p2.getRight()))); // merge
}
然后您可以使用此方法收集外部流:
List<Map<String, Pair<UUID, Long>>> result = docs
.map(doc -> Pair.of(UUID.randomUUID(), doc))
.map(p -> p.getRight() // right: doc stream
.map(word -> Pair.of(word, p.getLeft()))) // left: uuid
.map(this::collectInnerDoc) // map inner stream to map
.collect(Collectors.toList());
这假定私有方法是在您正在收集外部流的同一个 class 中声明的。如果不是这种情况,请相应地更改 this::collectInnerDocs
方法参考。
这个怎么样?
List<Map<String, Pair<UUID, Long>>> collect = docs.map(doc -> {
UUID id = UUID.randomUUID();
return doc.collect(groupingBy(
identity(),
// v--- adapting Collector<?,?,Long> to Collector<?,?,Pair>
collectingAndThen(counting(), n -> Pair.of(id, n))
));
}).collect(Collectors.toList());
我只是复制你的代码片段,并通过 Collectors#collectingAndThen:
将你最后一个通用参数 Long
调整为 Pair
// v--- the code need to edit is here
List<Map<String, Long>> collect = docs
.map(doc -> doc.collect(Collectors.groupingBy(Function.identity()
// the code need to edit is here ---v
,Collectors.counting())))
.collect(Collectors.toList());
我正在使用 java 8 的计数收集器来获取有关值计数的信息。
例如; 如果我有一堆像
这样的流Stream<String> doc1 = Stream.of("a", "b", "c", "b", "c");
Stream<String> doc2 = Stream.of("b", "c", "d");
Stream<Stream<String>> docs = Stream.of(doc1, doc2);
我可以计算文档中每个单词的出现次数
List<Map<String, Long>> collect = docs
.map(doc -> doc.collect(Collectors.groupingBy(Function.identity(), Collectors.counting())))
.collect(Collectors.toList());
这导致结构为
[
{a=1, b=2, c=2},
{b=1, c=1, d=1}
]
但是,我希望将计数与其来源的 docId 相关联。例如我想要一个结构
[
{a=(randId1, 1), b=(randId1, 2), c=(randId1, 2)},
{b=(randId2, 1), c=(randId2, 1), d=(randId2, 1)}
]
其中 randId1
和 randId2
可以在运行时生成(我只需要一种方法可以追溯到唯一的来源),而 ()
代表一个 Pair class 来自 Apache。
我试图将文档包装在 (docId, doc)
的 Pair
中,但我坚持修改 Collectors.counting()
替换
List<Map<String, Long>> collect = docs.map(doc -> Pair.of(UUID.randomUUID(), doc))
.map(p -> p.getRight().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())))
.collect(Collectors.toList());
如何获得所需格式的输出?
这不是很可读...我用 AbstractMap.SimpleEntry
替换了 Pair
因为它做同样的事情而且我已经在我的类路径中了。
List<Map<String, AbstractMap.SimpleEntry<Long, UUID>>> result = docs.map(doc -> doc.collect(Collectors.collectingAndThen(
Collectors.groupingBy(Function.identity(), Collectors.counting()),
map -> {
UUID rand = UUID.randomUUID();
return map.entrySet().stream().collect(Collectors.toMap(
Entry::getKey,
e -> new AbstractMap.SimpleEntry<>(e.getValue(), rand)));
})))
.collect(Collectors.toList());
System.out.println(result);
而这个的输出:
[{a=1=890d7276-efb7-41cc-bda7-f2dd2859e740,
b=2=890d7276-efb7-41cc-bda7-f2dd2859e740,
c=2=890d7276-efb7-41cc-bda7-f2dd2859e740},
{b=1=888d78a5-0dea-4cb2-8686-c06c784d4c66,
c=1=888d78a5-0dea-4cb2-8686-c06c784d4c66,
d=1=888d78a5-0dea-4cb2-8686-c06c784d4c66}]
我想你可以这样做:
List<Map<String, Pair<UUID, Long>>> result = docs
.map(doc -> Pair.of(UUID.randomUUID(), doc))
.map(p -> p.getRight() // right: doc stream
.map(word -> Pair.of(word, p.getLeft()))) // left: uuid
.map(stream -> stream.collect(Collectors.toMap(
Pair::getLeft, // word
p -> Pair.of(p.getRight(), 1L), // right: uuid
(p1, p2) -> Pair.of(p1.getLeft(), p1.getRight() + p2.getRight())))) // merge
.collect(Collectors.toList());
我已经多次使用 Pair.of
来传递单词和随机文档 ID。最后,我将 Collectors.toMap
与一个函数结合使用,以便在键发生冲突时合并值。结果完全如你所愿,即:
[{a=(fa843dec-3e02-4811-b34f-79949340b4c5,1),
b=(fa843dec-3e02-4811-b34f-79949340b4c5,2),
c=(fa843dec-3e02-4811-b34f-79949340b4c5,2)},
{b=(dc2ad8c7-298a-433e-8b27-88bd3c8eaebb,1),
c=(dc2ad8c7-298a-433e-8b27-88bd3c8eaebb,1),
d=(dc2ad8c7-298a-433e-8b27-88bd3c8eaebb,1)}]
也许这可以通过将收集内部流的代码移至辅助方法来改进:
private Map<String, Pair<UUID, Long>> collectInnerDoc(
Stream<Pair<String, UUID>> stream) {
return stream.collect(Collectors.toMap(
Pair::getLeft, // word
p -> Pair.of(p.getRight(), 1L), // random doc id
(p1, p2) -> Pair.of(p1.getLeft(), p1.getRight() + p2.getRight()))); // merge
}
然后您可以使用此方法收集外部流:
List<Map<String, Pair<UUID, Long>>> result = docs
.map(doc -> Pair.of(UUID.randomUUID(), doc))
.map(p -> p.getRight() // right: doc stream
.map(word -> Pair.of(word, p.getLeft()))) // left: uuid
.map(this::collectInnerDoc) // map inner stream to map
.collect(Collectors.toList());
这假定私有方法是在您正在收集外部流的同一个 class 中声明的。如果不是这种情况,请相应地更改 this::collectInnerDocs
方法参考。
这个怎么样?
List<Map<String, Pair<UUID, Long>>> collect = docs.map(doc -> {
UUID id = UUID.randomUUID();
return doc.collect(groupingBy(
identity(),
// v--- adapting Collector<?,?,Long> to Collector<?,?,Pair>
collectingAndThen(counting(), n -> Pair.of(id, n))
));
}).collect(Collectors.toList());
我只是复制你的代码片段,并通过 Collectors#collectingAndThen:
将你最后一个通用参数Long
调整为 Pair
// v--- the code need to edit is here
List<Map<String, Long>> collect = docs
.map(doc -> doc.collect(Collectors.groupingBy(Function.identity()
// the code need to edit is here ---v
,Collectors.counting())))
.collect(Collectors.toList());