首页 > 解决方案 > 我们如何按值降序对 Map 进行排序,然后按键按字母顺序排序?

问题描述

我目前正在做以下编程任务:Prize Draw,语句为:

为了参加抽奖,每个人都给出了他/她的名字。

名字的每个字母都有一个值,即它在英文字母表中的排名。A 和 a 的等级为 1,B 和 b 的等级为 2,依此类推。

名字的长度被添加到这些等级的总和中,因此是一个数字 som。

一组随机权重与名字相关联,每个 som 乘以其相应的权重,得到他们所谓的中奖号码。

例子:

名称:“COLIN,AMANDBA,AMANDAB,CAROL,PauL,JOSEPH” 权重:[1, 4, 4, 5, 2, 1]

PauL -> som = 名字长度 + 16 + 1 + 21 + 12 = 4 + 50 -> 54与 PauL 相关的权重为 2,因此 PauL 的中奖号码为 54 * 2 = 108。

现在可以按中奖号码的降序对名字进行排序。当两个人的中奖号码相同时,按名字的字母顺序排列。任务:

parameters: st a string of firstnames, we an array of weights, n a rank

return: the firstname of the participant whose rank is n (ranks are numbered from 1)

例子:

名称:“COLIN,AMANDBA,AMANDAB,CAROL,PauL,JOSEPH” 权重:[1, 4, 4, 5, 2, 1] n: 4

该函数应返回:“PauL”

笔记:

If st is empty return "No participants".

If n is greater than the number of participants then return "Not enough participants".

我认为伪代码为:

if names are empty return No participants
if n is greater than names length return Not enough participants
for each name
    sum its length
    for each character
        sum its value in the alphabet
    multiply sum by weight
    store name and sum in the map
sort map descending by value
sort map alphabetically by key (if values are equal)
return key n from the map

此外,我还编写了以下代码:

import java.util.*;
import java.util.stream.*;

class Rank {

