2011-10-19 105 views
3

我有一個包含這樣的數據串矢量:如何對包含數字的字符串集合進行排序?

5:34,5:38,17:21,22:11,...

如果我嘗試合併這使用Collections.sort(...);會出現這樣的:

17:21,22:11,5:34,5:38

其實我希望它看起來是這樣的:

5 :34,5:38,17點21分,22時11分

所以想要的元素根據冒號前的數排序:如果某些元件具有前相同的數目,然後「」「:」的按照「:」後面的數字對它們進行排序。

這樣做的最簡單方法是什麼?

回答

2

您既可以創建自定義Comparator拆分String並將其解析爲兩個整數,創建一個定製類來代表每個String並將其存儲在Collection中。我贊成後一種方法,因爲你只會招致分裂/解析字符串一次的開銷;例如

public class Data implements Comparable<Data> { 
    private final int prefix; 
    private final int suffix; 

    public Data(String str) { 
    String[] arr = str.split(":"); 

    if (arr.length != 2) { 
     throw new IllegalArgumentException(); 
    } 

    this.prefix = Integer.parseInt(arr[0]); 
    this.suffix = Integer.parseInt(arr[1]); 
    } 

    public int compareTo(Data data) { 
    // Should really avoid subtraction in case of overflow but done to keep code brief. 
    int ret = this.prefix - data.prefix; 

    if (ret == 0) { 
     ret = this.suffix - data.suffix; 
    } 

    return ret; 
    } 

    // TODO: Implement equals and hashCode (equals to be consistent with compareTo). 

    public String toString() { return String.format("%d:%d", prefix, suffix); } 
} 

然後它只是在你的Collection存儲一些Data對象的情況;例如

List<Data> l = new ArrayList<Data>(); 
l.add(new Data("13:56")); 
l.add(new Data("100:16")); 
l.add(new Data("9:1")); 
Collections.sort(l); 

一兩件事 - 你提到你使用的是Vector。您應該儘量避免使用Vector/Hashtable,因爲它們已被List/Map取代,它們是作爲JDK 1.2的集合框架的一部分引入的。

+0

我試過你的解決方案,它的工作原理非常完美。我可以使用ArrayList而不是Vector。但最後一件事:如果我在Vector或ArrayList中有2個重複項像「13:56」和「13:56」,我怎樣才能跳過重複並插入一次只保留列表排序? – Brad

+2

如果您希望避免重複的問題,您可能需要考慮將Comparable對象插入SortedSet而不是List(例如TreeSet)。這將避免必須對數據進行明確排序。 – Adamski

+0

非常感謝。很好地工作。 – Brad

0

創建一個java.util.Comparator並將其提供給sort方法。

0

實現您自己的Comparator類,比較兩個值並調用Collections.sort(List list, Comparator c)

0

實施您自己的比較儀並將其作爲Colelctions.sort方法的第二個參數。

4

這是非常低效的,但它應該做的工作。

Collections.sort(data, new Comparator<String>(){ 
    public int compare(String a, String b){ 
     String[] as = a.split(":"); 
     String[] bs = b.split(":"); 
     int result = Integer.valueOf(as[0]).compareTo(Integer.valueOf(bs[0])); 
     if(result==0) 
      result = Integer.valueOf(as[1]).compareTo(Integer.valueOf(bs[1])); 
     return result; 
    } 
}) 

(提示:如果它是我的代碼,我會優化它使用子代替String.split(),但我懶得)

+0

我想寫相同的代碼;) –

7

正確這樣做的方法是不將非字符串值存儲爲字符串。

集合中的數據有一些結構和規則,不能是任意字符串。因此,您不應使用String數據類型。

讓我們定義一個名爲TwoNumbers型(因爲我不知道是什麼類型應該代表,即使我能猜出):

class TwoNumbers implements Comparable<TwoNumbers> { 
    private final int num1; 
    private final int num2; 

    public TwoNumbers(int num1, int num2) { 
     if (num1 <= 0 || num2 <= 0) { 
      throw new IllegalArgumentException("Numbers must be positive!"); 
     } 
     this.num1 = num1; 
     this.num2 = num2; 
    } 

    public static TwoNumbers parse(String s) { 
     String[] parts = s.split(":"); 
     if (parts.length != 2) { 
      throw new IllegalArgumentException("String format must be '<num>:<num>'"); 
     } 
     try { 
      return new TwoNumbers(Integer.parseInt(parts[0]), Integer.parseInt(parts[0])); 
     } catch (NumberFormatException e) { 
      throw new IllegalArgumentException("parts must be numeric!", e); 
     } 
    } 

    public int getNum1() { 
     return num1; 
    } 

    public int getNum2() { 
     return num2; 
    } 

    @Override 
    public int compareTo(TwoNumbers o) { 
     if (o == null) { 
      return 1; 
     } 
     int diff = Integer.compare(o.num1, this.num1); 
     if (diff == 0) { 
      diff = Integer.compare(o.num2, this.num2); 
     } 
     return diff; 
    } 
} 

