TreeSet not arranging value in ascending order - java

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]

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:
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;}.
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.

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.

#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))));

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))));

Related

using TreeSet to sort without providing a Comparator to it

I know that TreeSet in java automatically sort its elements in ascending order an it guarantees the order.
for example, If i have an array of Date object in random and I copy it to TreeSet then it will be added in TreeSet in sorted way.
but suppose instead of a simple Date object, I have an ArrayList of HashMap<String,Object>, in the following format.
first value in arraylist,
{mydate = 32156464 , mystring = "abc", mystring2 = "xyz"}
2nd value in arraylist of hashmap,
{mydate = 64687678 , mystring = "abdc", mystring2 = "xyzzz"}
3rd value in arraylist of hashmap,
{mydate = 11233678 , mystring = "abxdc", mystring2 = "xyzppzz"}
Now if i want to sort this arraylist of hashmap based on mydate key, i have to create a new comparator in TreeSet instance like this,
public static Set<HashMap<String, Object>> mySet = new TreeSet<>(new Comparator<HashMap<String, Object>>() {
#Override
public int compare(HashMap<String, Object> o1, HashMap<String, Object> o2) {
return ((Date) o2.get(mydate)).compareTo((mydate) o1.get(DATE));
}
});
and it will store the arraylist inside the TreeSet in sorted order just fine. But I have used a custom Comparator to achieve this. What is the point of using TreeSet in this situation for sorting data if i am also providing a custom Comparator to it ?
How can i sort this ArrayList of HashMap based on date value without using a new instance of Comparator in TreeSet ?
What is the point of using TreeSet in this situation for sorting data if i am also providing a custom Comparator to it ?
Because it's the TreeSet code that keeps it sorted. You haven't had to provide any of the code for that - all you've had to provide is the custom comparison.
How can i sort this ArrayList of HashMap based on date value without using a new instance of Comparator in TreeSet ?
You can't, directly. You could write a subclass of HashMap which implemented Comparable for itself, but that would seem a bit odd to me. For example:
public class SpecialMap extends HashMap<String, Object>
implements Comparable<SpecialMap> {
private final String key;
public SpecialMap(String key) {
this.key = key;
}
public int compareTo(SpecialMap other) {
// TODO: Null handling
Date thisDate = (Date) this.get(key);
Date otherDate = (Date) other.get(key);
return thisDate.compareTo(otherDate);
}
}
Then you could have an ArrayList<SpecialMap> and sort that.
But given that you've had to provide basically the same code as for the comparator and bound your comparison with the map type, it feels to me like it would be better just to stick with the comparator.
If you don't supply a Comparator to the TreeSet, then it will rely on its elements being Comparable to sort them. If they're not Comparable, then a ClassCastException will result. The TreeSet javadocs explain:
A NavigableSet implementation based on a TreeMap. The elements are ordered using their natural ordering, or by a Comparator provided at set creation time, depending on which constructor is used.
But the HashMap class is not Comparable, so you must supply a custom Comparator so the TreeSet knows how you want to sort them. You cannot sort your HashMaps without a Comparator, whether they're in a TreeSet or any other collection.
There are advantages to using a TreeSet in this situation. The add, find, and remove operations are O(log n). If you were to use an ArrayList for this, then add and remove operations would be O(n), even if the find operation would still be O(log n).
More from TreeSet javadocs:
This implementation provides guaranteed log(n) time cost for the basic operations (add, remove and contains).
Instead of using HashMap you could create a class that contains mydate, mystring, mystring2 and implements a Comparable interface.
However it's a good practice to use comparator because you'll be able to supply sorting criteria in runtime.
The point of using a comparator in a TreeSet is that you don't have write the code to do the actual sorting, you just provide a method that determines which of your objects is first, based on your comparison rules.
If you don't want to create a comparator, you would need to add to your collection objects that implement the Comparable interface.
To sort an ArrayList, use Collections.sort().

Sorting of hash set in descending

