如何在 java 中编写自定义比较器?
How to write custom comparator in java?
我想在 TreeMap
中存储键值对,并按照以下逻辑根据键的值对条目进行排序:
- “类型”(不区分大小写)键应该在第一位。
- 以“元数据”开头的键(不区分大小写)最后应按升序排列
- 其余键(不区分大小写)应按升序排列在中间
我正在使用 Java 8 版本。
节目:
public class CustomeCamarator {
public static void main(String[] args) {
CustomComparator comparator = new CustomComparator();
Map<String,Object> empData=new TreeMap<>(comparator);
empData.put("name","someName");
empData.put("DOB","someDOB");
empData.put("address","someAddress");
empData.put("type","employee data");
empData.put("ContactNo.","someContactNumber");
empData.put("metadata.source","someMetaDataSource");
empData.put("metadata.location","someMetaDataLocation");
empData.put("metadata","someMetaData");
System.out.println(empData);
System.out.println(empData.containsKey("metadata"));//should be true but showing false
}
}
class CustomComparator implements Comparator<String>{
@Override
public int compare(String o1, String o2) {
String str1 = o1.toLowerCase();
String str2 = o2.toLowerCase();
if(str1.equalsIgnoreCase("type")) {
return -1;
}else if(!str1.contains("metadata") && !str2.contains("metadata")) {
return str1.compareTo(str2);
}else if(o1.contains("metadata") && !o2.contains("metadata")) {
return 1;
}else if(!o1.contains("metadata") && o2.contains("metadata")) {
return -1;
}else {
return 1;
}
}
}
**Expected Output like this:**
type: someType
address: someAddress
ContactNo: someContactNumber
DOB: someDOB
name: someName
metadata: someMetaData
metadata.location: someMetaDataLocation
metadata.source: someMetaDataSource
TreeMap
supports a custom Comparator
in its constructor。只需实现 Comparator
接口并将其传递给构造函数即可。
您应该将实现该逻辑的比较器传递给 TreeMap
构造函数:
Map<String, Integer> order = Map.of(
"type", Integer.MIN_VALUE,
"metadata", Integer.MAX_VALUE);
Map<String, Object> map = new TreeMap<>(
Comparator.comparing(a -> order.getOrDefault(
a.toLowerCase().startsWith("metadata") ?
"metadata" :
a.toLowerCase(),
0))
.thenComparing(Comparator.naturalOrder()));
我认为应该这样做。这个想法是使用地图首先比较一些 Integer
值。 type
对应的值是Integer
的最小值,所以它总是排在第一位。 metadata
的值是 Integer
的最大值,因此它总是排在最后。在地图中搜索之前,我们首先检查字符串是否以 metadata
开头。确实如此,我们只需将其更改为 metadata
,即可从地图中获取 Integer
值。如果地图中没有条目,我们 return 0
。如果有平局,我们使用 String
的自然顺序。
编辑:
如果您使用 Java8 并且不能使用 Map.of
,请考虑使用传统的 HashMap
:
Map<String, Integer> order = new HashMap<>();
order.put("type", Integer.MIN_VALUE);
order.put("metadata", Integer.MAX_VALUE);
我用了一个Decision Table如下
type metadata.* others
2 1 0
Key 1 Key 2 Result Action
0 0 0 String.compare
0 1 1 return -1
0 2 2 return 1
1 0 3 return 1
1 1 4 String.compare
1 2 5 return 1
2 0 6 return -1
2 1 7 return -1
2 2 8 return 0
Key 1和Key 2是接口Comparator
的方法compare()的参数。每个键可以具有三个值之一:
- 类型
- [开头为] 元数据
以上- none
实现如下:
Comparator<String> comparator = (k1, k2) -> {
Objects.requireNonNull(k1);
Objects.requireNonNull(k2);
int k1Val;
int k2Val;
if (k1.equalsIgnoreCase("type")) {
k1Val = 2;
}
else if (k1.matches("(?i)^metadata.*$")) {
k1Val = 1;
}
else {
k1Val = 0;
}
if (k2.equalsIgnoreCase("type")) {
k2Val = 2;
}
else if (k2.matches("(?i)^metadata.*$")) {
k2Val = 1;
}
else {
k2Val = 0;
}
int retVal;
int index = k1Val * 3 + k2Val;
switch (index) {
case 0:
case 4:
retVal = k1.compareToIgnoreCase(k2);
break;
case 1:
case 6:
case 7:
retVal = -1;
break;
case 2:
case 3:
case 5:
retVal = 1;
break;
case 8:
retVal = 0;
break;
default:
throw new RuntimeException("Unhandled: " + index);
}
return retVal;
};
Map<String, Object> empData = new TreeMap<>(comparator);
empData.put("name","someName");
empData.put("address","someAddress");
empData.put("type","employee data");
empData.put("ContactNo.","someContactNumber");
empData.put("Metadata.source","someMetaDataSource");
empData.put("metadata.location","someMetaDataLocation");
empData.put("metadata","someMetaData");
System.out.println(empData);
上述代码 运行 的输出。
{type=employee data, address=someAddress, ContactNo.=someContactNumber, name=someName, metadata=someMetaData, metadata.location=someMetaDataLocation, Metadata.source=someMetaDataSource}
我认为以下涵盖了您的案例
public class CustomComparator implements Comparator<String> {
@Override public int compare(String left, String right) {
left = left.toLowerCase();
right = right.toLowerCase();
final int LEFT = -1;
final int RIGHT = 1;
// exact match!
if (left.equals(right)) return 0;
// not identical, so consider 'type' match
if ("type".equals(left)) return LEFT;
if ("type".equals(right)) return RIGHT;
// at this point we know neither matches 'type' so lets check for 'metadata' prefix
if (left.startsWith("metadata")) {
// if both start with metadata use natural ordering
if (right.startsWith("metadata")) return left.compareTo(right);
// only left starts with 'metadata' so right comes first
else return RIGHT;
}
// only right starts with 'metadata' so left comes first
if (right.startsWith("metadata")) return LEFT;
// at this point we know they are not equal but neither contains 'text' nor starts with 'metadata' so use natural ordering
return left.compareTo(right);
}
}
我想在 TreeMap
中存储键值对,并按照以下逻辑根据键的值对条目进行排序:
- “类型”(不区分大小写)键应该在第一位。
- 以“元数据”开头的键(不区分大小写)最后应按升序排列
- 其余键(不区分大小写)应按升序排列在中间
我正在使用 Java 8 版本。
节目:
public class CustomeCamarator {
public static void main(String[] args) {
CustomComparator comparator = new CustomComparator();
Map<String,Object> empData=new TreeMap<>(comparator);
empData.put("name","someName");
empData.put("DOB","someDOB");
empData.put("address","someAddress");
empData.put("type","employee data");
empData.put("ContactNo.","someContactNumber");
empData.put("metadata.source","someMetaDataSource");
empData.put("metadata.location","someMetaDataLocation");
empData.put("metadata","someMetaData");
System.out.println(empData);
System.out.println(empData.containsKey("metadata"));//should be true but showing false
}
}
class CustomComparator implements Comparator<String>{
@Override
public int compare(String o1, String o2) {
String str1 = o1.toLowerCase();
String str2 = o2.toLowerCase();
if(str1.equalsIgnoreCase("type")) {
return -1;
}else if(!str1.contains("metadata") && !str2.contains("metadata")) {
return str1.compareTo(str2);
}else if(o1.contains("metadata") && !o2.contains("metadata")) {
return 1;
}else if(!o1.contains("metadata") && o2.contains("metadata")) {
return -1;
}else {
return 1;
}
}
}
**Expected Output like this:**
type: someType
address: someAddress
ContactNo: someContactNumber
DOB: someDOB
name: someName
metadata: someMetaData
metadata.location: someMetaDataLocation
metadata.source: someMetaDataSource
TreeMap
supports a custom Comparator
in its constructor。只需实现 Comparator
接口并将其传递给构造函数即可。
您应该将实现该逻辑的比较器传递给 TreeMap
构造函数:
Map<String, Integer> order = Map.of(
"type", Integer.MIN_VALUE,
"metadata", Integer.MAX_VALUE);
Map<String, Object> map = new TreeMap<>(
Comparator.comparing(a -> order.getOrDefault(
a.toLowerCase().startsWith("metadata") ?
"metadata" :
a.toLowerCase(),
0))
.thenComparing(Comparator.naturalOrder()));
我认为应该这样做。这个想法是使用地图首先比较一些 Integer
值。 type
对应的值是Integer
的最小值,所以它总是排在第一位。 metadata
的值是 Integer
的最大值,因此它总是排在最后。在地图中搜索之前,我们首先检查字符串是否以 metadata
开头。确实如此,我们只需将其更改为 metadata
,即可从地图中获取 Integer
值。如果地图中没有条目,我们 return 0
。如果有平局,我们使用 String
的自然顺序。
编辑:
如果您使用 Java8 并且不能使用 Map.of
,请考虑使用传统的 HashMap
:
Map<String, Integer> order = new HashMap<>();
order.put("type", Integer.MIN_VALUE);
order.put("metadata", Integer.MAX_VALUE);
我用了一个Decision Table如下
type metadata.* others
2 1 0
Key 1 Key 2 Result Action
0 0 0 String.compare
0 1 1 return -1
0 2 2 return 1
1 0 3 return 1
1 1 4 String.compare
1 2 5 return 1
2 0 6 return -1
2 1 7 return -1
2 2 8 return 0
Key 1和Key 2是接口Comparator
的方法compare()的参数。每个键可以具有三个值之一:
- 类型
- [开头为] 元数据 以上
- none
实现如下:
Comparator<String> comparator = (k1, k2) -> {
Objects.requireNonNull(k1);
Objects.requireNonNull(k2);
int k1Val;
int k2Val;
if (k1.equalsIgnoreCase("type")) {
k1Val = 2;
}
else if (k1.matches("(?i)^metadata.*$")) {
k1Val = 1;
}
else {
k1Val = 0;
}
if (k2.equalsIgnoreCase("type")) {
k2Val = 2;
}
else if (k2.matches("(?i)^metadata.*$")) {
k2Val = 1;
}
else {
k2Val = 0;
}
int retVal;
int index = k1Val * 3 + k2Val;
switch (index) {
case 0:
case 4:
retVal = k1.compareToIgnoreCase(k2);
break;
case 1:
case 6:
case 7:
retVal = -1;
break;
case 2:
case 3:
case 5:
retVal = 1;
break;
case 8:
retVal = 0;
break;
default:
throw new RuntimeException("Unhandled: " + index);
}
return retVal;
};
Map<String, Object> empData = new TreeMap<>(comparator);
empData.put("name","someName");
empData.put("address","someAddress");
empData.put("type","employee data");
empData.put("ContactNo.","someContactNumber");
empData.put("Metadata.source","someMetaDataSource");
empData.put("metadata.location","someMetaDataLocation");
empData.put("metadata","someMetaData");
System.out.println(empData);
上述代码 运行 的输出。
{type=employee data, address=someAddress, ContactNo.=someContactNumber, name=someName, metadata=someMetaData, metadata.location=someMetaDataLocation, Metadata.source=someMetaDataSource}
我认为以下涵盖了您的案例
public class CustomComparator implements Comparator<String> {
@Override public int compare(String left, String right) {
left = left.toLowerCase();
right = right.toLowerCase();
final int LEFT = -1;
final int RIGHT = 1;
// exact match!
if (left.equals(right)) return 0;
// not identical, so consider 'type' match
if ("type".equals(left)) return LEFT;
if ("type".equals(right)) return RIGHT;
// at this point we know neither matches 'type' so lets check for 'metadata' prefix
if (left.startsWith("metadata")) {
// if both start with metadata use natural ordering
if (right.startsWith("metadata")) return left.compareTo(right);
// only left starts with 'metadata' so right comes first
else return RIGHT;
}
// only right starts with 'metadata' so left comes first
if (right.startsWith("metadata")) return LEFT;
// at this point we know they are not equal but neither contains 'text' nor starts with 'metadata' so use natural ordering
return left.compareTo(right);
}
}