Java8:从列表中查找最小值的索引
假设我有一个包含元素的列表Java8:从列表中查找最小值的索引,java,java-8,java-stream,Java,Java 8,Java Stream,假设我有一个包含元素的列表(34、11、98、56、43) 使用Java8流,我如何找到列表中最小元素的索引(例如,在本例中为1) 我知道在Java中使用list.indexOf(Collections.min(list))可以很容易地做到这一点。然而,我正在寻找一个类似Scala的解决方案,我们可以简单地说List(34,11,98,56,43).zipWithIndex.min.\u 2来获得最小值的索引 使用streams或lambda表达式(比如Java8特定的特性)是否可以实现相同的结
(34、11、98、56、43)
使用Java8流,我如何找到列表中最小元素的索引(例如,在本例中为1)
我知道在Java中使用list.indexOf(Collections.min(list))
可以很容易地做到这一点。然而,我正在寻找一个类似Scala的解决方案,我们可以简单地说List(34,11,98,56,43).zipWithIndex.min.\u 2
来获得最小值的索引
使用streams或lambda表达式(比如Java8特定的特性)是否可以实现相同的结果
注意:这只是为了学习。我在使用
集合实用方法方面没有任何问题。您可以这样做:
int indexMin = IntStream.range(0, list.size())
.mapToObj(i -> new SimpleEntry<>(i, list.get(i)))
.min(comparingInt(SimpleEntry::getValue))
.map(SimpleEntry::getKey)
.orElse(-1);
int indexMin=IntStream.range(0,list.size())
.mapToObj(i->newsimpleentry(i,list.get(i)))
.min(比较(SimpleEntry::getValue))
.map(SimpleEntry::getKey)
.orElse(-1);
如果列表是随机访问列表,get
是一个固定时间操作。API缺少标准的元组类,因此我使用AbstractMap
类中的SimpleEntry
作为替代
因此,IntStream.range
从列表中生成一个索引流,从中可以将每个索引映射到相应的值。然后通过对值(列表中的值)提供一个比较器来获得最小元素。然后将可选的
映射到一个可选的
,从中可以得到索引(如果可选的为空,则为-1)
顺便说一句,我可能会使用一个简单的for循环来获取最小值的索引,因为您的min
/indexOf
的组合会在列表中传递2次
您可能也有兴趣查看
正如@TagirValeev在中提到的,您可以通过使用IntStream#reduce
而不是Stream#min
来避免装箱,但这样做的代价是模糊意图:
int minIdx = IntStream.range(0,list.size())
.reduce((i,j) -> list.get(i) > list.get(j) ? j : i)
.getAsInt(); // or throw
以下是使用我的库的两种可能的解决方案:
或:
内部的第二个解决方案非常接近@AlexisC提出的解决方案。第一个可能是最快的,因为它不使用装箱(这是一个reduce操作)
在不使用第三方代码的情况下,@Misha的答案看起来最适合我。因为这是为了学习,让我们尝试找到一种解决方案,它不只是以某种方式使用流,而是在我们列表的流上实际工作。我们也不想假设随机访问
因此,有两种方法可以从流中获得非平凡的结果:collect
和reduce
。是一种使用收集器的解决方案:
class Minimum {
int index = -1;
int range = 0;
int value;
public void accept(int value) {
if (range == 0 || value < this.value) {
index = range;
this.value = value;
}
range++;
}
public Minimum combine(Minimum other) {
if (value > other.value) {
index = range + other.index;
value = other.value;
}
range += other.range;
return this;
}
public int getIndex() {
return index;
}
}
static Collector<Integer, Minimum, Integer> MIN_INDEX = new Collector<Integer, Minimum, Integer>() {
@Override
public Supplier<Minimum> supplier() {
return Minimum::new;
}
@Override
public BiConsumer<Minimum, Integer> accumulator() {
return Minimum::accept;
}
@Override
public BinaryOperator<Minimum> combiner() {
return Minimum::combine;
}
@Override
public Function<Minimum, Integer> finisher() {
return Minimum::getIndex;
}
@Override
public Set<Collector.Characteristics> characteristics() {
return Collections.emptySet();
}
};
我觉得在这一点上并行化的潜力很大。谢谢@Alexis,似乎比简单使用集合
或“Scala”实现要复杂得多。是的,这是一个不错的+1。我关注的是zipWithIndex
案例,但这可能是解决这个问题的惯用方法。我会用orElse(-1)
来代替。这是一个非常有趣的解决方案。首先,我认为它对并行流不起作用,但似乎会起作用。请注意,您可以使用Collector.of(Minimum::new,Minimum::accept,Minimum::combine,Minimum::getIndex)
而不是定义匿名类。这种方法的优点是不要求列表对随机访问友好。请注意,combine
方法应正确处理other
为空的情况Collector
javadoc特别要求将并行友好性作为“身份约束”。它没有说明如何处理这个
为空而其他
不为空的情况,但处理这个问题似乎也很谨慎。@Misha,是的,它有一些小问题,但我喜欢这个想法。顺便说一句,它的工作速度相当快,在一些测试中,它甚至比我的IntStreamEx.minBy
要好。我有一个固定版本,可能会将其包含在我的库中
int idx = IntStreamEx.ofIndices(list).minBy(list::get).getAsInt();
int idx = EntryStream.of(list).minBy(Entry::getValue).get().getKey();
class Minimum {
int index = -1;
int range = 0;
int value;
public void accept(int value) {
if (range == 0 || value < this.value) {
index = range;
this.value = value;
}
range++;
}
public Minimum combine(Minimum other) {
if (value > other.value) {
index = range + other.index;
value = other.value;
}
range += other.range;
return this;
}
public int getIndex() {
return index;
}
}
static Collector<Integer, Minimum, Integer> MIN_INDEX = new Collector<Integer, Minimum, Integer>() {
@Override
public Supplier<Minimum> supplier() {
return Minimum::new;
}
@Override
public BiConsumer<Minimum, Integer> accumulator() {
return Minimum::accept;
}
@Override
public BinaryOperator<Minimum> combiner() {
return Minimum::combine;
}
@Override
public Function<Minimum, Integer> finisher() {
return Minimum::getIndex;
}
@Override
public Set<Collector.Characteristics> characteristics() {
return Collections.emptySet();
}
};
List<Integer> list = Arrays.asList(4,3,7,1,5,2,9);
int minIndex = list.stream().collect(MIN_INDEX);
int minIndex = list.stream().reduce(new Minimum(), Minimum::accept, Minimum::combine).getIndex();