Comparator not functioning as intended for PriorityQueue - java

I wrote this comparator function to be used with a priority queue. It works fine this way, it gives the words with the least frequency when I poll().
But this is frequency and I want them to behave in the opposite way. I swapped the return values -1 and +1, and that is leads to unordered and mixed results on poll().
Why does that happen, and how to fix it? Also, why is it necessary to always specify the initial size of a PriorityQueue while specifying the comparator in the constructor? Does it have any effect that I am unable to see?
public class StringFrequencyComparator implements Comparator<Word>
{
public int compare(Word x, Word y)
{
if (x.frequency() < y.frequency())
{
return -1;
}
if (x.frequency() > y.frequency())
{
return +1;
}
return 0;
}
}
Update: I had made a mistake. I was inserting values in the PriorityQueue and then updating their frequencies. But, I was confused because in spite of this mistake the orders were perfect for the comparator and only mixed up when I swapped the -1s and 1s values forcing me towards believing there was something wrong with the comparator. I still wonder how it was perfect.

Related

Java: Comparison method violates its general contract

I got the exception from above which I know was discussed often here on SO. Anyways, the others don't tell me what's wrong with my code. These StyleAlbum are just Music albums. getAdded() returns the time when their last song was added to Android's MediaStore in milliseconds as int.
This happens only on some devices!
Code:
public static List<StyleAlbum> sortAdded() {
List<StyleAlbum> mAlbums = new ArrayList<>();
mAlbums.addAll(Library.getAlbums());
Collections.sort(mAlbums, new Comparator<StyleAlbum>() {
#Override
public int compare(StyleAlbum lhs, StyleAlbum rhs) {
if (lhs.getAdded() > rhs.getAdded()) {
return -1;
} else return 1;
}
});
return mAlbums;
}
Your comparator does not account for two items being equal, ie it never returns zero. You should use something like
return Integer.compare(lhs.getAdded(), rhs.getAdded());
otherwise the result is at least inconsistent, and depending on the algorithm it may even be completely wrong. In this case, Tim sort (the algorithm used in the JDK) is smarter enough to tell you that there's an error.
Note: I compared integers with Integer.compare instead of a subtraction (thanks Louis Wasserman) to avoid overflow errors
You should return 0 in the compare method if both values are the same. Especially, a comparison from an object to itself should give 0.

Creating a dictionary: Method to prevent the same word from being added more than once

I need to create a method to determine whether or not the word I'm trying to add to my String[] dictionary has already been added. We were not allowed to use ArrayList for this project, only arrays.
I started out with this
public static boolean dictHasWord(String str){
for(int i = 0; i < dictionary.length; i++){
if(str.equals(dictionary[i])){
return true;
}
}
return false;
}
However, my professor told me not to use this, because it is a linear function O(n), and is not effective. What other way could I go about solving this method?
This is a example of how to quickly search through a Array with good readability. I would suggest using this method to search your array.
import java.util.*;
public class test {
public static void main(String[] args) {
String[] list = {"name", "ryan"
};
//returns boolean here
System.out.println(Arrays.asList(list).contains("ryan"));
}
}
If you are allowed to use the Arrays class as part of your assignment, you can sort your array and use a binary search instead, which is not O(n).
public static boolean dictHasWord(String str){
if(Arrays.binarySearch(dictionary, str) != -1){
return true;
}
return false;
}
Just keep in mind you must sort first.
EDIT:
Regarding writing your own implementation, here's a sample to get you going. Here are the javadocs for compareTo() as well. Heres another sample (int based example) showing the difference between recursive and non recursive, specifically in Java.
Although it maybe an overkill in this case, but a hash-table would not be O(n).
This uses the fact that every String can be turnt into an int via hashCode(), and equal strings will produce the same hash.
Our dictionary can be declared as:
LinkedList<String>[] dictionary;
In other words in each place several strings may reside, this is due to possible collisions (different strings producing the same result).
The simplest solution for addition would be:
public void add(String str)
{
dictionary[str.hashCode()].add(str);
}
But in order to do this, you would need to make an array size equal to 1 less the maximum of hashCode() function. Which is probably too much memory for you. So we can do a little differently:
public void add(String str)
{
dictionary[str.hashCode()%dictionary.length].add(str);
}
This way we always mod the hash. For best results you should make your dictionary size some prime number, or at least a power of a single prime.
Then when you want to test the existence of the string you do exactly what you had in the original, but you use the specific LinkedList that you get from the hash:
public static boolean dictHasWord(String str)
{
for(String existing : dictionary[str.hashCode()%dictionary.length])
{
if(str.equals(existing)){
return true;
}
}
return false;
}
At which point you may ask "Isn't it O(n)?". And the answer is that it is not, since the hash function did not take into consideration the number of elements in array. The more memory you will give to your array, less collisions you will have, and more this approach moves towards O(1).
If somebody finds this answer searching for a real solution (not homework assignment). Then just use HashMap.

