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());
}
}
Related
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))));
I have an ArrayList of TreeSets defined like this (in Java) where n is some given number.
ArrayList<TreeSet<Integer>>(n)
Since I know that all values inside TreeSet are sorted in ascending order, that keep my List unsorted. Now I want to sort my List based on the first Element of each TreeSet in order to have both list and all treesets sorted.
Is it possible to sort just these elements which I get using list.get(i)? Will this mess up my TreeSets too?
Let's say that you already have an ArrayList<TreeSet<Integer>> initialized; we'll call it list.
You'll have to pass a custom Comparator<TreeSet<Integer>> to sort each TreeSet<Integer>:
Collections.sort(list, Comparator.comparing(TreeSet::first));
This sorts the ArrayList<TreeSet<Integer>> in ascending order according to the first element of each TreeSet<Integer>.
You can try sorting your ArrayList using a custom comparator. In this case, your custom comparator can compare the first numbers from each of two tree set elements.
Collections.sort(list, new ListOfTreeSetComparator());
class ListOfTreeSetComparator implements Comparator<TreeSet<Integer>> {
#Override
public int compare(TreeSet<Integer> ts1, TreeSet<Integer> ts2) {
return ts1.first().compareTo(ts2.first());
}
}
Note that there is a potential problem with your logic. Just because the first elements in each TreeSet have ascending order in your ArrayList does not necessarily mean that all elements would be sorted.
Here some rough idea which you can implement
Collections.sort(ArrayList, new Comparator<TreeSet<Integer>>() {
#Override
public int compare(TreeSet lhs, TreeSet rhs) {
// -1 - less than, 1 - greater than, 0 - equal
return lhs.first().compareTo(rhs.first());
}
});
I am new to Java and am trying to implement a priority queue with a custom comparator. I want to put the Sentences in the queue and have them removed in order to highest score.
For the comparator class I have:
public class SentenceScoreComparator implements Comparator<Sentence> {
#Override
public int compare(Sentence o1, Sentence o2) {
if (o2.getScore() > o1.getScore()) return -1;
//fixed typo
if (o2.getScore() < o1.getScore()) return 1;
return 0;
}
}
I then print out the sentences like so:
PriorityQueue<Sentence> allSentences = new PriorityQueue<Sentence>(new SentenceScoreComparator());
//add sentences
for(Sentence s :allSentences){
System.out.println(s.getScore());
}
but they are not in order
0.34432960587450223
0.47885099912108975
0.10991840331015199
0.36222267254836954
0.05164923572003221
0.5366117828694823
0.3891453014131773
0.0961512261934429
0.5566040852233918
0.5079687049927742
0.7628021620154812
0.6023121606121791
0.25695632228681914
0.15701049878801304
0.1260031244674359
0.36516025683986736
0.3846995962155155
I checked that the queue is using the comparator with the correct comparator method. Can someone explain what I am missing?
You have a typo in the comparator in the second if where o2 score is compared to itself.
Replace it with:
#Override
public int compare(Sentence o1, Sentence o2) {
return Double.compare(o1.getScore(), o2.getScore());
}
On top of that, as bradimus answered, PriorityQueue does not guarantee any sorted traversal. Use a regular list and sort it for that.
PriorityQueue never promised to traverse them in order. From the javadocs:
This class and its iterator implement all of the optional methods of the Collection and Iterator interfaces. The Iterator provided in method iterator() is not guaranteed to traverse the elements of the priority queue in any particular order. If you need ordered traversal, consider using Arrays.sort(pq.toArray()).
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().
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);
}
}
}