Java 8排序HashMap,其中映射键是<;的对象;字符串,整数>;

Java 8排序HashMap,其中映射键是<;的对象;字符串,整数>;,java,collections,hashmap,type-conversion,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;

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

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;
    }
}
我使用以下命令填充这些对象的列表

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)
        ));
collected.entrySet().forEach(c -> {
    System.out.println(c);
});
哪个输出以下内容

[Jane, 15]=2
[Joe, 15]=3
[John, 15]=2
[Mary, 25]=1
[John, 25]=1
[Jane, 25]=2
[Goerge, 25]=2
问题是如何按名称和折扣对地图进行排序,使其看起来像这样

Map<Object, Long> collected = customerList
    .stream()
    .collect(Collectors.groupingBy(x -> Arrays.asList(x.name, x.discount), Collectors.counting()));
[Goerge, 25]=2
[Jane, 15]=2
[Jane, 25]=2
[Joe, 15]=3
[John, 15]=2
[John, 25]=1
[Mary, 25]=1
我一直碰到收集器返回的对象类型

我是否可以强制转换收集器,使其返回一个类,可能类似于

private class DiscountCounts
{
    public String name;
    public Integer discount;
}
是否可以将
Map()
转换为类似
Map()
的内容,这是否允许使用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());
    });
Map collected2=newhashmap();
collected.entrySet().forEach(o->{
折扣计数键1=(折扣计数)o.getKey();//-->在此失败
collected2.put((折扣计数)o.getKey(),o.getValue());
});

不使用
折扣计数
类的一种方法是,首先对列表进行排序,然后按操作执行摸索,然后使用
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()));
@Andreas在评论中建议启发我另一种方法,我觉得这是您可以在
折扣计数上实现
compariable
的最佳方法之一,并提供排序逻辑,这样您就不需要为
树映射提供Compariator

@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()));
@覆盖
公共整数比较(折扣计数客户){
int last=this.getName().compareTo(cust.getName());
return last==0?this.getDiscount().compareTo(cust.getDiscount()):last;
}
Map result1=customerList.stream().collect(Collectors.groupingBy(
c->new DiscountCounts(c.getName(),c.getDiscount()),TreeMap::new,Collectors.counting());

对于
折扣计数
,使用适当的
等于
哈希代码
实现,您可能会在以下几行中寻找一些内容:

Map<DiscountCounts, Long> collectSortedEntries = customerList
        .stream()
        .collect(Collectors.groupingBy(x -> new DiscountCounts(x.name, x.discount),
                Collectors.counting()))
        .entrySet()
        .stream()
        .sorted(Comparator.comparing((Map.Entry<DiscountCounts, Long> e) -> e.getKey().getName())
                .thenComparing(e -> e.getKey().getDiscount()))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                (a, b) -> a, LinkedHashMap::new));
Map collectorsortedntries=customerList
.stream()
.collect(Collectors.groupingBy(x->new discounts(x.name,x.discount)),
Collectors.counting())
.entrySet()
.stream()
.sorted(Comparator.comparing((Map.Entry e)->e.getKey().getName())
.然后比较(e->e.getKey().getDiscount())
.collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue,
(a,b)->a,LinkedHashMap::new);

您是否考虑过使用
TreeMap
而不是
HashMap
?它会自动对键进行排序。Dawood,从下面的回答中,我可以看出树形图在这里会有多大帮助,但我永远都不可能完全理解收集器对象来实现它,谢谢你的建议。如果使用
折扣计数
,让
折扣计数
实现
可比
可能会更好,这样您就不必为
树状图
构建
比较器了。谢谢,第二个解决方案奏效了,为我提供了最强大的功能。完全归功于完整而连贯的解决方案。纳曼,我喜欢你;接下来,您的代码会在e->e.getKey().getName()上抛出一个编译器错误,但这将是我进一步了解比较器的机会。@vscoder已更新了修复编译的答案,但事实上,Andreas对另一个答案的评论是有意义的,即更新
折扣计数
,以
实现可比
,然后进一步简化收集到
树状图
Map<DiscountCounts, Long> collectSortedEntries = customerList
        .stream()
        .collect(Collectors.groupingBy(x -> new DiscountCounts(x.name, x.discount),
                Collectors.counting()))
        .entrySet()
        .stream()
        .sorted(Comparator.comparing((Map.Entry<DiscountCounts, Long> e) -> e.getKey().getName())
                .thenComparing(e -> e.getKey().getDiscount()))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                (a, b) -> a, LinkedHashMap::new));