Details about the collectors.

I looked at the implementation Collectors.toSetunder jdk-8 and pretty much saw the obvious thing:

 public static <T> Collector<T, ?, Set<T>> toSet() {
    return new CollectorImpl<>(
       (Supplier<Set<T>>) HashSet::new, 
       Set::add,
       (left, right) -> { left.addAll(right); return left; }, // combiner
       CH_UNORDERED_ID);

Look at combinerthe moment; this has been discussed until here , but the idea is that a combiner folds from the second argument into the first. And this is obviously happening here.

But then I looked at the implementation jdk-9and saw this:

 public static <T> Collector<T, ?, Set<T>> toSet() {
    return new CollectorImpl<>(
       (Supplier<Set<T>>) HashSet::new, 
       Set::add,
       (left, right) -> {
          if (left.size() < right.size()) {
            right.addAll(left); return right;
          } else {
             left.addAll(right); return left;
          }
       },
       CH_UNORDERED_ID);

Now why this is happening is a little obvious - less elements to a bigger Set, then the other way aroundit takes less time to add . But is it really cheaper than usual addAll, consider the extra overhead for the branch?

It also violates my law of always folding left ...

Can someone shed some light here?

+6
1

Collector left right , , Collector, .

:

, . .

List , left.addAll(right) right.addAll(left), Set . toSet() UNORDERED, Stream ( ), left right, , , , , , (Java 8s ).

, ... add, , ...

+10

Source: https://habr.com/ru/post/1016964/


All Articles