4

I have this list of Strings

Car1

Car2

Car3

......


Carn

I want to sort this list according to numerics in it.
for instance if I have Car3, Car1, Car12, Car45, I want it to be sorted like Car1, Car3, Car12, Car45.
I have used Collections.sort(), but it returns something like Car1, car12, car3, Car45.
what should I do to put it in correct order?

Jubin Patel
  • 1,959
  • 19
  • 38
Just_another_developer
  • 5,737
  • 12
  • 50
  • 83
  • 3
    Have you seen [this question :)](http://stackoverflow.com/questions/14337682/sorting-alphanumeric-strings-java) – Freak Jun 26 '13 at 09:44
  • Check this http://stackoverflow.com/questions/104599/sort-on-a-string-that-may-contain-a-number – Rohan Jun 26 '13 at 09:46

9 Answers9

6

You need a custom Comparator, something like

    Collections.sort(list, new Comparator<String>() {
        public int compare(String s1, String s2) {
            int i1 = Integer.parseInt(s1.replaceAll("\\D", ""));
            int i2 = Integer.parseInt(s2.replaceAll("\\D", ""));
            return Integer.compare(i1, i2);
        }
    });
Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275
  • +1, but for performance I'd compile the regex and use a positive match to extract the numbers. Comparators usually are performance-critical. – Marko Topolnik Jun 26 '13 at 09:54
4

Use this:

class ComparatorOfNumericString implements Comparator<String>{

    public int compare(String string1, String string2) {
        // TODO Auto-generated method stub
        String a = string1.substring(3);
        String b = string2.split(3);
        return Integer.parseInt(a)-Integer.parseInt(b);
    }
}

Now when sorting pass this comparator as follows:

Collections.sort(stringList,new ComparatorOfNumericString ());
ajay.patel
  • 1,957
  • 12
  • 15
3

The sort is lexicographical and you get this order because 1 is smaller than 3 in the alphabet and the comparer disregards what comes next. What you need to do is to either write your own comparer, that will cut the "Car" leaving only the number in the string, then you need to parse that string representation of the number to an int and compare the ints ( as one of the solutions )

user2511124
  • 158
  • 5
3

You need to specify a custom Comparator<String> to the Collections.sort() method that defines the ordering that you want for the strings.

This works for a number at any position in the string, and also handles strings that have no numbers (falls back to ordinary string comparison with natural ordering):

final Pattern intsOnly = Pattern.compile("\\d+");

Comparator<String> comparator = new Comparator<String>() {
    @Override
    public int compare(final String string1, final String string2) {
        String int1 = null;
        String int2 = null;
        Matcher matcher1 = intsOnly.matcher(string1);
        if (matcher1.find()) {
            int1 = matcher1.group();
            Matcher matcher2 = intsOnly.matcher(string2);
            if (matcher2.find()) {
                int2 = matcher2.group();
            }
        }
        if (int1 == null || int2 == null) { return string1.compareTo(string2); }
        return Integer.valueOf(int1).compareTo(Integer.valueOf(int2));
    }
};
Collections.sort(strings, comparator);

Of course this means that Truck1 will come before Car2, but that is how you wanted it according to your question.

UPDATE: The above solution does not make sure that Car6 will come before Truck6. If you also want to fall back to natural string ordering in case of equal numbers, use this:

final Pattern intsOnly = Pattern.compile("\\d+");

Comparator<String> comparator = new Comparator<String>() {
    @Override
    public int compare(final String string1, final String string2) {
        String int1 = null;
        String int2 = null;
        Matcher matcher1 = intsOnly.matcher(string1);
        if (matcher1.find()) {
            int1 = matcher1.group();
            Matcher matcher2 = intsOnly.matcher(string2);
            if (matcher2.find()) {
                int2 = matcher2.group();
            }
        }
        int result = 0;
        if (int1 != null && int2 != null) {
            result = Integer.valueOf(int1).compareTo(Integer.valueOf(int2));
        }
        if (result == 0) {
            return string1.compareTo(string2);
        }
        else {
            return result;
        }
    }
};
Collections.sort(strings, comparator);
herman
  • 11,740
  • 5
  • 47
  • 58
1

You will need to create a custom Comparator that does the sorting how you want it. You can then call

Collections.sort(myList, myComparator)

See Collections.sort(List, Comparator).

You can read up on comparators here: Implementing Java Comparator

Community
  • 1
  • 1
cowls
  • 24,013
  • 8
  • 48
  • 78
1

You need to use the overloading of sort with the comparator. The following code is for your specific strings (CarXXX):

Collections.sort(carList, new Comparator<String>() {
    public int compare(String s1, String s2) {
        int t1 = Integer.parseInt(s1.substring(3));
        int t2 = Integer.parseInt(s2.substring(3));
        return t1 - t2;
    }
});
Aleks G
  • 56,435
  • 29
  • 168
  • 265
1

Collections.sort() is correct, sou just have to write the comparator:

public class testComp implements Comparator<String> {

@Override
public int compare(String o1, String o2) {
    int number1 = Integer.parseInt(o1.substring(3, o1.length()));
    int number2 = Integer.parseInt(o2.substring(3, o2.length()));
    if (number1 > number2) {
        return -1;
    } else {
        return 1;
    }
}
  • Pardon my ignorance, what is this with -1 and 1 .... what they indicate? – Just_another_developer Jun 26 '13 at 10:10
  • This works only for numbers at index 3 in the string. I'm sure "Car1", "Car2" etc. was just an example. See my answer for a complete solution. – herman Jun 26 '13 at 10:14
  • Return value of "-1" indicates that "o2" should be above "o1" in the list. Return value of "1" puts "o1" above "o2". A return value of 0 indicates that o1 & o2 are equal. You are basically indicating which way to sort the list. – Ian Dec 12 '18 at 13:38
0

Car1 is object so you have to create a custom Comparator for that. and specify the ordering nature of it.

Collection.Sort() is Sorts the specified list into ascending order, according to the natural ordering of its elements. just example: string will do like a-z, number is 0-9. but objects does not have any natural order.

bNd
  • 7,512
  • 7
  • 39
  • 72
0

This will work for you. But you can't have duplicate

    String text = "Car3, Car1, Car12, Car45";
    String[] str = text.split(", ");
    Map<Integer,String> myMap=new HashMap<>();
    int[] numOnly=new int[str.length];
    for (int i = 0; i < str.length; i++) {
          numOnly[i] = Integer.parseInt(str[i].replaceAll("\\D", ""));
          myMap.put(numOnly[i],str[i]);
    }       
    System.out.println(myMap.values());
Ruchira Gayan Ranaweera
  • 34,993
  • 17
  • 75
  • 115