Say I have a list with elements (34, 11, 98, 56, 43)
.
Using Java 8 streams, how do I find the index of the minimum element of the list (e.g. 1 in this case)
Since this is for learning purposes, let's try to find a solution that doesn't just somehow use a stream, but actually works on the stream of our list. We also don't want to assume random access.
So, there are two ways to get a non-trivial result out of a stream: collect
and reduce
. Here is a solution that uses a collector:
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 MIN_INDEX = new Collector() {
@Override
public Supplier supplier() {
return Minimum::new;
}
@Override
public BiConsumer accumulator() {
return Minimum::accept;
}
@Override
public BinaryOperator combiner() {
return Minimum::combine;
}
@Override
public Function finisher() {
return Minimum::getIndex;
}
@Override
public Set characteristics() {
return Collections.emptySet();
}
};
Writing a collectors creates an annoying amount of code, but it can be easily generalized to support any comparable value. Also, calling the collector looks very idiomatic:
List list = Arrays.asList(4,3,7,1,5,2,9);
int minIndex = list.stream().collect(MIN_INDEX);
If we change the accept
and combine
methods to always return a new Minimum
instance (ie. if we make Minimum
immutable), we can also use reduce
:
int minIndex = list.stream().reduce(new Minimum(), Minimum::accept, Minimum::combine).getIndex();
I sense large potential for parallelization in this one.