compareTo方法存在爲the Comparable interface實現:它定義瞭如何這種類型的對象是有序的。

我已經使用了final字段(並且不提供setter),因爲類實現了immutable objects

這樣你可以直接對數據進行排序,不需要額外的Comparator,也不需要在程序中分發所有的「拆分和解析」代碼。相反,您有一個單個類負責處理該特定格式,所有其他代碼段可以使用該類。

+0

+1使用對象 –

+0

@Joachim ..感謝您的好解決方案。只有2個問題:你爲什麼使用「最終」字段?和什麼是比較(...)方法? – Brad

+0

@Brad:我已經用一些細節和幾個鏈接更新了我的答案。 –

0

通常,將Java中的對象(包括集合)與其默認的hashCode()和equals()方法進行比較。對於內置的對象和數據類型(如String,Integet等),hashCode()是內部計算的,因此它們被JLS(Java語言規範)保證使用。由於我們不能總是依賴於默認/內置對象,我們需要處理自己的自定義對象(如Employee,Customer等),所以我們應該重寫hashCode()和equals() )方法,以便我們可以根據自定義類的對象的「最佳」相等來提供真/假。類似地,sort()涉及一個比較行爲,它確實需要一個比較器(這是一個實現比較方法的重載方法的比較器接口的類)。您還應該重寫比較方法,該方法需要比較兩個對象並返回一個結果(第一個對象大於第二個,第二個對於第一種情況的反向,結果爲0)。

現在,您的數據應該以不同於正常比較的不同方式處理。您需要將數據拆分爲兩部分(使用可拆分方法),然後您可以對這兩個部分(冒號前的第一部分,冒號後的第二部分)進行個別比較。

最後,你應該提供這個自定義比較的排序方法的一個實例,最終將做自定義排序爲您的自定義數據:)

0

我覺得這是非常簡單的:

public class NumericalStringSort { 

    public static void main(String[] args) { 
     List<String> input = Arrays.asList(new String[] {"17:21", "22:11", "5:34", "5:38"}); 
     Collections.sort(input, new NumericalStringComparator()); 
     System.out.println(input); 
    } 

    public static class NumericalStringComparator implements Comparator<String> { 
     public int compare(String object1, String object2) { 
      return pad(object1).compareTo(pad(object2)); 
     } 

     private String pad(String input) { 
      return input.indexOf(":") == 1 ? "0" + input : input; 
     } 
    } 
} 
+0

此比較器僅根據「:」之前的數字進行排序,並忽略「:」之後的數字。如果還有「5:7」呢?它會在你的例子中的「5:38」之後出現。 – Brad

+0

@Brad沒有給出冒號後面一個數字的例子。我認爲這些是沒有前導零的時間字符串。確實,如果您對輸入格式做出其他假設,則需要使用不同的比較器實現。 –

0

剛發現這個(相當陳舊)的帖子,答案並沒有完全解決我的問題。我需要一個更通用的解決方案,因爲這些值是用戶輸入,像「abc 1 a 12」和「abc 1 a 1」這樣的東西應按照包含的數字排序。所以我寫了下面的比較:

new Comparator<String>() { 

     @Override 
     public int compare(String o1, String o2) { 
      String[] s1=splitNumeric(o1); 
      String[] s2=splitNumeric(o2); 
      for (int x=0;x<s1.length&&x<s2.length;x++){ 
       if (!s1[x].equals(s2[x])){ 
        if (s1[x].charAt(0)=='N' && s2[x].charAt(0)=='N'){ 
         long l1=Long.parseLong(s1[x].substring(1)); 
         long l2=Long.parseLong(s2[x].substring(1)); 
         return (int)Math.signum(l1-l2); 
        } 
        break; 
       } 
      } 
      return o1.compareTo(o2); 
     } 
    } 

雖然功能splitNumeric定義如下:

private String[] splitNumeric(String s){ 
     final String numbers=""; 
     LinkedList<String> out=new LinkedList<String>(); 
     int state=-1; 
     for (int x=0;x<s.length();x++){ 
      if (numbers.contains(s.charAt(x)+"")){ 
       if (state==1) 
        out.set(out.size()-1,out.getLast()+s.charAt(x)); 
       else{ 
        state=1; 
        out.add("N"+s.charAt(x)); 
       } 
      } 
      else{ 
       if (state==0) 
        out.set(out.size()-1,out.getLast()+s.charAt(x)); 
       else{ 
        state=0; 
        out.add("S"+s.charAt(x)+""); 
       } 
      } 
     } 
     return out.toArray(new String[0]); 
    } 

如下的代碼將排序字符串

"X 124 B" 
"X 1 Y" 
"X 111 Z" 
"X 12 Y" 
"12:15" 
"12:13" 
"12:1" 
"1:1" 
"2:2" 

"1:1" 
"2:2" 
"12:1" 
"12:13" 
"12:15" 
"X 1 Y" 
"X 12 Y" 
"X 111 Z" 
"X 124 B" 

享受:)

相關問題