意外绑定 - 为什么我可以使用此收集器中的t扩展收藏品之类的东西?

发布于 2025-02-12 03:19:26 字数 1640 浏览 0 评论 0原文

嗨,我已经编写了此自定义收集器,用于将列表 elements转换为Multimap

public class MultiMapCollector implements Collector<Entity,
        Map<String, Collection<String>>, Map<String, Collection<String>>> {

    @Override
    public Supplier<Map<String, Collection<String>>> supplier() {
        return HashMap::new;
    }

    @Override
    public BiConsumer<Map<String, Collection<String>>, Entity> accumulator() {
        return (map, e) -> map.computeIfAbsent(e.getName(),
                k -> new HashSet<>()).add(e.getValue());
    }

    @Override
    public BinaryOperator<Map<String, Collection<String>>> combiner() {
        return (map1, map2) -> {
            map2.keySet().forEach(key -> map1.computeIfAbsent(key, k -> new HashSet<>()).addAll(map2.get(key)));
            return map1;
        };
    }

    @Override
    public Function<Map<String, Collection<String>>, Map<String, Collection<String>>> finisher() {
        return Collections::unmodifiableMap;
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Set.of(Characteristics.UNORDERED);
    }
}

现在我希望能够传递list> listSET

因此,代替map&lt; string,collection&lt; string&gt;&gt;我想要类似map&lt&lt; string,v Extends collection&lt extents collection&gt;&gt;&gt;的东西,

但是当我这样做时,我得到了一个模糊的错误说:

Unexpected Bounds

我做错了什么?

我似乎无法将其缠住。

Hi I have written this custom collector for converting a List of elements into a Multimap:

public class MultiMapCollector implements Collector<Entity,
        Map<String, Collection<String>>, Map<String, Collection<String>>> {

    @Override
    public Supplier<Map<String, Collection<String>>> supplier() {
        return HashMap::new;
    }

    @Override
    public BiConsumer<Map<String, Collection<String>>, Entity> accumulator() {
        return (map, e) -> map.computeIfAbsent(e.getName(),
                k -> new HashSet<>()).add(e.getValue());
    }

    @Override
    public BinaryOperator<Map<String, Collection<String>>> combiner() {
        return (map1, map2) -> {
            map2.keySet().forEach(key -> map1.computeIfAbsent(key, k -> new HashSet<>()).addAll(map2.get(key)));
            return map1;
        };
    }

    @Override
    public Function<Map<String, Collection<String>>, Map<String, Collection<String>>> finisher() {
        return Collections::unmodifiableMap;
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Set.of(Characteristics.UNORDERED);
    }
}

Now I want to be able to pass a List or a Set.

So instead of Map<String, Collection<String>> I want something like Map<String, V extends Collection<String>>

But when I do that I get a vague error saying :

Unexpected Bounds

What am I doing wrong ?

I can't seem to wrap my head around this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

柒夜笙歌凉 2025-02-19 03:19:26

您需要在类名称之后添加一个通用类型参数(让我们称其为c),而不是instentements子句。

然后,添加一个构造函数,该构造函数可吸引供应商&lt; c&gt;告诉您如何创建c,并将供应商存储在某个地方。用新的HashSet&lt;()get调用的所有出现。

class MultiMapCollector<C extends Collection<String>> implements Collector<Entity, Map<String, C>, Map<String, C>> {

     private final Supplier<C> supplier;

     public MultiMapCollector(Supplier<C> supplier) {

         this.supplier = supplier;
     }

    @Override
    public Supplier<Map<String, C>> supplier() {
        return HashMap::new;
    }

    @Override
    public BiConsumer<Map<String, C>, Entity> accumulator() {
        return (map, e) -> map.computeIfAbsent(e.getName(),
            k -> supplier.get()).add(e.getValue());
    }

    @Override
    public BinaryOperator<Map<String, C>> combiner() {
        return (map1, map2) -> {
            map2.keySet().forEach(key -> map1.computeIfAbsent(key, k -> supplier.get()).addAll(map2.get(key)));
            return map1;
        };
    }

    @Override
    public Function<Map<String, C>, Map<String, C>> finisher() {
        return Collections::unmodifiableMap;
    }

    @Override
    public Set<Characteristics> characteristics() {
        // now you aren't sure whether the collection is ordered or not
        return Set.of();
    }
}

现在,每次创建MultiMapCollector时,您都需要传递供应商:

new MultiMapCollector<>(HashSet::new)

如果您希望它使用HashSet当您不提供任何内容时使用默认值,请添加静态出厂方法并将构造函数私有化:

 public static MultiMapCollector<Set<String>> get() {
     return new MultiMapCollector<>(HashSet::new);
 }

 public static <C extends Collection<String>> MultiMapCollector<C> with(Supplier<C> supplier) {
     return new MultiMapCollector<>(supplier);
 }

