2012-12-20 15 views
12

Mam problem z domyślnym komparatorem dla Ciągów (w SortedSet). Problemem jest to, że domyślna komparator nie sort dobre znaków, który zawiera numery tj W zestawie mam:Sortowanie ciągów zawierających liczbę w Javie

room1, room2, room100 

naturalna kolejność powinna być jak wyżej, ale w zestawie mam:

room1, room100, room2 

I wiem, dlaczego tak jest, ale nie wiem, jak to zmienić.

+6

Musisz utworzyć niestandardową comparartor –

+0

wiem, ale nie mam pojęcia jak go porównać. Wiesz, że próbuję z tym: > int int intortyNumbers (String o1, String o2) { Pattern numberPattern = Pattern.compile (WZORZEC); Matcher matcher1 = numberPattern.matcher (o1); Matcher matcher2 = numberPattern.matcher (o2); int i1 = Integer.parseInt (matcher1.group()); int i2 = Integer.parseInt (matcher2.group()); System.out.println (i1 + "" + i2); powrót i1 - i2; } – MAGx2

+0

Należy wprowadzić założenia, takie jak, czy wszystkie ciągi w formacie ? lub zdecyduj, jaki dokładnie jest twój format, tylko wtedy możesz napisać swój kompilator. –

Odpowiedz

22

Spróbuj porównawczy, który usuwa wszystkie non-cyfrowy znaki następnie porównuje pozostałe znaki jako liczby:

Collections.sort(strings, new Comparator<String>() { 
    public int compare(String o1, String o2) { 
     return extractInt(o1) - extractInt(o2); 
    } 

    int extractInt(String s) { 
     String num = s.replaceAll("\\D", ""); 
     // return 0 if no digits found 
     return num.isEmpty() ? 0 : Integer.parseInt(num); 
    } 
}); 

Oto test:

public static void main(String[] args) throws IOException { 
    List<String> strings = Arrays.asList("room1", "foo", "room2", "room100", "room10"); 
    Collections.sort(strings, new Comparator<String>() { 
     public int compare(String o1, String o2) { 
      return extractInt(o1) - extractInt(o2); 
     } 

     int extractInt(String s) { 
      String num = s.replaceAll("\\D", ""); 
      // return 0 if no digits found 
      return num.isEmpty() ? 0 : Integer.parseInt(num); 
     } 
    }); 
    System.out.println(strings); 
} 

wyjściowa:

[foo, room1, room2, room10, room100] 
+0

Dziękuję :). To działa. Jedyne, co zmienię, to gdy jeden z ciągów będzie normalny bez liczb :). – MAGx2

+0

Dodałem kod, który zajmuje się "brak numerów" dla ciebie – Bohemian

+0

Dlaczego, jeśli uruchomię twój algorytm z listami ciągi = Arrays.asList ("a", "aaa"); wynik jest różny od List stringów = Arrays.asList ("aaa", "a"); ? Uważam, że wynik powinien być taki sam. Proszę mi powiedzieć, czy jestem w błędzie ... – Goose

0

Można zaimplementować komparator i przekazać go do konstruktora zestawu. Zobacz http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Comparator.html.

Jeśli wszystkie twoje ciągi mają postać pokoju [numer], możesz usunąć "pokój", przeanalizować liczbę i porównać przez nią.
Alternatywnie - możesz przechowywać wartości Integers w twoim zestawie i drukować je z prefiksem "pokój".

+0

Ale nie mam pojęcia jak to porównać. Jakiś regex? – MAGx2

+1

Czy ciągi znaków mają zawsze postać pokoju [numer]? –

5

Spróbuj tego. Przyjąłem, że zawsze będziesz miał "pokój" na początku łańcucha.

List<String> list = Arrays.asList("room1", "room100", "room2"); 
    Collections.sort(list, new Comparator<String>() 
    { 
     @Override 
     public int compare(String o1, String o2) 
     { 
      return new Integer(o1.replaceAll("room", "")) 
       .compareTo(new Integer(o2.replaceAll("room", ""))); 
     } 

    }); 
+0

@Vikdor dzięki za edycję – RNJ

2

Oto moja Comparator realizacja dla takiego rodzaju: (łańcuchy mogą zacząć od wszelkich znaków)

public class StringNumberComparator implements Comparator<String>{ 

    @Override 
    public int compare(String o1, String o2) { 
    int i1 = this.getRearInt(o1); 
    int i2 = getLeadingInt(o2); 
    String s1 = getTrailingString(o1); 
    String s2 = getTrailingString(o2); 

    if(i1==i2) 
     return s1.compareTo(s2); 
    if(i1>i2) 
     return 1; 
    else if(i1<i2) 
      return -1; 
    return 0; 
    } 

    private int getRearInt(String s) { 
    s=s.trim(); 
    int i=Integer.MAX_VALUE; 
    try { 
      i = Integer.parseInt(s.split("[^0-9]+")[1]); 
    } catch(ArrayIndexOutOfBoundsException e) { 

    } catch(NumberFormatException f) { 
      return i; 
    } 

    return i; 
    } 

    private String getTrailingString(String s) { 

    return s.replaceFirst("[0-9]", ""); 

} }

0

Leniwy alternatywą byłoby uczynić pracę komparatora String nie robiąc nic dodatkowego (Definiowanie własnego porównawczy). Możesz to zrobić, wypełniając zerami numery w łańcuchu w ten sposób: room0001, room0002, room0100, wtedy domyślny komparator Ciągów będzie działał. Musisz jednak znać maksymalną wartość liczbową, aby odpowiednio dostosować dopełnienie.

4

Używana odpowiedź @bohm. Po prostu trochę się poprawiłem. Ten pracował dla mnie bardzo dobrze ..

 Collections.sort(asdf, new Comparator<String>() { 
      public int compare(String o1, String o2) { 

       String o1StringPart = o1.replaceAll("\\d", ""); 
       String o2StringPart = o2.replaceAll("\\d", ""); 


       if(o1StringPart.equalsIgnoreCase(o2StringPart)) 
       { 
        return extractInt(o1) - extractInt(o2); 
       } 
       return o1.compareTo(o2); 
      } 

      int extractInt(String s) { 
       String num = s.replaceAll("\\D", ""); 
       // return 0 if no digits found 
       return num.isEmpty() ? 0 : Integer.parseInt(num); 
      } 
     }); 
Powiązane problemy