DevX
DevX

Reputation: 510

TreeSet not arranging value in ascending order

I am trying to create a TreeSet to sort the strings which are inserted to be in an ascending order. I am using below code for entering values in TreeSet.

TreeSet<String> ts = new TreeSet<String>();

ts.add("@Test0");
ts.add("@Test1");
ts.add("@Test2");
ts.add("@Test3");
ts.add("@Test10");
ts.add("@Test4");

System.out.println("Tree set :: "+ts);

Output:

Tree set :: [@Test0, @Test1, @Test10, @Test2, @Test3, @Test4]

Upvotes: 2

Views: 1340

Answers (4)

rzwitserloot
rzwitserloot

Reputation: 102892

You've used the no-args TreeSet constructor. This means TreeSet will order its elements based on natural order. It's the way the objects compare themselves: It means the things you add must be of a type that implements Comparable<Self>. String does that: The String class is defined to implement Comparable<String>. However, the way strings compare themselves is lexicographically. 10 comes before 2 for the same reason that aa comes before b.

You have two routes available to fix this:

  1. Don't put strings in there but some other object that implements Comparable and does it right. Perhaps a class Thingie {String name; int idx;}.

  2. Pass a Comparator as first and only argument to your TreeSet class. Write code that determines that @Test10 comes before @Test2. Then, TreeSet uses this comparator to determine ordering and won't use the one built into strings.

Upvotes: 4

Mureinik
Mureinik

Reputation: 311188

As @Jems noted in the comment, strings are sorted lexichographically, so "@Test10" will come before "@Test2". If could however, supply a custom Comparator to define the order you need. E.g., if you know all the strings will have the form of "@Test" followed by a number, you could extract this number and sort accordingly:

TreeSet<String> ts = 
    new TreeSet<>(Comparator.comparingInt(s -> Integer.parseInt(s.substring(5))));

Upvotes: 1

WJS
WJS

Reputation: 40034

Specify the Comparator to sort on the number part only. This removes all but the number portion, converts that to an integer and sorts on that.

TreeSet<String> ts = new TreeSet<String>(Comparator.comparing(
        s -> Integer.valueOf(s.replace("@Test", ""))));

ts.add("@Test0");
ts.add("@Test1");
ts.add("@Test2");
ts.add("@Test3");
ts.add("@Test10");
ts.add("@Test4");

System.out.println(ts);

prints

[@Test0, @Test1, @Test2, @Test3, @Test4, @Test10]

This works for the shown example. You may need to modify it somewhat for more varied data. But it demonstrates the idea.

Upvotes: 1

M A
M A

Reputation: 72844

@Test10 comes before @Test2 because 1 comes before 2. That's how the default ordering of String works (String implements the interface Comparable to do this sorting).

To solve your issue you need to provide a custom Comparator to the TreeSet, and do the comparison by parsing the integer within the string:

TreeSet<String> ts = new TreeSet<String>(new Comparator<String>() {

    @Override
    public int compare(String s1, String s2) {
        return Integer.parseInt(s1.substring(5)) - Integer.parseInt(s2.substring(5)); 
    }
});

The comparator can be constructed using the static convenience method:

TreeSet<String> ts = new TreeSet<>(Comparator.comparing(s -> Integer.parseInt(s.substring(5))));

Upvotes: 1

Related Questions