2017-08-08 2 views
0

Je viens d'avoir une méthode de comparaison simple pour trier un Map par la taille de la valeur qui est un ensemble.Trier la carte par la taille de la valeur

public List<Entry<String, HashSet<String>>> orderByDescStringSetSize(HashMap<String, HashSet<String>> map){ 
    Set<Entry<String, HashSet<String>>> set = map.entrySet(); 
    List<Entry<String, HashSet<String>>> list = new ArrayList<Entry<String, HashSet<String>>>(set); 

    Collections.sort(list, new Comparator<Map.Entry<String, HashSet<String>>>(){ 
     public int compare(Map.Entry<String, HashSet<String>> o1, Map.Entry<String, HashSet<String>> o2){ 

      Integer o1Vals = o1.getValue().size(); 
      Integer o2Vals = o2.getValue().size(); 

      //descending 
      if(o2Vals > o1Vals) 
       return 1; 
      else if(o2Vals==o1Vals) 
       return 0; 
      else 
       return -1; 

     } 
    }); 
    return list; 
} 

J'obtiens java.lang.IllegalArgumentException: Comparison method violates its general contract! Pourquoi ça?

+3

Possibilité de duplication de ["La méthode de comparaison enfreint son contrat général!"] (Https://stackoverflow.com/questions/8327514/comparison-method-violates-its-general-contract) – Guy

+0

Ajouter un exemple de la carte qui provoque l'apparition de l'exception. – Oleg

+0

Oui sur la duplication. Je suggère un 'return o2Vals - o1Vals;' et vous devriez aller bien. – daniu

Répondre

2

C'est parce que o2Vals==o1Vals ne fait pas ce que vous attendez.

Vous travaillez avec IntegerObjets - Utilisez equals !!

Ce n'est peut-être pas le seul problème.

+0

Merci beaucoup! Je n'ai pas pensé à cela – Song

+1

Ou mieux, utilisez 'int' ici, car il n'y a aucune raison d'utiliser' Integer' quand 'size()' renvoie un 'int'. Ou simplifiez la méthode entière pour 'renvoyer Integer.compare (o1.getValue(). Size(), o2.getValue(). Size());' – Holger

0

En utilisant la même approche que vous avez fait, je modifierai:

  • La taille du HashSets est un int, l'utiliser pour la comparaison plutôt que Integer. Il n'est pas nécessaire d'ajouter if else if else si vous renvoyez la valeur. De plus, vous pouvez simplement renvoyer les valeurs soustraites: return o2Vals - o1Vals.
  • Vous utilisez Entry dans certains points et Map.Entry, êtes-vous sûr d'avoir les importations correctes? Dans tous les cas, je vous suggère d'utiliser toujours l'un ou l'autre pour vous assurer que les types sont les mêmes.

Cela dit, la solution devrait ressembler à:

public List<Entry<String, HashSet<String>>> orderByDescStringSetSize2(HashMap<String, HashSet<String>> map) { 
    Set<Entry<String, HashSet<String>>> set = map.entrySet(); 
    List<Entry<String, HashSet<String>>> list = new ArrayList<>(set); 

    Collections.sort(list, new Comparator<Entry<String, HashSet<String>>>() { 

    @Override 
    public int compare(Entry<String, HashSet<String>> o1, Entry<String, HashSet<String>> o2) { 
     int o1Vals = o1.getValue().size(); 
     int o2Vals = o2.getValue().size(); 

     // Descending 
     return o2Vals - o1Vals; 
    } 
    }); 
    return list; 
} 

Cependant, si vous utilisez Java 8, vous pouvez profiter de l'interface Stream et de le faire plus compact sans définir Comparator:

public List<Entry<String, HashSet<String>>> orderByDescStringSetSize(HashMap<String, HashSet<String>> map) { 
    return map.entrySet().stream() // Create stream 
      .sorted(Comparator.comparing(entry -> -((Entry<String, HashSet<String>>) entry).getValue().size())) // Sort descending 
      .collect(Collectors.toList()); // Collect 
}