我需要创建某种Key的所有可能组合,它由X(在我的情况下为8)组成,同样重要的元素。所以我想出了这样的代码:
final LinkedList<Key> keys = new LinkedList();
firstElementCreator.getApplicableElements() // All creators return a Set of elements
.forEach( first -> secondElementCreator.getApplicableElements()
.forEach( second -> thirdElementCreator.getApplicableElements()
// ... more creators
.forEach( X -> keys.add( new Key( first, second, third, ..., X ) ) ) ) ) ) ) ) );
return keys;
它正在运行,但有X嵌套forEach,我觉得我错过了一个更简单/更好/更优雅的解决方案。有什么建议? 提前谢谢!
答案 0 :(得分:1)
是Cartesian产品吗?许多库提供API,例如:Guava中的Sets和Lists:
List<ApplicableElements> elementsList = Lists.newArrayList(firstElementCreator, secondElementCreator...).stream()
.map(c -> c.getApplicableElements()).collect(toList());
List<Key> keys = Lists.cartesianProduct(elementsList).stream()
.map(l -> new Key(l.get(0), l.get(1), l.get(2), l.get(3), l.get(4), l.get(5), l.get(6), l.get(7))).collect(toList());
答案 1 :(得分:1)
由于输入集的数量是固定的(它必须与Key构造函数中的参数数量相匹配),因此您的解决方案实际上并不错。
如果没有lambda,它会更有效,更容易阅读,例如:
for (Element first : firstElementCreator.getApplicableElements()) {
for (Element second : secondElementCreator.getApplicableElements()) {
for (Element third : thirdElementCreator.getApplicableElements()) {
keys.add(new Key(first, second, third));
}
}
}
答案 2 :(得分:1)
规范解决方案是使用flatMap
。但是,棘手的部分是从多个输入级别创建Key
对象。
直接的方法是在最里面的函数中进行评估,其中每个值都在范围内
final List<Key> keys = firstElementCreator.getApplicableElements().stream()
.flatMap(first -> secondElementCreator.getApplicableElements().stream()
.flatMap(second -> thirdElementCreator.getApplicableElements().stream()
// ... more creators
.map( X -> new Key( first, second, third, ..., X ) ) ) )
.collect(Collectors.toList());
但很快就会因深度嵌套而变得不切实际
没有深度嵌套的解决方案需要元素来保存中间复合值。例如。如果我们将Key
定义为
class Key {
String[] data;
Key(String... arg) {
data=arg;
}
public Key add(String next) {
int pos = data.length;
String[] newData=Arrays.copyOf(data, pos+1);
newData[pos]=next;
return new Key(newData);
}
@Override
public String toString() {
return "Key("+Arrays.toString(data)+')';
}
}
(假设String
为元素类型),我们可以使用
final List<Key> keys =
firstElementCreator.getApplicableElements().stream().map(Key::new)
.flatMap(e -> secondElementCreator.getApplicableElements().stream().map(e::add))
.flatMap(e -> thirdElementCreator.getApplicableElements().stream().map(e::add))
// ... more creators
.collect(Collectors.toList());
请注意,这些flatMap
步骤现在处于同一级别,即不再嵌套。此外,所有这些步骤都是相同的,只是在实际创建者中有所不同,这导致支持任意数量的Creator
实例的一般解决方案。
List<Key> keys = Stream.of(firstElementCreator, secondElementCreator, thirdElementCreator
/* , and, some, more, if you like */)
.map(creator -> (Function<Key,Stream<Key>>)
key -> creator.getApplicableElements().stream().map(key::add))
.reduce(Stream::of, (f1,f2) -> key -> f1.apply(key).flatMap(f2))
.apply(new Key())
.collect(Collectors.toList());
这里,每个创建者都映射到前一个解决方案的相同流生成函数,然后将所有函数简化为单个函数,将每个函数与flatMap
步骤组合到下一个函数,最后生成函数执行以获取流,然后将其收集到List
。