就像collector.groupingby是一种工厂方法一样。

更一般而言,您可以使用完整的下游收集器,而不仅仅是供应商&lt; c&gt;,这就是collectors.groupingby 的2-参数过载。 groupingby只能处理分组,而下游收集器(第二参数)处理组。

这是您将如何做到这一点的草图:

class MultiMapCollector<R> implements Collector<Entity, Map<String, R>, Map<String, R>> {

     private final Collector<String, R, R> downstream;

     private MultiMapCollector(Collector<String, R, R> downstream) {

         this.downstream = downstream;
     }

     public static MultiMapCollector<Set<String>> get() {
         return new MultiMapCollector<>(Collectors.toSet());
     }

     public static <R> MultiMapCollector<R> with(Collector<String, R, R> downstream) {
         return new MultiMapCollector<>(downstream);
     }

    @Override
    public Supplier<Map<String, R>> supplier() {
        return HashMap::new;
    }

    @Override
    public BiConsumer<Map<String, R>, Entity> accumulator() {
        return (map, e) -> downstream.accumulator().accept(map.computeIfAbsent(e.getName(),
            k -> downstream.supplier().get()), e.getValue());
    }

    @Override
    public BinaryOperator<Map<String, R>> combiner() {
        return (map1, map2) -> {
            map2.keySet().forEach(key -> 
                downstream.combiner().apply(
                    map1.computeIfAbsent(key, k -> downstream.supplier().get()),
                    map2.get(key)
                )
            );
            return map1;
        };
    }

    @Override
    public Function<Map<String, R>, Map<String, R>> finisher() {
        return Collections::unmodifiableMap;
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Set.of();
    }
}

