Java 8 Sort HashMap,其中映射键是 <String, Integer> 的对象

vsc*_*der 3 java collections hashmap type-conversion

我有一个像这样的简单客户类

public class Customer {
    public int age;
    public int discount;
    public String name;

    public Customer(String name) {
        this.name = name;
    }
    public Customer(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public Customer(String name, int age, int discount) {
        this.name = name;
        this.age = age;
        this.discount = discount;
    }

    @Override
    public String toString() {
        return "Customer [age=" + age + ", discount=" + discount + ", name=" + name + "]";
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Integer getDiscount() {
        return discount;
    }
    public void setDiscount(int discount) {
        this.discount = discount;
    }
}
Run Code Online (Sandbox Code Playgroud)

我使用这个填充这些对象的列表

List<Customer> customerList = new ArrayList<>(Arrays.asList(
        new Customer("John",   2, 15),
        new Customer("John",   4, 15),
        new Customer("John",   6, 25),
        new Customer("Joe",    3, 15),
        new Customer("Joe",    3, 15),
        new Customer("Joe",    3, 15),
        new Customer("Goerge", 6, 25),
        new Customer("Goerge", 6, 25),
        new Customer("Mary",   7, 25),
        new Customer("Jane",   1, 15),
        new Customer("Jane",   2, 15),
        new Customer("Jane",   8, 25),
        new Customer("Jane",   8, 25)
        ));
Run Code Online (Sandbox Code Playgroud)

现在我想使用这样的收集器对名称和折扣进行分组和计数

Map<Object, Long> collected = customerList
    .stream()
    .collect(Collectors.groupingBy(x -> Arrays.asList(x.name, x.discount), Collectors.counting()));
Run Code Online (Sandbox Code Playgroud)

我可以使用这个查看我的输出

collected.entrySet().forEach(c -> {
    System.out.println(c);
});
Run Code Online (Sandbox Code Playgroud)

输出以下内容

[Jane, 15]=2
[Joe, 15]=3
[John, 15]=2
[Mary, 25]=1
[John, 25]=1
[Jane, 25]=2
[Goerge, 25]=2
Run Code Online (Sandbox Code Playgroud)

问题是如何按名称和折扣对地图进行排序,使其看起来像这样

[Goerge, 25]=2
[Jane, 15]=2
[Jane, 25]=2
[Joe, 15]=3
[John, 15]=2
[John, 25]=1
[Mary, 25]=1
Run Code Online (Sandbox Code Playgroud)

我不断遇到收集器返回的 Object 类型?

我可以转换收集器以便它返回一个类,也许像

private class DiscountCounts
{
    public String name;
    public Integer discount;
}
Run Code Online (Sandbox Code Playgroud)

是否可以将 转换Map<**Object**, Long>()为类似的东西Map<DiscountCounts, Long>(),这是否允许使用 lambda 或 Comparator 构造访问 Map 键的字段?

我试过这样的事情,遍历地图并手动转换为我想要的地图,但我无法获得原始集合的键?

    Map<DiscountCounts, Long> collected2 = new HashMap<>();
    collected.entrySet().forEach(o -> {
        DiscountCounts key1 = (DiscountCounts)o.getKey();  //--> Fails here
        collected2.put((DiscountCounts)o.getKey(), o.getValue());
    });
Run Code Online (Sandbox Code Playgroud)

Dea*_*ool 5

不使用DiscountCounts类的一种方法是,先对列表进行排序,然后按操作进行摸索,并使用LinkedHashMap保存排序顺序

Map<List<Object>, Long> map = customerList.stream()
                .sorted(Comparator.comparing(Customer::getName).thenComparing(Customer::getDiscount))
                .collect(Collectors.groupingBy(x -> Arrays.asList(x.name, x.discount),LinkedHashMap::new, Collectors.counting()));
Run Code Online (Sandbox Code Playgroud)

在用另一种方式DiscountCounts类是,通过覆盖的equalshashcodeDiscountCounts阶级,做一个groupingBy创建DiscountCounts为每个对象Customer对象作为重点Map,并使用TreeMapComparator该排序结果

Map<DiscountCounts, Long> result = customerList.stream().collect(Collectors.groupingBy(
            c -> new DiscountCounts(c.getName(), c.getDiscount()),
            () -> new TreeMap<DiscountCounts, Long>(
                    Comparator.comparing(DiscountCounts::getName).thenComparing(DiscountCounts::getDiscount)),
            Collectors.counting()));
Run Code Online (Sandbox Code Playgroud)

@Andreas建议在评论开导我做的另一种方式,我觉得这是可以实现的最佳途径之一ComparableDiscountCounts,并提供排序逻辑,这样你就不需要提供给比较TreeMap

@Override
public int compareTo(DiscountCounts cust) {

      int last = this.getName().compareTo(cust.getName());

     return last == 0 ? this.getDiscount().compareTo(cust.getDiscount()) : last;
}

Map<DiscountCounts, Long> result1 = customerList.stream().collect(Collectors.groupingBy(
            c -> new DiscountCounts(c.getName(), c.getDiscount()), TreeMap::new, Collectors.counting()));
Run Code Online (Sandbox Code Playgroud)