英文:
How to write custom comparator in java?
问题
我想在`TreeMap`中存储键值对,并根据以下逻辑对条目进行排序:
1. "type"(不区分大小写)键应排在第一位。
2. 以"metadata"(不区分大小写)开头的键应按升序排在最后。
3. 其余键(不区分大小写)应按升序排在中间。
我使用的是Java 8版本。
程序:
```java
public class CustomComparator {
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")); // 应该为true,但显示为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;
}
}
}
期望的输出如下所示:
type: someType
address: someAddress
ContactNo: someContactNumber
DOB: someDOB
name: someName
metadata: someMetaData
metadata.location: someMetaDataLocation
metadata.source: someMetaDataSource
<details>
<summary>英文:</summary>
I want to store key-value pairs in `TreeMap` and sort the entries based on the value of key as per the following logic:
1. "type" (case insensitive) key should be at first.
2. the key start with "metadata" (case insensitive) should be at last in ascending order
3. rest of the keys(case insensitive) should be in middle in ascending order
I am using Java 8 version.
Program:
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
</details>
# 答案1
**得分**: 0
你应该将实现该逻辑的比较器传递给 `TreeMap` 构造函数:
```java
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
值。如果映射中没有条目,则返回 0
。如果存在平局,我们使用 String
的自然顺序。
编辑:
如果你使用的是 Java 8 并且无法使用 Map.of
,考虑改为使用传统的 HashMap
:
Map<String, Integer> order = new HashMap<>();
order.put("type", Integer.MIN_VALUE);
order.put("metadata", Integer.MAX_VALUE);
英文:
You should pass a comparator that implements that logic to the TreeMap
constructor:
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()));
I think this should do it. The idea is to use a map to first compare by some Integer
value. The value corresponding to type
is Integer
's min value, so that it always comes first. The value for metadata
is Integer
's max value, so that it always comes last. Before searching in the map, we first check if the string starts with metadata
. It it does, we just change it to metadata
, to obtain the Integer
value from the map. If there is no entry in the map, we return 0
. It there's a tie, we use String
's natural order.
EDIT:
If you are on Java8 and cannot use Map.of
, consider using a traditional HashMap
instead:
Map<String, Integer> order = new HashMap<>();
order.put("type", Integer.MIN_VALUE);
order.put("metadata", Integer.MAX_VALUE);
答案2
得分: 0
我使用了一个决策表,如下所示:
<!-- language: lang-none -->
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() 的参数。每个键可以有以下三个值之一:
- type
- [以] metadata 开头
- 以上都不是
以下是实现部分:
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);
运行上述代码的输出。
<!-- language: lang-none -->
{type=employee data, address=someAddress, ContactNo.=someContactNumber, name=someName, metadata=someMetaData, metadata.location=someMetaDataLocation, Metadata.source=someMetaDataSource}
英文:
I used a Decision Table as follows
<!-- language: lang-none -->
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 and Key 2 are the parameters to method compare() of interface Comparator
. Each key can have one of three values:
- type
- [starts with] metadata
- none of the above
Here is the implementation:
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);
Output from running above code.
<!-- language: lang-none -->
{type=employee data, address=someAddress, ContactNo.=someContactNumber, name=someName, metadata=someMetaData, metadata.location=someMetaDataLocation, Metadata.source=someMetaDataSource}
答案3
得分: 0
我认为以下内容涵盖了您的情况:
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;
// 完全匹配!
if (left.equals(right)) return 0;
// 非完全相同,考虑 'type' 匹配
if ("type".equals(left)) return LEFT;
if ("type".equals(right)) return RIGHT;
// 到这一步,我们知道都不匹配 'type',所以检查是否有 'metadata' 前缀
if (left.startsWith("metadata")) {
// 如果两者都以 'metadata' 开头,则使用自然顺序
if (right.startsWith("metadata")) return left.compareTo(right);
// 只有左侧以 'metadata' 开头,因此右侧较前
else return RIGHT;
}
// 只有右侧以 'metadata' 开头,因此左侧较前
if (right.startsWith("metadata")) return LEFT;
// 到这一步,我们知道它们不相等,但既不包含 'text',也不以 'metadata' 开头,因此使用自然顺序
return left.compareTo(right);
}
}
英文:
I think the following covers your cases
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);
}
}
答案4
得分: -1
TreeMap
在其构造函数中支持自定义的Comparator
。只需实现Comparator
接口并将其传递给构造函数。
英文:
TreeMap
supports a custom Comparator
in its constructor. Just implement the Comparator
interface and pass it to the constructor.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论