Use binary search if sorted otherwise use linear search

I'm given a problem where the user is given an empty recipe book and they can input and sort the recipes.
I know that a book is sorted if it is empty, has one recipe, and two recipes(ascending/descending). These can use binary search.
But when the user inputs a third recipe it could be either "cookies, donut, turkey" (which is sorted) or "cookies, donut, apples" and that's not sorted. If it's not sorted then I have to use linear search.
This is what I have so far
public void sortBook(int choice, boolean ascend) {
RecipeBookComparator comparing = new RecipeBookComparator(choice, ascend);
mList.sort(comparing);}
public class RecipeBookComparator implements Comparator {
private int mSortRBook;
private boolean mAscend;
public RecipeBookComparator (int choice, boolean ascend) {
mSortRBook = choice;
mAscend = ascend;
}
public int compare(Object o1, Object o2) {
Recipe s1 = (Recipe)o1, s2 = (Recipe)o2;
switch (mSortRBook) {
case 1:
if (mAscend == true) {
int compareName = s1.getName().compareTo(s2.getName());
if (compareName != 0) {
return compareName;
}
}
else {
int compareName = s1.getName().compareTo(s2.getName());
if (compareName != 0) {
return compareName * -1;
}
} ///more cases...
I know what I'm supposed to do, but I don't know how to approach it "code-wise"
To find out if a list is sorted, you would have to compare each element with ist neighbors. If only one element of thousands is not in order, a binary search can fail. So you would have to check the complete list. But going through all the list to check if the list is sorted takes longer than to look for one element in the list with linear search, so that makes no sense. Use linear search, if you don't know for sure if a list is sorted or not. That's it.
Your code says:
mList.sort(comparing);
I believe you've misunderstood what you've been asked to do - assuming that "use binary search if sorted otherwise use linear search", the title of your question, is what you are supposed to do. You are not supposed to sort them at all. This problem requires no knowledge of how to sort things in Java whatsoever.
What you need to understand is searching, not sorting. And how to check whether a sequence of inputs is already sorted.
Now, admittedly, technically you can check if a sequence is already sorted by actually sorting it and then checking if the resulting sequence is in the same order as what you started with. But I wouldn't recommend that.
What I would recommend, instead, is using a Comparator to compare each pair of adjacent elements (if any) in the sequence, to check if the sequence is monotonically increasing or monotonically decreasing.

Custom comparator isn't sorting

I'm trying to simulate a CPU scheduler. I have an ArrayList of a Process class I made. I'm trying to sort this array list by the arrival time of the process. I must be missing something, because when I print my ArrayList, nothing happens.
I've been looking through other users posts but I haven't found anything that made sense to me.
Here is my Comparator and call to sort:
class ArrivalTimeComparator implements Comparator<Process> {
#Override
public int compare(Process p1, Process p2) {
return (int) (p1.getArrivalTime()-p2.getArrivalTime());
}
}
Collections.sort(processArray, new ArrivalTimeComparator());
This code
(int)(p1.getArrivalTime()-p2.getArrivalTime())
may suffer from integer operation overflow, thus you can get odd results. If you're using Java 7, use
Integer.compare(p1.getArrivalTime(), p2.getArrivalTime()); //or Long.compare
If you're working with Java 6 or less:
return p1.getArrivalTime() > p2.getArrivalTime() ? 1 : p1.getArrivalTime() < p2.getArrivalTime() ? -1 : 0;

Eliminating or avoiding adding duplicates in a ArrayList with custom Object

I have a custom object in this structure
static class Node {
int col;
int row;
int g;
int h;
int f;
public Node(int col, int row, int g, int h) {
this.col = col;
this.row = row;
this.g = g;
this.h = h;
this.f = g+h;
}
}
The col and row variables are unique, and may only occur once in ArrayList<Node> myList.
Is there a way optimal way to avoid adding or checking for possible duplicate without having to make a nasty for-loop?
I am aware that Set interface possibly could be a solution for this as duplicates cannot occur, but i have alot of code right now, which i do not want to refactor unless it becomes necessary.
Here are your options. All of these solutions require proper implementation of equals and hashCode. Since you want row and col to be unique:
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != Node.class) {
return false;
}
Node other = (Node) obj;
if (other.col != this.col) {
return false;
}
if (other.row != this.row) {
return false;
}
return true;
}
public int hashCode() {
int result = 7;
result += row * 31;
result += col * 31;
return result;
}
Iterate over the List
You don't have to do the iteration yourself, but that is exactly what calling List.contains will do. This one is pretty easy:
if (!myList.contains(node)) {
myList.add(node);
}
This will iterate for you, so you don't have to write the loop.
List to Set to List
Here you have two sub-options. If you want to preserve the order of your input list, then you can use LinkedHashSet. If you don't care, you can just use HashSet. What I mean is if I have a List with elements A, B, C, converting it to a HashSet and back may produce a different list, like B, C, A. LinkedHashSet keeps the elements in insertion order, avoiding this problem. In any case, you'll just do this:
Set<Node> nodeSet = new [Linked]HashSet<Node>(myList);
nodeSet.add(node);
myList = new ArrayList<Node>(nodeSet);
Remember that this is essentially doing iteration as well, but it's using a hash-code shortcut instead of checking every element's equality, which may be a big deal with enough nodes. If your node list is small (less than 1000 elements) then I doubt this will make much of a difference, and you may as well use the first one.
Converting everything to Set
You mentioned that this would require a lot of refactoring in your code, but this isn't a bad thing, especially if you plan on working on this code a lot in the future. My rule of thumb is if the refactoring will make the code easier to maintain, adding a little extra development time is never a bad thing. Writing maintainable, readable, and understandable code is what the experts do (the question here isn't relevant, but this particular answer is). Since Set implies unique elements and List does not, then it makes sense to make the change. The compiler will pretty much tell you all the places you have to change with its errors, and it might take less time than you think.
Add a equals method in Node if possible :
#Override
public boolean equals(Node n){
if(this.getRow().equals(n.getRow()) && this.getCol().equals(n.getCol())
return true;
else
return false;
}
And then use list-to-set-to-list trick.
Try this method:
List<Node> removeDuplicateNodes(List<Node> inputList){
return (inputList ==null or inputList.size()==0)? Collections.EMPTY_LIST: new ArrayList<Node>(new HashSet<Node>(inputList));
}
Update :
If you wan't to maintain input order, use LinkedHashSet
List<Node> removeDuplicateNodes(List<Node> inputList){
return (inputList ==null or inputList.size()==0)? Collections.EMPTY_LIST: new ArrayList<Node>(new LinkedHashSet<Node>(inputList));
}
Add all the elements to a new Set, then put all the elements from the Set to a new List. That will make it.
I always find weird to see cases where people want to use a List (for the get(int) method, I guess) when they require unicity, which is only achieved through Set.
Anyway, by manipulating a little the equals/hashcode (making equals return true when row and col are the same) method and adding calls to List#contains(Object), you could have your goal reached without sacrifying your List
EDIT
Notice you could also create a Comparator and rely upon Collections#sort(List, Comparator) to have your list sorted and items with the same value melted into only one value.
Keep both a Set and a List. Use the Set to check for duplicates. Add to both Set and List if no dupe.
...assuming that Node has an .equals method defined...
private final Set<Node> seen = new HashMap<Node>();
private final List<Node> uniqueNodes = new ArrayList<Node>();
public void insertIfUnique(final Node n) {
if (seen.contains(n)) {
return;
}
seen.add(n);
uniqueNodes.add(n);
}
Ideally, you'd use Set, but if you'd like to avoid reimplementing your data structures from ArrayList<Node> to Set, you can implement Set as a gatekeeper:
Each time an element is about to be inserted into your ArrayList, check if the row-col pair is already in the Set.
If not, register the row-col pair into the Set
If the pair already exists in the set, do not insert it
Each time an element is about to be removed from your ArrayList, remove it from the Set.
And thus it's a "gatekeeper".
All of the Set operations are O(1) since they are hashed; minimal refactoring and no nasty loops as desired.

Categories

Resources