【问题标题】:How to sort an ArrayList<String> using Comparator?如何使用比较器对 ArrayList<String> 进行排序?
【发布时间】:2018-11-25 23:45:41
【问题描述】:

大家好,我正在尝试为我的游戏制作记分牌,因此我需要对其进行排序。我的输入是 DATE;LEVEL;SCORE,我想按最高分排序,如果它与最高级别相同,如果它与日期相同。

我的数组列表:

ArrayList<String> test = new ArrayList<>();
test.add("16.06.2018;1;10");
test.add("16.06.2018;1;2");
test.add("16.06.2018;1;5");
test.add("16.06.2018;1;1");
test.add("16.06.2018;1;3");
test.add("16.06.2018;2;3");
test.add("15.06.2018;1;3");
test.add("17.06.2018;1;3");

应该排序

[16.06.2018;1;10, 16.06.2018;1;5, 16.06.2018;2;3, 15.06.2018;1;3, 16.06.2018;1;3, 17.06.2018;1;3, 16.06.2018;1;2, 16.06.2018;1;1]; 

但我得到了

[16.06.2018;1;5, 16.06.2018;2;3, 15.06.2018;1;3, 16.06.2018;1;3, 17.06.2018;1;3, 16.06.2018;1;2, 16.06.2018;1;10, 16.06.2018;1;1]

我的代码:

Collections.sort(test, new Comparator<String>() {
    @Override
    public int compare(String A, String B) {
        String[] tmp = A.split(";");
        String[] tmp2 = B.split(";");
        if (tmp[2].equals(tmp2[2])) {
            if (tmp[1].equals(tmp2[1])) {
                return compareDate(tmp[0], tmp2[0]);
            } else {
                return tmp2[1].compareTo(tmp[1]);
            }
        } else {
            return tmp2[2].compareTo(tmp[2]);
        }
    }

    //compares 2 dates
    private int compareDate(String A, String B) {
        String[] tmp = A.split("\\.");
        String[] tmp2 = B.split("\\.");
        if (tmp[2].equals(tmp2[2])) {
            if (tmp[1].equals(tmp2[1])) {
                return tmp[0].compareTo(tmp2[0]);
            } else {
                return tmp[1].compareTo(tmp2[1]);
            }
        } else {
            return tmp[2].compareTo(tmp2[2]);
        }
    }
});

【问题讨论】:

    标签: java sorting arraylist comparator


    【解决方案1】:

    您正在使用基于字符串的词法比较,它将 "5" 视为大于 "10"(因为字符 '5' 在 Unicode 表中位于 '1' 之后)。

    您应该使用数值比较。将字符串转换为整数并将它们与Integer.compare 或类似的进行比较:

    而不是这个:

    return tmp2[2].compareTo(tmp[2]);
    

    你可以这样做:

    return Integer.compare(
        Integer.parseInt(tmp2[2]),
        Integer.parseInt(tmp[2])
    );
    

    【讨论】:

      【解决方案2】:

      如果您使用的是 Java 8,我想从该字符串创建一个对象,以便您轻松比较:

      test.stream()
              .map(c -> {
                  String[] tmp = c.split(";");
                  MyObject obj = new MyObject(
                          LocalDate.parse(tmp[0], DateTimeFormatter.ofPattern("dd.MM.yyyy")),
                          Integer.valueOf(tmp[1]), Integer.valueOf(tmp[2])
                  );
                  return obj;
      
              }).sorted(
              Comparator.comparing(MyObject::getDate)
                      .thenComparing(MyObject::getLevel)
                      .thenComparing(MyObject::getScore));
      

      有了这个对象:

      class MyObject {
          private LocalDate date;
          private Integer level;
          private Integer score;
      
          public MyObject(LocalDate date, Integer level1, Integer score) {
              this.date = date;
              this.level = level;
              this.score= score;
          }
      
          public MyObject() {
          }
          //getter setter
      }
      

      或者没有对象:

      test.stream().map(c -> c.split(";")).sorted(
              Comparator.comparing(a -> LocalDate.parse(((String[]) a)[0], DateTimeFormatter.ofPattern("dd.MM.yyyy")))
                      .thenComparing(a -> Integer.valueOf(((String[]) a)[1]))
                      .thenComparing(a -> Integer.valueOf(((String[]) a)[2])));
      

      注意:你可以把它们按你想要的顺序排列,这样你就会得到预期的结果

      【讨论】:

        【解决方案3】:

        我喜欢@YCF_L 的方法以及@jspcal 如何切中要害。我通常会把它分解成这样的可重用组件。

        public static void sort(List<String> data) {
            Collections.sort(data, new DataComparator());
        }
        
        private static class DataComparator implements Comparator<String>
        {
            @Override
            public int compare(String str1, String str2) {
                DataObject data1 = DataObject.valueOf(str1);
                DataObject data2 = DataObject.valueOf(str2);
                return data1.compareTo(data2);
            }
        }
        
        private static class DataObject implements Comparable<DataObject>
        {
            private static final Map<String,DataObject> valuesCache = new HashMap<String,DataObject>();
        
            private LocalDate date;
            private int value1;
            private int value2;
        
            /**
             * Parse the "date;value1;value2" String into an Object.
             * @param value the string
             * @throws ParseException if the date is invalid
             */
            public DataObject(String value) {
                String[] values = value.split(";");
                this.date = LocalDate.parse(values[0], DateTimeFormatter.ofPattern("dd.MM.yyyy"));
                this.value1 = Integer.valueOf(values[1]);
                this.value2 = Integer.valueOf(values[2]);
            }
        
            /**
             * Parse the String into an object.
             * @param str the string
             * @return the data object
             */
            public static DataObject valueOf(String str) {
                DataObject data = valuesCache.get(str);
                if (data == null) {
                    data = new DataObject(str);
                    valuesCache.put(str, data);
                }
                return data;
            }
        
            /**
             * Compare this DataObject to the other DataObject.
             */
            @Override
            public int compareTo(DataObject other) {
                int cmp = 0;
                if (this != other) {
                    // first compare the value2 integers
                    // sort descending (higher first) by multiplying by -1
                    cmp = -1 * Integer.compare(this.value2, other.value2);
        
                    // if those values matched, then compare value1 integers
                    // also sort descending
                    if (cmp == 0) {
                        cmp = -1 * Integer.compare(this.value1, other.value1);
                    }
        
                    // if those values matched, then compare dates ascending
                    if (cmp == 0) {
                        cmp = this.date.compareTo(other.date);
                    }
                }
                return cmp;
            }
        
            @Override
            public String toString() {
                return String.format("%s;%d;%d", date, value1, value2);
            }
        }
        

        【讨论】:

        • 更新为使用比 SimpleDateFormat 更简洁的 LocalDate。谢谢@YCF_L。
        猜你喜欢
        • 2014-03-01
        • 1970-01-01
        • 2014-04-14
        • 1970-01-01
        • 2020-05-17
        • 2016-08-28
        • 1970-01-01
        相关资源
        最近更新 更多