Sorting 如何基于自定义比较器对Hashmap进行键排序

Sorting 如何基于自定义比较器对Hashmap进行键排序,sorting,collections,java-8,custom-compare,Sorting,Collections,Java 8,Custom Compare,我收到了一个常用进程的Hashmap,我需要使用自定义比较器对该进程的键进行排序 下面是我尝试过的,但它似乎不起作用-它没有排序键 地图的键的形式为长字符串短示例:(11169-SW-1/11169-SW-2/11132-SH-2/11132-SH-7/11132-SH-1)。字符串比较不起作用,因为我需要它用于数字部分,所以我定义了一个自定义比较器。我意识到自定义比较器代码需要清理-例如,需要合并多个返回语句,并且需要进行其他清理,但我可以在完成后进行清理 我怎么做 Map<String

我收到了一个常用进程的Hashmap,我需要使用自定义比较器对该进程的键进行排序

下面是我尝试过的,但它似乎不起作用-它没有排序键

地图的键的形式为长字符串短示例:(11169-SW-1/11169-SW-2/11132-SH-2/11132-SH-7/11132-SH-1)。字符串比较不起作用,因为我需要它用于数字部分,所以我定义了一个自定义比较器。我意识到自定义比较器代码需要清理-例如,需要合并多个返回语句,并且需要进行其他清理,但我可以在完成后进行清理

我怎么做

Map<String, Map<String, String>> strValuesMap = Utilities.getDataMapFromDB();

Map<String, Map<String, String>> sortedMap = new TreeMap<>(new CustomComparator());
sortedMap.putAll(strValuesMap);
sortedMap.forEach((x, y) -> System.out.println("id " + x + "=" + y));
Map strValuesMap=Utilities.getDataMapFromDB();
Map sortedMap=new TreeMap(new CustomComparator());
分类地图putAll(标准值地图);
sortedMap.forEach((x,y)->System.out.println(“id”+x+“=”+y));
自定义比较器定义如下

class CustomComparator implements Comparator<String> {

    @Override
    public int compare(String plId1, String plId2) {

        System.out.println("plId1 : " + plId1 + " plId2 " + plId2);
        String[] plId1Split = plId1.split("-");
        String[] plId2Split = plId2.split("-");
        int retValue = 0;
        if (!plId1Split[0].equalsIgnoreCase(plId2Split[0])) {
            Long seq1 = new Long(plId1Split[0]);
            Long seq2 = new Long(plId2Split[0]);
            retValue = seq1.compareTo(seq1);
        }
        if (retValue != 0) {
            return retValue;
        }
        if (!plId1Split[1].equalsIgnoreCase(plId2Split[1])) {
            retValue = plId1.compareTo(plId2);
        }
        if (retValue != 0) {
            return retValue;
        } else {
            Short seq1 = new Short(plId1Split[2]);
            Short seq2 = new Short(plId2Split[2]);
            retValue = seq1.compareTo(seq2);
            return retValue;
        }
    }
}
类CustomComparator实现Comparator{
@凌驾
公共整数比较(字符串plId1、字符串plId2){
系统输出打印项次(“plId1:+plId1+“plId2”+plId2);
字符串[]plId1Split=plId1.split(“-”);
字符串[]plId2Split=plId2.split(“-”);
int-retValue=0;
如果(!plId1Split[0].equalsIgnoreCase(plId2Split[0])){
Long seq1=新的Long(plId1Split[0]);
Long seq2=新的Long(plId2Split[0]);
retValue=seq1。与(seq1)相比;
}
如果(retValue!=0){
返回值;
}
如果(!plId1Split[1].equalsIgnoreCase(plId2Split[1])){
retValue=plId1.与之相比(plId2);
}
如果(retValue!=0){
返回值;
}否则{
短线顺序1=新短线(plId1Split[2]);
Short-seq2=新的Short(plId2Split[2]);
retValue=seq1.比较(seq2);
返回值;
}
}
}
谢谢这是不正确的:

