【问题标题】:What's the difference between classifier and downstream and only using classifier分类器和下游和只使用分类器有什么区别
【发布时间】:2019-02-03 16:55:44
【问题描述】:

我是 Java 8 的新手,流收集器试图了解两者之间的基本区别是什么?

因为两个代码产生相同的结果。一个使用return groupingBy(classifier, toList()); 并返回 groupingBy(classifier, HashMap::new,downstream);

这里是代码

public class Grouping {
    enum CaloricLevel { DIET, NORMAL, FAT };

    public static void main(String[] args) {
        System.out.println("Dishes grouped by type: " + groupDishesByType());
        System.out.println("Dish names grouped by type: " + groupDishNamesByType());
    }


    private static Map<Type, List<Dish>> groupDishesByType() {
        return Dish.menu.stream().collect(groupingBy(Dish::getType));
    }

    private static Map<Type, List<String>> groupDishNamesByType() {
        return Dish.menu.stream().collect(groupingBy(Dish::getType, mapping(Dish::getName, toList())));
    }
}

输出:

Dishes grouped by type: {MEAT=[pork, beef, chicken], OTHER=[french fries, rice, season fruit, pizza], FISH=[prawns, salmon]}
Dish names grouped by type: {MEAT=[pork, beef, chicken], OTHER=[french fries, rice, season fruit, pizza], FISH=[prawns, salmon]}

Dish.java

public class Dish {

    private final String name;
    private final boolean vegetarian;
    private final int calories;
    private final Type type;

    public Dish(String name, boolean vegetarian, int calories, Type type) {
        this.name = name;
        this.vegetarian = vegetarian;
        this.calories = calories;
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public boolean isVegetarian() {
        return vegetarian;
    }

    public int getCalories() {
        return calories;
    }

    public Type getType() {
        return type;
    }

    public enum Type {
        MEAT, FISH, OTHER
    }

    @Override
    public String toString() {
        return name;
    }

    public static final List<Dish> menu = asList(
            new Dish("pork", false, 800, Dish.Type.MEAT),
            new Dish("beef", false, 700, Dish.Type.MEAT), 
            new Dish("chicken", false, 400, Dish.Type.MEAT),
            new Dish("french fries", true, 530, Dish.Type.OTHER), 
            new Dish("rice", true, 350, Dish.Type.OTHER),
            new Dish("season fruit", true, 120, Dish.Type.OTHER), 
            new Dish("pizza", true, 550, Dish.Type.OTHER),
            new Dish("prawns", false, 400, Dish.Type.FISH), 
            new Dish("salmon", false, 450, Dish.Type.FISH));

    public static final Map<String, List<String>> dishTags = new HashMap<>();

    static {
        dishTags.put("pork", asList("greasy", "salty"));
        dishTags.put("beef", asList("salty", "roasted"));
        dishTags.put("chicken", asList("fried", "crisp"));
        dishTags.put("french fries", asList("greasy", "fried"));
        dishTags.put("rice", asList("light", "natural"));
        dishTags.put("season fruit", asList("fresh", "natural"));
        dishTags.put("pizza", asList("tasty", "salty"));
        dishTags.put("prawns", asList("tasty", "roasted"));
        dishTags.put("salmon", asList("delicious", "fresh"));
    }
}

【问题讨论】:

  • 您在代码中的示例与有问题的示例不同...Collectors.mapping vs groupingBy(classifier, HashMap::new, downstream)。你的实际问题是什么?

标签: java java-8 java-stream collectors


【解决方案1】:

在你的两个例子中

.collect(groupingBy(Dish::getType));
.collect(groupingBy(Dish::getType, mapping(Dish::getName, toList())));

返回值相同,因为 Dish 类中的 toString() 方法仅返回 name。尝试向toString() mehtod 添加更多信息,您会发现不同。

一般来说,使用 groupingBy 和仅分类器允许对对象进行分组,就像在您的第一个示例中一样。但是将goupingBy 与分类器和下游一起使用可以让您对更多的对象进行分组,而不仅仅是您的对象。例如,您可以按类型对平均卡路里进行分组:

.collect(groupingBy(Dish::getType, averagingInt(Dish::getCalories));  // Map<Type, Double>

或找出每种类型热量最高的菜

.collect(groupingBy(Dish::getType, maxBy(Comparator.comparingInt(Dish::getCalories)));  // Map<Type, Optional<Dish>>

groupingBy 通常用作下游本身以进行双重分组(按类型和是否是素食主义者):

.collect(groupingBy(Dish::getType, groupingBy(Dish::isVegetarian)); // Map<Type, Map<Boolean, List<Dish>>>

【讨论】:

    【解决方案2】:

    如果这是问题

    因为两个代码产生相同的结果。一种使用 return groupingBy(classifier, toList());并返回 groupingBy(classifier, HashMap::new, 下游); ?

    groupingBy(函数分类器,下游收集器)

    不保证返回的 Map 的类型、可变性、可序列化性或线程安全性。

    groupingBy(函数分类器,供应商mapFactory, 下游收集器)

    收集器生成的地图是使用提供的工厂函数创建的。

    唯一的区别是当您将groupingBymapFactory 结合使用时,所创建的Map 是基于您的供应商逻辑(可能是自定义、不可变、同步等)。

    【讨论】:

      【解决方案3】:

      两者的基本区别是什么?

      主要区别在于您在完成收集器之前的中间步骤中完成的映射。不过,您使用它们的不同方式是groupingBy 的签名。

      虽然一方面,您已将mapperdownstream 统称为:

      .collect(Collectors.groupingBy(Dish::getType,  // classifier
                   Collectors.mapping(Dish::getName,  // mapper <<<< difference here
                       Collectors.toList()))) // downstream
      

      另一方面,deault implementation of groupingBy 用于

      .collect(Collectors.groupingBy(Dish::getType)) 
      

      可以扩展为类似的格式:

      .collect(Collectors.groupingBy(Dish::getType, // classifier
                   Collectors.mapping(Function.identity(),  // mapper
                       Collectors.toList()))); // downstream
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-11-11
        • 1970-01-01
        • 2019-07-09
        • 2016-10-03
        • 1970-01-01
        • 2018-02-06
        • 2020-11-23
        相关资源
        最近更新 更多