I want to sort hashset values in descending value on the basis of length of string in hash set.
HashSet<String> hs = new HashSet<String>();
hs.add("The World Tourism Organization");
hs.add("reports the following ten countries");
hs.add("as the most visited in terms of the number");
hs.add("of international travellers.");
System.out.println(hs);
My output should be
['as the most visited in terms of the number',
'reports the following ten countries',
'The World Tourism Organization',
'of international travellers.']
What is the method to sort in descending order?
A HashSet by definition doesn't sort its members. What you want is a TreeSet.
If you have a hashset you can create a treeset from it, as long as the objects are Comparable:
TreeSet ts = new TreeSet (hs);
You should use TreeSet instead of hashset or create a comparator to sort your set
You need to use a TreeSet instead of a HashSet with your own custom comparator which will sort the values based on their lengths.
Set<String> yourSet = new TreeSet<>(new Comparator<String>() {
public int compare(String o1, String o2) {
// Your comparison logic goes here
return 0;
}
});
// Add all the HashSet values to the TreeSet
yourSet.addAll(hs);
HashSet does not provide any meaningful order to the entries. The documentation says:
It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time.
To get a sensible ordering, you need to use a different Set implementation such as TreeSet. TreeSet lets you provide a Comparator that specifies how to order the entries; something like:
public class SortByString implements Comparator<FullName>{
public int compare(FullName n1, FullName n2) {
return n1.getLastName().compareTo(n2.getLastName());
}
}

How to sort ArrayList<Long> in decreasing order?

