I'm fairly when it comes to Java, and I've decided to dig a little into the implementations and uses of the API, especially the Stream API.
I've made an implementation after thinking I got it right, and it worked. However I realized something that bugged me out.
The mapMulti
function takes in parameter a BiConsumer
:
default <R> Stream<R> mapMulti(BiConsumer<? super T, ? super Consumer<R>> mapper) {
Objects.requireNonNull(mapper);
return flatMap(e -> {
SpinedBuffer<R> buffer = new SpinedBuffer<>();
mapper.accept(e, buffer);
return StreamSupport.stream(buffer.spliterator(), false);
});
}
I wanted to benchmark the mapMulti function by passing it the accept function of my Element class (that's why I discard the value of s
), and the ExecutionPlan
simply has values to benchmark with JMH.
public void mapMultiTest(ExecutionPlan exec){
Stream<Integer> s = exec.elts.stream().mapMulti(Element::accept);
}
Here is the Element class, which simply decomposes an int into prime factors, and calls forEach on the consumer.
public record Element(int value) {
public void accept(Consumer<Integer> consumer) {
decomp().forEach(consumer);
}
public ArrayList<Integer> decomp() {
ArrayList<Integer> list = new ArrayList<>();
int value = this.value;
while (!isPrime(value)) {
int prime = 2;
while (!isPrime(prime) || value % prime != 0)
prime++;
list.add(prime);
value /= prime;
}
list.add(value);
return list;
}
private boolean isPrime(int num) {
if (num <= 1) {
return false;
}
for (int i = 2; i <= Math.sqrt(num); i++) {
if (num % i == 0) {
return false;
}
}
return true;
}
}
Why is my Element::accept
(which is theorically the mapper
arg) considered as valid when it is not of the type BiConsumer
, and takes only one argument, even though when it is called inside mapMulti
, it takes the element and buffer argument.
I may totally be missing something obvious or having a wrong understanding of those kind of functions, but I'm having some troubles understanding BiConsumer
, Consumer
, Functions
, BiFunctions
, etc.
So, as @Thomas Kläger pointed out in the comments:
Element.accept()
is an instance method. To call it, you need two objects: anElement
instance and aConsumer<Integer> consumer
. Method references are smart enough to detect this as aBiConsumer<Element, Consumer<Integer> consumer
So
elts.stream().<Integer>mapMulti((elt,cons)->{
elt.accept(cons);
})
and
elts.stream().<Integer>mapMulti(Element::accept)
are the same thing.