【问题标题】:How to create a custom comparator to sort a numbering system如何创建自定义比较器来对编号系统进行排序
【发布时间】:2015-07-09 06:29:21
【问题描述】:

这是我目前的代码:

package Demo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Demo {

    public static void main(String[] args) {

        ArrayList<String> list = new ArrayList<String>();

        list.add("3.3.x");
        list.add("1.2.x");
        list.add("2.3.x");

        VersionComparator x = new VersionComparator();

        Collections.sort(list, x );

        for(String str : list)
            System.out.println(str);

    }

    static class VersionComparator implements Comparator {
        public int compare(String str1, String str2) {

            String firstNumberStringOne = str1.split(".")[0]; 
            String firstNumberStringTwo = str2.split(".")[0];  
            String secondNumberStringOne = str1.split(".")[1]; 
            String secondNumberStringTwo = str2.split(".")[1];  

            return;
        }

        @Override
        public int compare(Object o1, Object o2) {
            // TODO Auto-generated method stub
            return 0;
        }
    }
}

我想按句点之前出现的第一个数字对列表中的项目进行排序。如果这两个数字相等,则转到第二个数字并比较这些数字。如果相等,则返回它们。

我不知道如何使用比较器。我试图实现它,编译器抱怨我需要在第二个比较方法中添加覆盖。我也不知道如何根据返回方式进行比较。我知道,如果它相等,则返回 0,小于则返回 -1 且大于 1。但在编码方面,我不知道程序如何知道如何对其进行排序。

【问题讨论】:

标签: java sorting comparator


【解决方案1】:

您的VersionComparator 类正在实现Comparator 接口的原始形式,其中compare 方法将采用2 个Objects 而不是2 个Strings。

通过提供String 作为类型参数来实现接口。然后编译器将识别您的compare 方法以Strings 作为参数作为接口的正确实现。您不需要采用Objects 的compare 方法。

static class VersionComparator implements Comparator<String> {

您当然需要在compare method 中实现比较逻辑,如果str1 比较小于、等于或大于@987654336,则返回小于0、0 或大于0 的int @根据您的自定义排序顺序。

【讨论】:

    【解决方案2】:

    您的代码存在一些问题:

    1. 你正在使用原始类型比较器

      class VersionComparator implements Comparator // no <Type> here
      

      所以Comparator&lt;T&gt; 的编译器将尝试使用Object 作为T。这意味着它将把compare(T t1, T t2) 方法编译为compare(Object t1, Object t2)

      如果您希望该方法编译为compare(String t1, String o2) 那么

      • 它只接受String 参数
      • 它可以在内部使用像length()这样的字符串方法

      &lt;String&gt;设置为泛型通过

      class VersionComparator implements Comparator<String>
      

      现在编译器会知道TString

    2. 当您调用split(".") 时,您不是在点上拆分,而是在任何字符(行分隔符除外)上拆分,因为split 使用正则表达式(正则表达式),并且在正则表达式@987654337 @ 表示行分隔符旁边的任何字符。因此,在开始时您将获得空元素数组,然后将被清除,因为split(regex) 删除了空的尾随字符串。更多信息在这里:https://stackoverflow.com/a/21790319/1393766

      要解决此问题,您需要转义 .,例如使用 "\\."

    这应该涵盖代码中的主要问题。但是,如果您想创建好的解决方案,请考虑避免对 Strings 进行排序,而是使用您自己的 Version 类的实例。
    这种方法的优点是您不必每次想要比较两个字符串时都将 String 解析为 int (这可能很昂贵),但您可以将解析后的 "1.2.3" 版本存储为数组 [1, 2, 3] 和在需要时重复使用它。
    Version 类也可以实现 Comparable&lt;Vector&gt; 以提供默认的比较方法。

    所以你的代码看起来更像:

    class Version implements Comparable<Version> {
    
        private String versionStr;
        private int[] arr;
    
        public String getVersionStr() { return versionStr; }
    