    public static String nthRank(String st, Integer[] we, int n) {
      System.out.println("\n\n\nNames: "+st+" weights: "+Arrays.toString(we)+" n: "+n);

      if(st.isEmpty()) return "No participants";
      String[] names = st.split(",");
      if(n > names.length) return "Not enough participants";

      String alphabet = "abcdefghijklmnopqrstuvwxyz";
      int sum = 0;
      Map<String, Integer> map = new HashMap<String, Integer>();

      for(int i = 0; i < names.length; i++){
        String currentName = names[i];
        sum += currentName.length();
        for(int j = 0; j < currentName.length(); j++){
          char currentChar = Character.toLowerCase(currentName.charAt(j));
          sum += alphabet.indexOf(currentChar) + 1;
        }
        map.put(currentName, sum*we[i]);
        System.out.println("Map: "+map.toString());
        sum = 0;
      }

      map = map.entrySet().stream()
            .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
            .collect(Collectors.toMap(
              Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

      System.out.println("\n\nSorted Map: "+map.toString());

      Object[] namesByWinningNumber = map.keySet().toArray();
      Arrays.sort(namesByWinningNumber);

      System.out.println("\n\n\nSorted names array: "+ Arrays.toString(namesByWinningNumber));

      return map.keySet().toArray()[n-1].toString();
    }
}

当输入为:

Names: William,Willaim,Olivia,Olivai,Lily,Lyli weights: [1, 1, 1, 1, 1, 1] n: 1

预期是:

Willaim

但代码输出:

William

作为痕迹:

Names: William,Willaim,Olivia,Olivai,Lily,Lyli weights: [1, 1, 1, 1, 1, 1] n: 1
Map: {William=86}
Map: {William=86, Willaim=86}
Map: {Olivia=74, William=86, Willaim=86}
Map: {Olivia=74, Olivai=74, William=86, Willaim=86}
Map: {Olivia=74, Olivai=74, William=86, Willaim=86, Lily=62}
Map: {Olivia=74, Olivai=74, William=86, Willaim=86, Lily=62, Lyli=62}


Sorted Map: {William=86, Willaim=86, Olivia=74, Olivai=74, Lily=62, Lyli=62}



Sorted names array: [Lily, Lyli, Olivai, Olivia, Willaim, William]

因此,如我们所见,首先我按值按降序对地图进行排序。然后我想如果我得到键并按字母顺序对它们进行排序,它只会处理具有完全相同值的键;但是,一切都解决了。

我们如何才能按值降序对 Map 进行排序,然后按键按字母顺序排序‽‽‽</p>

我也读过:

编辑:

我读到:如何为 Map.Entry 中的 Map 值编写 java 8 Comparator

我也尝试编写自己的比较器:

Comparator<Map.Entry> outerComparator = (pair1, pair2) -> {
         if ((int)pair1.getValue() > (int)pair2.getValue()){
            return 1;
          }else if ((int)pair1.getValue() < (int)pair2.getValue()){
            return -1;
          }else{
            return pair1.getKey().toString().compareTo(pair2.getKey().toString());
          } 
       };

并按如下方式使用它:

map = map.entrySet().stream()
            .sorted(Map.Entry.comparingByValue(outerComparator))
            .collect(Collectors.toMap(
              Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

但是,我们的控制台说:

./src/main/java/Rank.java:40: error: incompatible types: inference variable V has incompatible bounds
            .sorted(Map.Entry.comparingByValue(outerComparator))
            ^
    equality constraints: Integer
    lower bounds: Entry,Object
  where V,K are type-variables:
    V extends Object declared in method <K,V>comparingByValue(Comparator<? super V>)
    K extends Object declared in method <K,V>comparingByValue(Comparator<? super V>)
./src/main/java/Rank.java:41: error: incompatible types: cannot infer type-variable(s) T,K#1,U,M,K#2,V#1
            .collect(Collectors.toMap(
                                     ^
    (argument mismatch; invalid method reference
      method getKey in interface Entry<K#3,V#2> cannot be applied to given types
        required: no arguments
        found: Object
        reason: actual and formal argument lists differ in length)
  where T,K#1,U,M,K#2,V#1,K#3,V#2 are type-variables:
    T extends Object declared in method <T,K#1,U,M>toMap(Function<? super T,? extends K#1>,Function<? super T,? extends U>,BinaryOperator<U>,Supplier<M>)
    K#1 extends Object declared in method <T,K#1,U,M>toMap(Function<? super T,? extends K#1>,Function<? super T,? extends U>,BinaryOperator<U>,Supplier<M>)
    U extends Object declared in method <T,K#1,U,M>toMap(Function<? super T,? extends K#1>,Function<? super T,? extends U>,BinaryOperator<U>,Supplier<M>)
    M extends Map<K#1,U> declared in method <T,K#1,U,M>toMap(Function<? super T,? extends K#1>,Function<? super T,? extends U>,BinaryOperator<U>,Supplier<M>)
    K#2 extends Object declared in class LinkedHashMap
    V#1 extends Object declared in class LinkedHashMap
    K#3 extends Object declared in interface Entry
    V#2 extends Object declared in interface Entry
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
2 errors

在此之前是完整的代码:

import java.util.*;
import java.util.stream.*;

class Rank {

    public static String nthRank(String st, Integer[] we, int n) {
      System.out.println("\n\n\nNames: "+st+" weights: "+Arrays.toString(we)+" n: "+n);

      if(st.isEmpty()) return "No participants";
      String[] names = st.split(",");
      if(n > names.length) return "Not enough participants";

      String alphabet = "abcdefghijklmnopqrstuvwxyz";
      int sum = 0;
      Map<String, Integer> map = new HashMap<String, Integer>();

      for(int i = 0; i < names.length; i++){
        String currentName = names[i];
        sum += currentName.length();
        for(int j = 0; j < currentName.length(); j++){
          char currentChar = Character.toLowerCase(currentName.charAt(j));
          sum += alphabet.indexOf(currentChar) + 1;
        }
        map.put(currentName, sum*we[i]);
        System.out.println("Map: "+map.toString());
        sum = 0;
      }

      Comparator<Map.Entry> outerComparator = (pair1, pair2) -> {
         if ((int)pair1.getValue() > (int)pair2.getValue()){
            return 1;
          }else if ((int)pair1.getValue() < (int)pair2.getValue()){
            return -1;
          }else{
            return pair1.getKey().toString().compareTo(pair2.getKey().toString());
          } 
       };

      map = map.entrySet().stream()
            .sorted(Map.Entry.comparingByValue(outerComparator))
            .collect(Collectors.toMap(
              Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

      System.out.println("\n\nSorted Map: "+map.toString());

      return map.keySet().toArray()[n-1].toString();
    }

}

作为一个新的尝试,我把它写成一个单独的类:

class MapComparator implements Comparator<Map.Entry> { 

    @Override
    public int compare(Map.Entry pair1, Map.Entry pair2) { 

        int valueCompare = - ( pair1.getValue().compareTo(pair2.getValue()) ); 
        int nameCompare = pair1.getKey().compareTo(pair2.getKey()); 

        if (valueCompare == 0) { 
            return ((nameCompare == 0) ? valueCompare : nameCompare); 
        } else { 
            return valueCompare; 
        } 
    } 
} 

我认为它可以很好地用作:

map = map.entrySet().stream()
            .sorted(Map.Entry.comparingByValue(MapComparator.compare()))
            .collect(Collectors.toMap(
              Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

然后我们的控制台声称:

./src/main/java/Rank.java:40: error: method compare in class Rank.MapComparator cannot be applied to given types;
            .sorted(Map.Entry.comparingByValue(MapComparator.compare()))
                                                            ^
  required: Entry,Entry
  found: no arguments
  reason: actual and formal argument lists differ in length
./src/main/java/Rank.java:41: error: incompatible types: cannot infer type-variable(s) T,K#1,U,M,K#2,V#1
            .collect(Collectors.toMap(
                                     ^
    (argument mismatch; invalid method reference
      method getKey in interface Entry<K#3,V#2> cannot be applied to given types
        required: no arguments
        found: Object
        reason: actual and formal argument lists differ in length)
  where T,K#1,U,M,K#2,V#1,K#3,V#2 are type-variables:
    T extends Object declared in method <T,K#1,U,M>toMap(Function<? super T,? extends K#1>,Function<? super T,? extends U>,BinaryOperator<U>,Supplier<M>)
    K#1 extends Object declared in method <T,K#1,U,M>toMap(Function<? super T,? extends K#1>,Function<? super T,? extends U>,BinaryOperator<U>,Supplier<M>)
    U extends Object declared in method <T,K#1,U,M>toMap(Function<? super T,? extends K#1>,Function<? super T,? extends U>,BinaryOperator<U>,Supplier<M>)
    M extends Map<K#1,U> declared in method <T,K#1,U,M>toMap(Function<? super T,? extends K#1>,Function<? super T,? extends U>,BinaryOperator<U>,Supplier<M>)
    K#2 extends Object declared in class LinkedHashMap
    V#1 extends Object declared in class LinkedHashMap
    K#3 extends Object declared in interface Entry
    V#2 extends Object declared in interface Entry
./src/main/java/Rank.java:54: error: cannot find symbol
            int valueCompare = - ( pair1.getValue().compareTo(pair2.getValue()) ); 
                                                   ^
  symbol:   method compareTo(Object)
  location: class Object
./src/main/java/Rank.java:55: error: cannot find symbol
            int nameCompare = pair1.getKey().compareTo(pair2.getKey()); 
                                            ^
  symbol:   method compareTo(Object)
  location: class Object
4 errors

总结一下,目前完整的代码是:

import java.util.*;
import java.util.stream.*;

class Rank {

    public static String nthRank(String st, Integer[] we, int n) {
      System.out.println("\n\n\nNames: "+st+" weights: "+Arrays.toString(we)+" n: "+n);

      if(st.isEmpty()) return "No participants";
      String[] names = st.split(",");
      if(n > names.length) return "Not enough participants";

      String alphabet = "abcdefghijklmnopqrstuvwxyz";
      int sum = 0;
      Map<String, Integer> map = new HashMap<String, Integer>();

      for(int i = 0; i < names.length; i++){
        String currentName = names[i];
        sum += currentName.length();
        for(int j = 0; j < currentName.length(); j++){
          char currentChar = Character.toLowerCase(currentName.charAt(j));
          sum += alphabet.indexOf(currentChar) + 1;
        }
        map.put(currentName, sum*we[i]);
        System.out.println("Map: "+map.toString());
        sum = 0;
      }

      Comparator<Map.Entry> outerComparator = (pair1, pair2) -> {
         if ((int)pair1.getValue() > (int)pair2.getValue()){
            return 1;
          }else if ((int)pair1.getValue() < (int)pair2.getValue()){
            return -1;
          }else{
            return pair1.getKey().toString().compareTo(pair2.getKey().toString());
          } 
       };

      map = map.entrySet().stream()
            .sorted(Map.Entry.comparingByValue(MapComparator.compare()))
            .collect(Collectors.toMap(
              Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

      System.out.println("\n\nSorted Map: "+map.toString());

      return map.keySet().toArray()[n-1].toString();
    }

    class MapComparator implements Comparator<Map.Entry> { 

        @Override
        public int compare(Map.Entry pair1, Map.Entry pair2) { 

            int valueCompare = - ( pair1.getValue().compareTo(pair2.getValue()) ); 
            int nameCompare = pair1.getKey().compareTo(pair2.getKey()); 

            if (valueCompare == 0) { 
                return ((nameCompare == 0) ? valueCompare : nameCompare); 
            } else { 
                return valueCompare; 
            } 
        } 
    } 


}

标签: javaarrayssortingdictionarymethods

解决方案


关于错误:

./src/main/java/Rank.java:40: error: method compare in class Rank.MapComparator cannot be applied to given types;
                .sorted(Map.Entry.comparingByValue(MapComparator.compare()))

中的compare方法MapComparator不是静态的。除了Map.Entry.comparingByValuein 的方法Map.Entry需要 a Comparator,而不是 from 的结果MapComparator.compare()

在这里,我认为您打算通过比较地图条目来排序,使用MapComparator,而不是地图的值(这是comparingByValue会做的)。所以你要:

map = map.entrySet().stream().sorted(new MapComparator()))

但是您MapComparator也应该Comparator<Map.Entry<String, Integer>>因为Map.Entry.


推荐阅读