如何在Java中编写自定义比较器?

问题描述

我想将键值对存储在TreeMap中,并根据以下逻辑基于键的值对条目进行排序:

  1. “类型”(不区分大小写)键应放在首位。
  2. 以“元数据”(不区分大小写)开头的密钥应最后以升序排列
  3. 其余键(不区分大小写)应按升序排列在中间

我正在使用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接口并将其传递给构造函数即可。

,

您应该将实现该逻辑的比较器传递给Prepare()构造函数:

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。有关系,我们使用0的自然顺序。


编辑: 如果您使用的是Java8,并且无法使用String,请考虑改用传统的Map.of

HashMap
,

我按如下方式使用了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

键1 键2 是接口Comparator的方法compare()的参数。每个键可以具有以下三个值之一:

  1. 类型
  2. [以]元数据开头
  3. 以上都不是

这里是实现:

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);
  }
}

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...