Skip to content
Advertisement

Issues understanding mapMulti

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

Then 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;
    }
}

My issue is : 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, eventhough 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.

Thanks in advance, and I hope I’ll understand this kind of subjects that I find very interesting, especially the way all those API are developped.

Advertisement

Answer

So, as @Thomas Kl├Ąger pointed out in the comments

Element.accept() is an instance method. To call it, you need two objects: an Element instance and a Consumer consumer. Method references are smart enough to detect this as a BiConsumer<Element, Consumer consumer

So

elts.stream().<Integer>mapMulti((elt,cons)->{
            elt.accept(cons);
        })

And

elts.stream().<Integer>mapMulti(Element::accept)

Are the same thing

Thanks a lot !

6 People found this is helpful
Advertisement