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();