Java PriorityQueue and Comparator Not Ordering correctly - java

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()).

Related

Sorting ArrayList of TreeSets

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

Implementing specific java MaxHeap using priorityQueue

I am going to implement specific java maxHeap using priorityQueue as follows:
Suppose I have a "Customer" class which has a double variable name "marginalGain".
PriorityQueue<Customer> marginalGainHeap=new PriorityQueue<Customer>();
How can I heapify "marginalGainHeap" using the natural order of "marginalGain" variable? Is it possible with using PriorityQueue or I need to re-implement MaxHeap myself?
Regards.
You can use PriorityQueue, however it is worth remembering only the first entry will be sorted. Most likely you want TreeSet as a SortedSet.
In any case you will need to make Customer Comparable, or a better solution more likely it is good idea to create a Comparator<Customer> as you might want to sort Customers in more than one order. Note: You will want to order all Customers, i.e. even if their marginGain is the same, you need to give them an order.
You can use a Comparator with PriorityQueue like this.
PriorityQueue<Customer> pq = new PriorityQueue<Customer>(new Comparator<Customer>() {
public int compare(Customer c1, Customer c2) {
return Double.compare(c1.getMarginGain(), c2.getMarginGain());
}
});
An advantage of PriorityQueue is you don't have to ensure the comparator doesn't return 0 for different Customers (in a TreeSet it would treat these as duplicates and discard them)
I tried to write a class implementing max heap using PriroityQueue, as follows, which might be of help for your problem:
class MaxHeapUsingPriorityQueue<T>{
int capacity;
Comparator<T> pqc;
Queue<T> q;
public MaxHeapUsingPriorityQueue(int capacity, Comparator<T> comparator){
this.capacity = capacity;
this.pqc = comparator;
q = new PriorityQueue<T>(comparator);
}
void add(T t){
if(q.size() < capacity) q.offer(t);
else{
if(pqc.compare(t, q.peek()) > 0){
q.poll();
q.offer(t);
}
}
}
void displayMaxHeapContent(){
// in ascending order
while(!q.isEmpty()) System.out.println(q.poll().toString());
}
}

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 can I randomize the iteration sequence of a Set?

I need to use the Set collection.
Each time I start a jvm to run the program, I want to iterate through the items in the Set in a randomly decided sequence.
The iteration sequence has nothing to do with the sequence in which I placed them in the Set, right?
So, what to do? How can I randomize the iteration sequence in a Set?
Here is my method, and it does not randomize.
public static <T> void shuffle(Set<T> set) {
List<T> shuffleMe = new ArrayList<T>(set);
Collections.shuffle(shuffleMe);
set.clear();
set.addAll(shuffleMe);
}
What you need is a RandomizingIterator
Set is unordered, so randomizing an unordered Collection doesn't make any logical sense.
An ordered Set is ordered using a Comparator which means it has a fixed order, you can't shuffle it, that has no meaning as the order is determined by the Comparator or the compare() method.
Set -> List will allow you to shuffle the contents of the List and then use a custom RandomizingIterator to iterate across the Set.
Example Implementation :
Link to Gist on GitHub - TestRandomizingIterator.java
import org.junit.Test;
import javax.annotation.Nonnull;
import java.util.*;
public class TestRandomzingIterator
{
#Test
public void testRandomIteration()
{
final Set<String> set = new HashSet<String>()
{
/** Every call to iterator() will give a possibly unique iteration order, or not */
#Nonnull
#Override
public Iterator<String> iterator()
{
return new RandomizingIterator<String>(super.iterator());
}
class RandomizingIterator<T> implements Iterator<T>
{
final Iterator<T> iterator;
private RandomizingIterator(#Nonnull final Iterator<T> iterator)
{
List<T> list = new ArrayList<T>();
while(iterator.hasNext())
{
list.add(iterator.next());
}
Collections.shuffle(list);
this.iterator = list.iterator();
}
#Override
public boolean hasNext()
{
return this.iterator.hasNext();
}
#Override
public T next()
{
return this.iterator.next();
}
/**
* Modifying this makes no logical sense, so for simplicity sake, this implementation is Immutable.
* It could be done, but with added complexity.
*/
#Override
public void remove()
{
throw new UnsupportedOperationException("TestRandomzingIterator.RandomizingIterator.remove");
}
}
};
set.addAll(Arrays.asList("A", "B", "C"));
final Iterator<String> iterator = set.iterator();
while (iterator.hasNext())
{
System.out.println(iterator.next());
}
}
}
Notes:
This is a straw man example, but the intention is clear, use a custom Iterator to get custom iteration.
You can't get the normal iteration behavior back, but that doesn't seem to be a problem with your use case.
Passing the the super.iterator() to the facade is important, it will StackOverflowError otherwise, because it becomes a recursive call if you pass this to .addAll() or the List() constructor.
HashSet may appear to be ordered but it isn't guaranteed to stay ordered, the order depends on the hashCode of the objects and adding a single object may reorder the how the contents are order, the contract of the Set interface is that the order is undefined and in particular the HashSet is nothing more than a Facade over a backing Map.keySet().
There are other more supposedly light weight, but much more complex solutions that use the original Iterator and try and keep track of what has already been seen, those solutions aren't improvements over this technique unless the size of the data is excessively large, and the you are probably looking at on disk structures at that point.
You could copy the contents of the Set into a List, shuffle the List, then return a new LinkedHashSet populated from the shuffled list. Nice thing about LinkedHashSet is that its iterators return elements in the order they were inserted.
public static <T> Set<T> newShuffledSet(Collection<T> collection) {
List<T> shuffleMe = new ArrayList<T>(collection);
Collections.shuffle(shuffleMe);
return new LinkedHashSet<T>(shuffleMe);
}
According to the docs for java.util.Set:
The elements are returned in no particular order (unless this set is an instance of some class that provides a guarantee).
When you insert the elements there is no guarantee about the order they will be returned to you. If you want that behavior you will need to use a data structure which supports stable iteration order, e.g. List.
Internally HashSet sorts all its elements, AFAIR according to their hash() value. So you should use other classes like SortedSet with a custom comparator. But remember the whole idea of Set is to find elements quickly, that's why it sorts elements internally. So you have to keep "stability" of the comparison. Maybe you don't need a set after shuffling?

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

Categories