How to sort an ArrayList<Long> in Java in decreasing order?
Here's one way for your list:
list.sort(null);
Collections.reverse(list);
Or you could implement your own Comparator to sort on and eliminate the reverse step:
list.sort((o1, o2) -> o2.compareTo(o1));
Or even more simply use Collections.reverseOrder() since you're only reversing:
list.sort(Collections.reverseOrder());
Comparator<Long> comparator = Collections.reverseOrder();
Collections.sort(arrayList, comparator);
You can use the following code which is given below;
Collections.sort(list, Collections.reverseOrder());
or if you are going to use custom comparator you can use as it is given below
Collections.sort(list, Collections.reverseOrder(new CustomComparator());
Where CustomComparator is a comparator class that compares the object which is present in the list.
Java 8
well doing this in java 8 is so much fun and easier
Collections.sort(variants,(a,b)->a.compareTo(b));
Collections.reverse(variants);
Lambda expressions rock here!!!
in case you needed a more than one line logic for comparing a and b you could write it like this
Collections.sort(variants,(a,b)->{
int result = a.compareTo(b);
return result;
});
Sort normally and use Collections.reverse();
For lamdas where your long value is somewhere in an object I recommend using:
.sorted((o1, o2) -> Long.compare(o1.getLong(), o2.getLong()))
or even better:
.sorted(Comparator.comparingLong(MyObject::getLong))
Sort, then reverse.
By using Collections.sort() with a comparator that provides the decreasing order.
See Javadoc for Collections.sort.
A more general approach to implement our own Comparator as below
Collections.sort(lst,new Comparator<Long>(){
public int compare(Long o1, Long o2) {
return o2.compareTo(o1);
}
});
The following approach will sort the list in descending order and also handles the 'null' values, just in case if you have any null values then Collections.sort() will throw NullPointerException
Collections.sort(list, new Comparator<Long>() {
public int compare(Long o1, Long o2) {
return o1==null?Integer.MAX_VALUE:o2==null?Integer.MIN_VALUE:o2.compareTo(o1);
}
});
You can also sort an ArrayList with a TreeSet instead of a comparator. Here's an example from a question I had before for an integer array. I'm using "numbers" as a placeholder name for the ArrayList.
import.java.util.*;
class MyClass{
public static void main(String[] args){
Scanner input = new Scanner(System.in);
ArrayList<Integer> numbers = new ArrayList<Integer>();
TreeSet<Integer> ts = new TreeSet<Integer>(numbers);
numbers = new ArrayList<Integer>(ts);
System.out.println("\nThe numbers in ascending order are:");
for(int i=0; i<numbers.size(); i++)
System.out.print(numbers.get(i).intValue()+" ");
System.out.println("\nThe numbers in descending order are:");
for(int i=numbers.size()-1; i>=0; i--)
System.out.print(numbers.get(i).intValue()+" ");
}
}
So, There is something I would like to bring up which I think is important and I think that you should consider. runtime and memory. Say you have a list and want to sort it, well you can, there is a built in sort or you could develop your own. Then you say, want to reverse the list. That is the answer which is listed above.
If you are creating that list though, it might be good to use a different datastructure to store it and then just dump it into an array.
Heaps do just this. You filter in data, and it will handle everything, then you can pop everything off of the object and it would be sorted.
Another option would be to understand how maps work. A lot of times, a Map or HashMap as something things are called, have an underlying concept behind it.
For example.... you feed in a bunch of key-value pairs where the key is the long, and when you add all the elements, you can do: .keys and it would return to you a sorted list automatically.
It depends on how you process the data prior as to how i think you should continue with your sorting and subsequent reverses
Comparator's comparing method can be used to compare the objects and then method reversed() can be applied to reverse the order -
list.stream().sorted(Comparator.comparing(Employee::getName).reversed()).collect(toList());
Using List.sort() and Comparator.comparingLong()
numberList.sort(Comparator.comparingLong(x -> -x));

Treeset to order elements in descending order

Here is the piece of code that I have used for Java 5.0
TreeSet<Integer> treeSetObj = new TreeSet<Integer>( Collections.reverseOrder() ) ;
Collections.reverseOrder() is used to obtain a comparator in order to reverse the way the elements are stored and iterated.
Is there a more optimized way of doing it?
Why do you think this approach won't be optimized? The reverse order Comparator is simply going to be flipping the sign of the output from the actual Comparator (or output from compareTo on the Comparable objects being inserted) and I would therefore imagine it is very fast.
An alternative suggestion: Rather than change the order you store the elements in you could iterate over them in descending order using the descendingIterator() method.
TreeSet::descendingSet
In Java 6 and later, there is a method on TreeSet called descendingSet() producing a NavigableSet interface object.
public NavigableSet descendingSet()
The descending set is backed by this
set, so changes to the set are
reflected in the descending set, and
vice-versa. If either set is modified
while an iteration over either set is
in progress (except through the
iterator's own remove operation), the
results of the iteration are
undefined.
The returned set has an ordering equivalent to
Collections.reverseOrder(comparator()).
The expression
s.descendingSet().descendingSet()
returns a view of s essentially
equivalent to s.
Specified by:
descendingSet in interface NavigableSet<E>
Returns:
a reverse order view of this set
Since:
1.6
TreeSet<Integer> treeSetObj = new TreeSet<Integer>(new Comparator<Integer>()
{
public int compare(Integer i1,Integer i2)
{
return i2.compareTo(i1);
}
});
there is need to flip the result. But I guess this is just a micro-optimization... Do you really need this ?
Using descendingSet method you can reverse existing treeSet in the class
import java.util.TreeSet;
public class TreeSetDescending {
public static void main(String[] args)
{
// Declare a treeset
TreeSet<Object> ints = new TreeSet<Object>();
ints.add(2);
ints.add(20);
ints.add(10);
ints.add(5);
ints.add(7);
ints.add(3);
// Initialize treeset with predefined set in reverse order
// using descendingSet()
TreeSet<Object> intsReverse = (TreeSet<Object>)ints.descendingSet();
// Print the set
System.out.println("Without descendingSet(): " + ints);
System.out.println("With descendingSet(): " + intsReverse);
}
}
Reverse compare
You can reverse the order of the two arguments in the compare method of your Comparator.
TreeSet t = new TreeSet(new MyComparator());
{
class MyComparator implements Comparator
{
public int compare(Integer i1,Integer i2)
{
Integer I1=(Integer)i1;
Integer I2=(Integer)i2;
return I2.compareTo(I1); // return -I1compareTo(I2);
}
}
}

Java TreeMap (comparator) and get method ignoring the comparator

public final Comparator<String> ID_IGN_CASE_COMP = new Comparator<String>() {
public int compare(String s1, String s2) {
return s1.compareToIgnoreCase(s2);
}
};
private Map< String, Animal > _animals = new TreeMap< String, Animal >(ID_IGN_CASE_COMP);
My problem is, how to use method get(id) ignoring the given comparator. I want the map to be order by Case Insensitive but, I want it to be case sensitive when I fetch the values by a given key.
I think the answer is easy. Implement your own comparator that does a case insensitive sort but does NOT return 0 for "A" and "a"... sort them too.
The issue is that your comparator returns 0 for the compare( "A", "a" ) case which means it is the same key as far as the map is concerned.
Use a comparator like:
public final Comparator<String> ID_IGN_CASE_COMP = new Comparator<String>() {
public int compare(String s1, String s2) {
int result = s1.compareToIgnoreCase(s2);
if( result == 0 )
result = s1.compareTo(s2);
return result;
}
};
Then all keys will go in regardless of case and "a" and "A" will still be sorted together.
In other words, get("a") will give you a different value from get("A")... and they will both show up in keySet() iterators. They will just be sorted together.
In a TreeMap, adding two keys a and b (in that order) so that compare(a, b) returns 0 will result in that the latest added entry (b) will overwrite the first one (a).
In your case, this means that there will never be any use for case insensitive get(id).
quoting http://java.sun.com/javase/6/docs/api/java/util/TreeMap.html
Note that the ordering maintained by a sorted map (whether or not an explicit comparator is provided) must be consistent with equals if this sorted map is to correctly implement the Map interface. (See Comparable or Comparator for a precise definition of consistent with equals.) This is so because the Map interface is defined in terms of the equals operation, but a map performs all key comparisons using its compareTo (or compare) method, so two keys that are deemed equal by this method are, from the standpoint of the sorted map, equal. The behavior of a sorted map is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Map interface.
This is probably not what you want.
If the map is comparably small and you don't need to fetch the sorted entries very many times, a solution is to use a HashMap (or a TreeMap without explicitly setting the comparator), and sort the entries case-insensitively when you need them ordered.
You'll have to use two separate TreeMaps for that, with the same contents but different comparators.
maybe it'll do the job:
new Comparator<String>(){
public int compare(String s1, String s2)
{
String s1n = s1.toLowerCase();
String s2n = s2.toLowerCase();
if(s1n.equals(s2n))
{
return s1.compareTo(s2);
}
return s1n.compareTo(s2n);
}
};
}
you need a multimap: each entry of this multimap keeps the case insensitive keys and aanother map with the original keys as value.
There are many freely usable implementations of multimaps such as Common Collections, Google Collections, etc
In addition to all the other answers and agreeing, that it is impossible to have a single TreeMap structure with different comparators:
From your question I understand that you have two requirements: the data model shall be case sensitive (you want the case sensitive values when you use get()), the presenter shall be case insensitive (you want an case sensitive ordering, presentation is just an assumption).
Let's assume, we populate the Map with the mappings (aa,obj1), (aA,obj2), (Aa,obj3), (AA,obj4). The iterator will provides the values in the order: (obj4, obj3, obj2, obj1)(*). Now which order do you expect if the map was ordered case-insensitive? All four keys would be equal and the order undefined. Or are you looking for a solution that would resolve the collection {obj1, obj2, obj3, obj4} for the key 'AA'? But that's a different approach.
SO encourages the community to be honest: therefore my advice at this point is to look at your requirement again :)
(*) not tested, assumed that 'A' < 'a' = true.
Use floorEntry and then higherEntry in a loop to find the entries case-insensitively; stop when you find the exact key match.

Categories