if (!plId1Split[1].equalsIgnoreCase(plId2Split[1])) {
    retValue = plId1.compareTo(plId2); // This part is wrong
}
在条件正文中,您应该只比较字符串的第二部分(
plidsplit[1]
plidsplit[1]
),而比较整个字符串(
plId1
plId2
)。
这意味着字符串的最后一部分(
plId1Split[2]
plId2Split[2]
)不是按照数字顺序比较的,而是按照字典顺序比较的

因此,它应该是:

if (!plId1Split[1].equalsIgnoreCase(plId2Split[1])) {
    retValue = plId1Split[1].compareTo(plId2Split[1]);
}
关于比较仪的清晰度,我认为您的一些测试是不需要的。
例如,与
equalsIgnoreCase()
比较,然后与long comparator比较转换为long的相同字符串:

if (!plId1Split[0].equalsIgnoreCase(plId2Split[0])) {
        Long seq1 = new Long(plId1Split[0]);
        Long seq2 = new Long(plId2Split[0]);
        retValue = seq1.compareTo(seq1);
} 
总的来说,如果调用这两个比较,这不是一个很好的优化,而且还会降低代码的可读性

还要注意的是,您过度使用了number对象,这些对象在这里产生了不需要的装箱操作(
Long
->
Long
),这会带来成本。
你可以这样写:

class CustomComparator implements Comparator<String> {

    @Override
    public int compare(String plId1, String plId2) {

        String[] plId1Split = plId1.split("-");
        String[] plId2Split = plId2.split("-");

        int retValue = Long.compare(Long.parseLong(plId1Split[0]), 
                               Long.parseLong(plId2Split[0]));

        if (retValue != 0) {
            return retValue;
        }

        retValue = plId1Split[1].compareTo(plId2Split[1]);

        if (retValue != 0) {
            return retValue;
        }

        return Short.compare(Short.parseShort(plId1Split[2]), 
                       Short.parseShort(plId2Split[2]));

   }                    
}
和使用

提取排序键以比较元素并为此排序键应用指定比较器的:

import static java.util.Comparator.comparing;
import static java.util.Comparator.comparingLong;

Comparator<String> comp =
        comparing(s -> new Identifier(s.split("-")),
                  comparingLong(Identifier::getPart1)
                 .thenComparing(Identifier::getPart2)
                 .thenComparingInt(Identifier::getPart3));
导入静态java.util.Comparator.Comparating;
导入静态java.util.Comparator.comparingLong;
比较器=
比较->新标识符(s.split(“-”),
比较长(标识符::getPart1)
.Then比较(标识符::getPart2)
。然后比较它(标识符::getPart3));
它清楚地揭示了比较/功能逻辑

请注意,由于Java不提供元组的内置结构,我们需要引入一个自定义类来保存数据。

对于元组类(来自或任何源代码),代码将更加简单

只是为了确保:您的比较器设置为对第一级映射的键进行排序,而不是第二级映射。这是你的要求吗?@davidxxx-是的,一级地图。我想,当用树形图定义比较器时,我需要指出这一点,对吗?但不知道怎么做?嗨,谢谢你发现了。但现在我遇到了另一个问题。原始地图有4000多行,而我的新地图只有50行。strValuesMap尺寸4444和sortedMap尺寸50是的-我知道我必须重写比较器。谢谢你给我这些建议。但仍然不确定为什么两张地图的大小不匹配我的比较器肯定还有其他问题。当我将其替换为您所做的更改时,它现在返回4444行并正确排序的地图。欢迎您。很有可能。冗长的代码经常产生键入错误,不一定容易发现。我用Java8方式进行了更新。这可能也很有趣,也感谢Java8的替代方式。这也很有效,而且更加紧凑和简洁。
import static java.util.Comparator.comparing;
import static java.util.Comparator.comparingLong;

Comparator<String> comp =
        comparing(s -> new Identifier(s.split("-")),
                  comparingLong(Identifier::getPart1)
                 .thenComparing(Identifier::getPart2)
                 .thenComparingInt(Identifier::getPart3));