为什么在 Java 的 TreeMap 中重载 Comparable 时我在 Keyset 中只得到一个 Key

Why do I get only one Key in Keyset while Overloading Comparable in TreeMap in Java

  1. 我尝试根据字符串的长度在我的 TreeMap 中进行自定义排序。尽管字符串不同,但字符串的长度相同,为什么我只得到一个密钥。

  2. 我该如何解决这个问题以及对 equals meth0d 有什么影响,它会被使用还是会被 compareTo 取代。

代码:

import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;

public class Student implements Comparable {
    int RollNo;
    String Name;
    public Student(int RollNo, String Name) {
        this.RollNo = RollNo;
        this.Name = Name;
    }

    public int compareTo(Object arg0) {
        Student str = (Student) arg0;
        return Integer.valueOf(str.Name.length()).compareTo(Integer.valueOf(this.Name.length()));
    }

    public static void main(String[] args) {
        Map<Student, Integer> mp = new TreeMap<Student, Integer>();
        mp.put(new Student(1, "Sameer"), 1);
        mp.put(new Student(2, "Aameer"), 2);

        for(Student st : mp.keySet()){
            System.out.println((st.Name));
        }
    }
}

您的 compareTo 方法比较名称 String 的长度而不是它们的内容,因此 Student 具有相同长度的名称被认为是相同的 TreeMap.

换句话说,TreeMap 使用的 ComparableComparator 决定了键的顺序和唯一性。

您可以通过按名称长度和名称内容(当长度相等时)对 TreeMap 进行排序来解决此问题:

public int compareTo(Object arg0) {

    Student str = (Student) arg0;

    int lengthComp = Integer.valueOf(str.Name.length()).compareTo(Integer.valueOf(this.Name.length()));
    if (lengthComp == 0) {
        // compare names if the lengths are equal
        return str.Name.compareTo(this.Name);
    } else {
        return lengthComp;
    }
}

除此之外,您的 class 最好实现 Comparable<Student>,这将允许您的 compareTo 方法接受 Student 参数。

How do I fix this and what are the implications for the equals meth0d, will it ever be used or will compareTo take its place.

equals 未被 TreeMap 使用,compareTo 确实取代了它。

答案在TreeMap.put中。通过覆盖 compareTo 方法,第二个 put 只会替换键的值,因为两个字符串的长度相同。

因为两个键的 compare() returns 都是 0,所以它们在排序中被认为是相等的。请参阅 https://docs.oracle.com/javase/8/docs/api/java/lang/Comparable.html 了解为什么严格建议比较行为与 equals() 一致,但底线是第二个键未添加,因为它被认为等于第一个。

解决方案是通过添加任何一种常用的打破平局的方式使您的比较与相等一致。例如,如果两个字符串不相等,但长度相同,return 它们的字典顺序,或者根据实际字符计算哈希值。

"Shameer" 和 "Aameer" 的字符串长度相同。所以 java compareTo 方法将认为两个值(考虑长度)相同。所以这两个对象在排序时被认为是相等的。

在 Map 接口中向键添加对象时,值应该是唯一的,这违反了 compareTo 方法。所以无法添加以上值。

As you already aware that in Map interface duplicate keys is not allowed.

我已经用简单的例子说明了。如果键值相同,则无法将值添加到 Map 界面中。

import java.util.Map;
import java.util.TreeMap;

public class Student2  {

public static void main(String[] args) {

    Map<String, Integer> mp = new TreeMap<String, Integer>();

    mp.put("Sameer", 1);
    mp.put("Sameer", 2);

    for(String st : mp.keySet()){
        System.out.println((st));
    }
}
}

Result: Sameer