You need to add a generic type parameter (let's call it C) after the class name, not in the implements clause.

Then, add a constructor that takes in a Supplier<C> telling you how to create a C, and store the supplier somewhere. Replace all the occurrences of new HashSet<>() with get calls to the supplier instead.

class MultiMapCollector<C extends Collection<String>> implements Collector<Entity, Map<String, C>, Map<String, C>> {

     private final Supplier<C> supplier;

     public MultiMapCollector(Supplier<C> supplier) {

         this.supplier = supplier;
     }

    @Override
    public Supplier<Map<String, C>> supplier() {
        return HashMap::new;
    }

    @Override
    public BiConsumer<Map<String, C>, Entity> accumulator() {
        return (map, e) -> map.computeIfAbsent(e.getName(),
            k -> supplier.get()).add(e.getValue());
    }

    @Override
    public BinaryOperator<Map<String, C>> combiner() {
        return (map1, map2) -> {
            map2.keySet().forEach(key -> map1.computeIfAbsent(key, k -> supplier.get()).addAll(map2.get(key)));
            return map1;
        };
    }

    @Override
    public Function<Map<String, C>, Map<String, C>> finisher() {
        return Collections::unmodifiableMap;
    }

    @Override
    public Set<Characteristics> characteristics() {
        // now you aren't sure whether the collection is ordered or not
        return Set.of();
    }
}

Now every time you create a MultiMapCollector, you need to pass in a supplier:

new MultiMapCollector<>(HashSet::new)

If you want it to use a default of HashSet when you don't provide anything, add static factory methods and make the constructor private:

 public static MultiMapCollector<Set<String>> get() {
     return new MultiMapCollector<>(HashSet::new);
 }

 public static <C extends Collection<String>> MultiMapCollector<C> with(Supplier<C> supplier) {
     return new MultiMapCollector<>(supplier);
 }

Just like how Collectors.groupingBy is a factory method.

More generally, you can take a full downstream collector instead of just a Supplier<C>, and that is what the 2-parameter overload of Collectors.groupingBy does. groupingBy just handles the grouping, and the downstream collector (the 2nd argument) handles the groups.

Here is a sketch of how you would do this:

class MultiMapCollector<R> implements Collector<Entity, Map<String, R>, Map<String, R>> {

     private final Collector<String, R, R> downstream;

     private MultiMapCollector(Collector<String, R, R> downstream) {

         this.downstream = downstream;
     }

     public static MultiMapCollector<Set<String>> get() {
         return new MultiMapCollector<>(Collectors.toSet());
     }

     public static <R> MultiMapCollector<R> with(Collector<String, R, R> downstream) {
         return new MultiMapCollector<>(downstream);
     }

    @Override
    public Supplier<Map<String, R>> supplier() {
        return HashMap::new;
    }

    @Override
    public BiConsumer<Map<String, R>, Entity> accumulator() {
        return (map, e) -> downstream.accumulator().accept(map.computeIfAbsent(e.getName(),
            k -> downstream.supplier().get()), e.getValue());
    }

    @Override
    public BinaryOperator<Map<String, R>> combiner() {
        return (map1, map2) -> {
            map2.keySet().forEach(key -> 
                downstream.combiner().apply(
                    map1.computeIfAbsent(key, k -> downstream.supplier().get()),
                    map2.get(key)
                )
            );
            return map1;
        };
    }

    @Override
    public Function<Map<String, R>, Map<String, R>> finisher() {
        return Collections::unmodifiableMap;
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Set.of();
    }
}
扶醉桌前 2025-02-19 03:19:26

如果要允许使用集合的不同实现&lt; string&gt;用作地图的值,则需要为MultiMapCollector定义类型参数。您还需要修改代码以接受某种对象,例如供应商,知道如何创建特定集合类型的实例。

例如:

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;

public class MultiMapCollector<V extends Collection<String>>
    implements Collector<Entity, Map<String, V>, Map<String, V>> {

  private final Supplier<? extends V> valueSupplier;

  public MultiMapCollector(Supplier<? extends V> valueSupplier) {
    this.valueSupplier = valueSupplier;
  }

  @Override
  public Supplier<Map<String, V>> supplier() {
    return HashMap::new;
  }

  @Override
  public BiConsumer<Map<String, V>, Entity> accumulator() {
    return (map, e) -> map.computeIfAbsent(e.getName(), k -> valueSupplier.get()).add(e.getValue());
  }

  @Override
  public BinaryOperator<Map<String, V>> combiner() {
    return (map1, map2) -> {
      for (var entry : map2.entrySet()) {
        map1.merge(
            entry.getKey(),
            entry.getValue(),
            (curValue, newValue) -> {
              curValue.addAll(newValue);
              return curValue;
            });
      }
      return map1;
    };
  }

  @Override
  public Function<Map<String, V>, Map<String, V>> finisher() {
    return Collections::unmodifiableMap;
  }

  @Override
  public Set<Characteristics> characteristics() {
    return Set.of(Characteristics.UNORDERED);
  }
}

Note MultiMapCollector现在具有类型参数&lt; v扩展了Collection&lt; string&gt;&gt;,并且在代码中其他任何地方collection&collection&lt lt; string&gt;已用v替换。然后,构造函数接受供应商?扩展v&gt;,用于在comgualator()消费者中创建v的实例。

另外,请注意,我更新了您的combiner()功能以使用MAP#MERGE(...),尽管这不是严格必要的。


顺便说一句,如果您要实际查看collectors.groupingby(...)的实现方式,那么您始终可以查看源代码

If you want to allow different implementations of Collection<String> to be used as the values of your maps, then you need to define a type parameter for your MultiMapCollector. You'll also need to modify the code to accept some sort of object, such as a Supplier, that knows how to create instances of the specific collection type.

For example:

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;

public class MultiMapCollector<V extends Collection<String>>
    implements Collector<Entity, Map<String, V>, Map<String, V>> {

  private final Supplier<? extends V> valueSupplier;

  public MultiMapCollector(Supplier<? extends V> valueSupplier) {
    this.valueSupplier = valueSupplier;
  }

  @Override
  public Supplier<Map<String, V>> supplier() {
    return HashMap::new;
  }

  @Override
  public BiConsumer<Map<String, V>, Entity> accumulator() {
    return (map, e) -> map.computeIfAbsent(e.getName(), k -> valueSupplier.get()).add(e.getValue());
  }

  @Override
  public BinaryOperator<Map<String, V>> combiner() {
    return (map1, map2) -> {
      for (var entry : map2.entrySet()) {
        map1.merge(
            entry.getKey(),
            entry.getValue(),
            (curValue, newValue) -> {
              curValue.addAll(newValue);
              return curValue;
            });
      }
      return map1;
    };
  }

  @Override
  public Function<Map<String, V>, Map<String, V>> finisher() {
    return Collections::unmodifiableMap;
  }

  @Override
  public Set<Characteristics> characteristics() {
    return Set.of(Characteristics.UNORDERED);
  }
}

Note MultiMapCollector now has the type parameter <V extends Collection<String>>, and that everywhere else in the code where Collection<String> was used has been replaced with V. And then the constructor accepts a Supplier<? extends V> which is used to create instances of V in the accumulator() consumer.

Also, note I updated your combiner() function to make use of Map#merge(...), though that's not strictly necessary.


By the way, if you want to actually see how Collectors.groupingBy(...) is implemented, then you can always look at the source code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文