鉴于项目与属性的列表,我试图让出现与上述财产的最大值的最后一个项目。
例如,对于对象名单如下:
t i
A: 3
D: 7 *
F: 4
C: 5
X: 7 *
M: 6
我可以得到最高i
的事情之一:
Thing t = items.stream()
.max(Comparator.comparingLong(Thing::getI))
.orElse(null);
然而,这将让我Thing t = D
。有越来越在这种情况下的最后一个项目,即X
的干净和优雅的方式?
一个可能的解决方案是使用reduce
功能。但是,属性计算的飞行,它看起来更像是:
Thing t = items.stream()
.reduce((left, right) -> {
long leftValue = valueFunction.apply(left);
long rightValue = valueFunction.apply(right);
return leftValue > rightValue ? left : right;
})
.orElse(null);
该valueFunction
现在需要将近一倍,而调用。
其他明显的迂回的解决方案是:
删除等号选项(不返回0,如果比较的数字是相等的,返回-1代替)从比较(即写自己的比较,不包括等于选项):
Thing t = items.stream()
.max((a, b) -> a.getI() > b.getI() ? 1 : -1)
.orElse(null);
从概念上讲,你似乎是可能寻找类似使用列表中的元素的thenComparing
index
:
Thing t = items.stream()
.max(Comparator.comparingLong(Thing::getI).thenComparing(items::indexOf))
.orElse(null);
为了避免valueFunction的多个应用程序在你的缩短解决方案,只需明确计算结果,并把它放在一个元组:
Item lastMax = items.stream()
.map(item -> new AbstractMap.SimpleEntry<Item, Long>(item, valueFunction.apply(item)))
.reduce((l, r) -> l.getValue() > r.getValue() ? l : r )
.map(Map.Entry::getKey)
.orElse(null);
如果你在两个步骤做的事情流是没有必要的坏:
1)发现在i
有多个实例Iterable
值(像你一样)
2)通过从物品的端部开始搜索此i
值的最后一个元素:
Thing t =
items.stream()
.max(Comparator.comparingLong(Thing::getI))
.mapping(firstMaxThing ->
return
IntStream.rangeClosed(1, items.size())
.mapToObj(i -> items.get(items.size()-i))
.filter(item -> item.getI() == firstMaxThing.getI())
.findFirst().get();
// here get() cannot fail as *max()* returned something.
)
.orElse(null)
该valueFunction现在需要将近一倍,而调用。
请注意,使用max
即使,在getI
方法将被一次又一次地呼吁每一个比较,而不仅仅是每个元素一次。在您的例子,它被称为11次,其中6次为d,而对于较长的列表,也似乎在每个单元的平均两次被调用。
怎么样,你只需直接缓存在Thing
情况下的计算值?如果这是不可能的,你可以使用一个外部Map
和使用calculateIfAbsent
计算值仅一次为每个Thing
,然后使用reduce
用你的方法。
Map<Thing, Long> cache = new HashMap<>();
Thing x = items.stream()
.reduce((left, right) -> {
long leftValue = cache.computeIfAbsent(left, Thing::getI);
long rightValue = cache.computeIfAbsent(right, Thing::getI);
return leftValue > rightValue ? left : right;
})
.orElse(null);
还是有点清洁,事先计算所有的值:
Map<Thing, Long> cache = items.stream()
.collect(Collectors.toMap(x -> x, Thing::getI));
Thing x = items.stream()
.reduce((left, right) -> cache.get(left) > cache.get(right) ? left : right)
.orElse(null);
你仍然可以使用的减少让这件事完成。如果T1较大,那么只有它将保持T1。在所有其他情况下,将保持T2。如果任T2大于或T1和T2是相同的,那么它最终会回归T2秉承您的要求。
Thing t = items.stream().
reduce((t1, t2) -> t1.getI() > t2.getI() ? t1 : t2)
.orElse(null);
您使用reduce
当前的实现看起来不错,除非你的价值提取功能是昂贵的。
后来考虑到可能要重复使用不同的对象类型和领域的逻辑,我想提取逻辑本身在不同的泛型方法/方法:
public static <T, K, V> Function<T, Map.Entry<K, V>> toEntry(Function<T, K> keyFunc, Function<T, V> valueFunc){
return t -> new AbstractMap.SimpleEntry<>(keyFunc.apply(t), valueFunc.apply(t));
}
public static <ITEM, FIELD extends Comparable<FIELD>> Optional<ITEM> maxBy(Function<ITEM, FIELD> extractor, Collection<ITEM> items) {
return items.stream()
.map(toEntry(identity(), extractor))
.max(comparing(Map.Entry::getValue))
.map(Map.Entry::getKey);
}
代码段可使用上述这样的:
Thing maxThing = maxBy(Thing::getField, things).orElse(null);
AnotherThing maxAnotherThing = maxBy(AnotherThing::getAnotherField, anotherThings).orElse(null);