如何基于自定义比较器对 Key 上的 Hashmap 进行排序
How to sort a Hashmap on Key based on Custom Comparator
我收到了一个常见流程的 Hashmap,我需要使用自定义比较器对 Key 进行排序。
以下是我尝试过的方法,但它似乎不起作用 - 它没有对键进行排序。
映射的键的形式为长-串-短示例:( 11169-SW-1 / 11169-SW-2 / 11132-SH-2 / 11132-SH-7 / 11132-SH-1 )。字符串比较不起作用,因为我需要它用于数字部分,所以我定义了一个自定义比较器。我意识到自定义比较器代码需要清理 - 例如需要合并多个 return 语句,并且需要进行其他清理,但一旦我开始工作,我就可以做到这一点。
我该怎么做?
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));
自定义比较器定义如下
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;
}
}
}
谢谢
这是不正确的:
if (!plId1Split[1].equalsIgnoreCase(plId2Split[1])) {
retValue = plId1.compareTo(plId2); // This part is wrong
}
在条件体中,您应该只比较字符串的第二部分(plId1Split[1]
和 plId2Split[1]
),而比较整个字符串(plId1
和 plId2
).
所以这意味着字符串的最后一部分(plId1Split[2]
和 plId2Split[2]
)不是根据数字顺序而是字典顺序进行比较。
所以应该是:
if (!plId1Split[1].equalsIgnoreCase(plId2Split[1])) {
retValue = plId1Split[1].compareTo(plId2Split[1]);
}
关于你的比较器的清晰度,我认为你的一些测试是不需要的。
例如,您与 equalsIgnoreCase()
进行比较,然后与 long 比较器进行比较,将相同的字符串转换为 long :
if (!plId1Split[0].equalsIgnoreCase(plId2Split[0])) {
Long seq1 = new Long(plId1Split[0]);
Long seq2 = new Long(plId2Split[0]);
retValue = seq1.compareTo(seq1);
}
这不是一个很好的优化,总的来说,如果调用两个比较,它也会降低代码的可读性。
另请注意,您过度使用数字对象会产生不需要的装箱操作 (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]));
}
}
作为替代方案,您可以使用 Java 8 个比较器,依靠自定义 class 表示要比较的对象:
// private package if makes sense to make this class not visible outside that
class Identifier {
private long part1;
private String part2;
private short part3;
Identifier(String[] split) {
this.part1 = Long.parseLong(split[0]);
this.part2 = split[1];
this.part3 = Short.parseShort(split[2]);
}
long getPart1() {
return part1;
}
String getPart2() {
return part2;
}
short getPart3() {
return part3;
}
}
提取排序键以比较元素并为此排序键应用指定的比较器:
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));
它清楚地揭示了 comparing/functional 逻辑。
请注意,由于 Java 没有为 Tuple 提供 built-in 结构,我们需要引入自定义 class 来保存数据。
使用元组 classes(来自 https://www.javatuples.org/ 或任何源代码),代码会更简单。
我收到了一个常见流程的 Hashmap,我需要使用自定义比较器对 Key 进行排序。
以下是我尝试过的方法,但它似乎不起作用 - 它没有对键进行排序。
映射的键的形式为长-串-短示例:( 11169-SW-1 / 11169-SW-2 / 11132-SH-2 / 11132-SH-7 / 11132-SH-1 )。字符串比较不起作用,因为我需要它用于数字部分,所以我定义了一个自定义比较器。我意识到自定义比较器代码需要清理 - 例如需要合并多个 return 语句,并且需要进行其他清理,但一旦我开始工作,我就可以做到这一点。
我该怎么做?
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));
自定义比较器定义如下
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;
}
}
}
谢谢
这是不正确的:
if (!plId1Split[1].equalsIgnoreCase(plId2Split[1])) {
retValue = plId1.compareTo(plId2); // This part is wrong
}
在条件体中,您应该只比较字符串的第二部分(plId1Split[1]
和 plId2Split[1]
),而比较整个字符串(plId1
和 plId2
).
所以这意味着字符串的最后一部分(plId1Split[2]
和 plId2Split[2]
)不是根据数字顺序而是字典顺序进行比较。
所以应该是:
if (!plId1Split[1].equalsIgnoreCase(plId2Split[1])) {
retValue = plId1Split[1].compareTo(plId2Split[1]);
}
关于你的比较器的清晰度,我认为你的一些测试是不需要的。
例如,您与 equalsIgnoreCase()
进行比较,然后与 long 比较器进行比较,将相同的字符串转换为 long :
if (!plId1Split[0].equalsIgnoreCase(plId2Split[0])) {
Long seq1 = new Long(plId1Split[0]);
Long seq2 = new Long(plId2Split[0]);
retValue = seq1.compareTo(seq1);
}
这不是一个很好的优化,总的来说,如果调用两个比较,它也会降低代码的可读性。
另请注意,您过度使用数字对象会产生不需要的装箱操作 (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]));
}
}
作为替代方案,您可以使用 Java 8 个比较器,依靠自定义 class 表示要比较的对象:
// private package if makes sense to make this class not visible outside that
class Identifier {
private long part1;
private String part2;
private short part3;
Identifier(String[] split) {
this.part1 = Long.parseLong(split[0]);
this.part2 = split[1];
this.part3 = Short.parseShort(split[2]);
}
long getPart1() {
return part1;
}
String getPart2() {
return part2;
}
short getPart3() {
return part3;
}
}
提取排序键以比较元素并为此排序键应用指定的比较器:
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));
它清楚地揭示了 comparing/functional 逻辑。
请注意,由于 Java 没有为 Tuple 提供 built-in 结构,我们需要引入自定义 class 来保存数据。
使用元组 classes(来自 https://www.javatuples.org/ 或任何源代码),代码会更简单。