        @Override public String toString() { return versionStr; }
    
        @Override
        public int compareTo(Version other) {
    
            int result = Integer.compare(arr[0], other.arr[0]);
    
            if (result != other)
                return result;
    
            return Integer.compare(arr[1], other.arr[1]);
        }
    
        public Version(String versionStr) {
            this.versionStr = versionStr;
            this.arr = Stream.of(versionStr.split("\\."))
                    .limit(2)//get only two first elements
                    .mapToInt(Integer::parseInt)//convert String to int
                    .toArray();//create array with integers
        }
    
    }
    
    class Demo {
    
        public static void main(String[] args) {
    
            List<Version> list = new ArrayList<Version>();
    
            list.add(new Version("3.3.x"));
            list.add(new Version("1.2.x"));
            list.add(new Version("1.11.x"));
            list.add(new Version("2.3.x"));
    
            Collections.sort(list);//with use default order provided by compareTo(Version other)
    
            for (Version str : list)
                System.out.println(str);
        }
    }
    

    【讨论】:

      【解决方案3】:
       public static void main(String[] args) {
          ArrayList<String> list = new ArrayList<String>();
          list.add("3.3.x");
          list.add("1.2.x");
          list.add("1.11.x");
          list.add("1.1.x");
          list.add("2.3.x");
      
          Collections.sort(list, new VersionComparator());
      
          for(String str : list)
             System.out.println(str);
      
      }
      
      static class VersionComparator implements Comparator<String> {
          //Temporary Cache Map to hold Split String value. String as Key and Split String array as value is this in this map.
          Map<String, String[]> mCacheMap = new HashMap<>();
      
          @Override
          public int compare(String string1, String string2) {
      
              if(!mCacheMap.containsKey(string1)){
                  //Put Split String of string1 to map if it does not contain it.
                  mCacheMap.put(string1, string1.split("\\."));
              }
              if(!mCacheMap.containsKey(string2)){
                 //Put Split String of string2 to map if it does not contain it.
                 mCacheMap.put(string2, string2.split("\\."));
              }
              //Get First Digit of first string1 from Map
              Integer string1Val1 = Integer.valueOf(mCacheMap.get(string1)[0]); 
              //Get First Digit of second string2 from Map            
              Integer string2Val1 = Integer.valueOf(mCacheMap.get(string2)[0]);
      
              //Compare Both Digit. compareTo Method return a negative integer, zero, or a positive integer as first Integer value is less than, equal to, or greater than the seconf Integer value.
              int cmp = string1Val1.compareTo(string2Val1);
      
              if( cmp != 0 ){
                  return cmp;
              }else {
                  //If First digit of both string is same compare second digit. 
                  Integer string1Val2 = Integer.valueOf(mCacheMap.get(string1)[1]); 
                  Integer string2Val2 = Integer.valueOf(mCacheMap.get(string2)[1]); 
                  return string1Val2.compareTo(string2Val2);
              }
      
          }
      }
      

      结果:

      1.1.x
      1.2.x
      1.11.x
      2.3.x
      3.3.x
      

      【讨论】:

      • 我会尽快发布更新。
      • 效果更好。现在您只需要向 OP 解释他/她的方法有什么问题以及您的方法为何/如何起作用。
      • 还要考虑避免重复你的代码(你真的需要在每次读取一个元素时重新创建数组吗?)。
      • Ineger.parseInt 返回int,而不是Integer,这意味着编译器需要添加自动装箱代码才能将int 转换为Integer(如Integer i = Integer.valueOf(someInt))。您可以使用Integer.valueOf(string)String 获取Integer
      • @Pshemo Yeh,你有一个有效的观点。我认为在这里使用 substring 方法是一种更好的方法。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-07-11
      • 1970-01-01
      • 1970-01-01
      • 2023-04-06
      • 1970-01-01
      • 2019-11-22
      • 2011-04-11
      相关资源
      最近更新 更多