Fatal Exception: java.lang.IllegalArgumentException: Comparison method violates its general contract - java

I know there many similar questions and I have received big help by reading answers to those questions, however I am not able to see how is my client facing this problem. And there is only one client who is facing this problem.
I have a List, and I am sorting that list using Comparator interface. Does any of you see problem with the following code?
private static class BiologySamplesComparator implements Comparator<BiologySample>, Serializable {
#Override
public int compare(BiologySample left, BiologySample right) {
if (left == right || (left != null && right != null && left.getSampleDateTime() == right.getSampleDateTime())) {
return 0;
}
if (left == null || left.getSampleDateTime() == null) {
return 1;
}
if (right == null || right.getSampleDateTime() == null) {
return -1;
}
return right.getSampleDateTime().compareTo(left.getSampleDateTime());
}
}
And this how I am calling this function
Collections.sort(biologySamples, new BiologySamplesComparator());
I know that the main problem in this kind of scenario is Transitivity. However I couldn't figure what is violating that rule.
This how getSampleDateTime() is returning date Fri Apr 09 17:00:00 PDT 2021
Update
This is how I was able to fix my problem.
I hope this helps, I was stuck for so long on this problem.
private static class BiologySamplesComparator implements Comparator<BiologySample>, Serializable {
#Override
public int compare(BiologySample left, BiologySample right) {
if (left == null) {
if (right == null) {
return 0;
} else {
return 1;
}
} else if (right == null) {
return -1;
} else if (left == right) {
return 0;
}
if (left.getSampleDateTime() == null) {
if (right.getSampleDateTime() == null) {
return 0;
} else {
return 1;
}
} else if (right.getSampleDateTime() == null) {
return -1;
} else if (left.getSampleDateTime() == right.getSampleDateTime()) {
return 0;
}
return right.getSampleDateTime().compareTo(left.getSampleDateTime());
}
}

You have a possible inconsistency when comparing a null "sample" to a non-null sample with a null timestamp.
Sample a = null;
Sample b = new Sample(null);
bsc.compare(a, b); // -> 1, a > b
bsc.compare(b, a); // -> 1, b > a
First, you should replace the Date in your sample class with Instant if at all possible, and then make your life simpler by saying this:
public static final Comparator<Sample> ORDER_BY_TIMESTAMP =
Comparator.nullsLast(Comparator.comparing(
Sample::getDateTime,
Comparator.nullsLast(Comparator.naturalOrder())
);
If you can rule out null values, even simpler:
Comparator.comparing(Sample::getDateTime);

I was missing some conditional for some cases and this is how I was able to solve my problem.
private static class BiologySamplesComparator implements Comparator<BiologySample>, Serializable {
#Override
public int compare(BiologySample left, BiologySample right) {
if (left == null) {
if (right == null) {
return 0;
} else {
return 1;
}
} else if (right == null) {
return -1;
} else if (left == right) {
return 0;
}
if (left.getSampleDateTime() == null) {
if (right.getSampleDateTime() == null) {
return 0;
} else {
return 1;
}
} else if (right.getSampleDateTime() == null) {
return -1;
} else if (left.getSampleDateTime() == right.getSampleDateTime()) {
return 0;
}
return right.getSampleDateTime().compareTo(left.getSampleDateTime());
}
}
courtesy of Why does my compare methd throw IllegalArgumentException sometimes?

Related

Binary Tree height implementation

This is the most common implementation for binary tree height I have found on internet
public int height(BinaryNode t) {
if (t == null) {
return 0;
} else {
return 1 + Math.max(height(t.left), height(t.right));
}
}
But shouldn't it modified like this since it is adding an unnecessary 1 at leaf nodes
public int height(BinaryNode t) {
if (t == null) {
return 0;
} else if (t.left == null && t.right == null) {
return 0;
} else {
return 1 + Math.max(height(t.left), height(t.right));
}
}

Java Treemap comparator ignoring get when key is different instances

The issue I'm having is that the get method throws NPE when the key is a different instance than the one in the TreeMap.
public class ConjuntDocuments {
private TreeMap<Capcalera, Document> almacen;
private ArrayList<Pair_plantilla> plantilla;
ConjuntDocuments() {
almacen = new TreeMap<Capcalera, Document>(new CustomComparator());
plantilla = new ArrayList<Pair_plantilla>();
}
private static class CustomComparator implements Comparator<Capcalera> {
#Override
public int compare(Capcalera c1, Capcalera c2) {
int ax = c1.get_tit().get_nom().compareFrase(c2.get_tit().get_nom());
if (ax < 0) return -1;
else if (ax > 0) return 1;
//titols iguals
else {
ax = c1.get_au().get_nom().compareFrase(c2.get_au().get_nom());
if (ax < 0) return -1;
else if (ax > 0) return 1;
}
//titols i autors iguals
return 0;
}
}
compareFrase compares ArrayLists(Paraula) -> Frase, Paraula is like a string, get_chars returns a String.
public int compareFrase(Frase f) {
for(int i=0; i<min(this.get_size(), f.get_size()); ++i){
int aux = this.get_paraula(i).get_chars().compareTo(f.get_paraula(i).get_chars());
if(aux < 0) return -1;
else if(aux > 0) return 1;
}
if(this.get_size() < f.get_size()) return -1;
else if(this.get_size() > f.get_size()) return 1;
return 0;
}
Titol and autor are Frases -> ArrayList(Paraula)
public class Capcalera {
private Titol tit;
private Autor au;
So after trying to figure this out, I've realised that the get method only works if the key referenced is the same instance than the one mapped, right after putting an entry (almacen.put(capcalera,document) , if I try to call almacen.get(Capcalera) it will return the value correctly, but if I create a new Capcalera, it will throw NPE. I'm assuming there is an issue with the comparator but since the entries are sorted correctly I can't figure out what is wrong.
EDIT:
I've implemented .equals and .hashcode from Capcalera, but I might be doing something wrong because .get from the Treemap still throws NPE.
#Override
public int hashCode() {
int hashTitol = tit != null ? tit.hashCode() : 0;
int hashAutor = au != null ? au.hashCode() : 0;
return (hashTitol + hashAutor) * hashAutor + hashTitol;
}
#Override
public boolean equals(Object other) {
if (other instanceof Capcalera) {
Capcalera otherCapcalera = (Capcalera) other;
return
(( this.get_tit().get_nom().equalsFrase(otherCapcalera.get_tit().get_nom()) ||
( this.get_tit() != null && otherCapcalera.get_tit() != null &&
this.get_tit().get_nom().equalsFrase(otherCapcalera.get_tit().get_nom()) )) &&
( this.get_au().get_nom().equalsFrase(otherCapcalera.get_au().get_nom()) ||
( this.get_au() != null && otherCapcalera.get_au() != null &&
this.get_au().get_nom().equalsFrase(otherCapcalera.get_au().get_nom()))) );
}
return false;
}
equalsFrase returns true if Titol/Autor are equals
public boolean equalsFrase(Frase f) {
for(int i=0; i<min(this.get_size(), f.get_size()); ++i){
int aux = this.get_paraula(i).get_chars().compareTo(f.get_paraula(i).get_chars());
if(aux < 0) return false;
else if(aux > 0) return false;
}
if(this.get_size() < f.get_size()) return false;
else if(this.get_size() > f.get_size()) return false;
return true;
}

How do I write my method for balancing my BST?

So I have written an implementation of a BST in Java. My goal is to make it balanced aswell, more precisly an AVL tree. I am having some problem though, I don't know how to implement the trinodeRestructering method(ie the method that balances the tree) I have tried various things but these pointers are sometimes difficult to deal with and I am not sure how to do this recursivly. Down below is my code for adding a new element and the method to check if we are more than 2 steps difference in the tree.
add and balancing method:
private TreeNode insert(TreeNode currN, TreeNode newN) {
if (currN == null) {
return newN;
}
if (currN.getData() == newN.getData()) {
throw new IllegalArgumentException("Value already exists.");
}
if (newN.getData() < currN.getData()) {
if (currN.getLeft() == null) {
currN.setLeft(newN);
} else {
insert(currN.getLeft(), newN);
}
} else {
if (currN.getRight() == null) {
currN.setRight(newN);
} else {
insert(currN.getRight(), newN);
}
}
if (needBalancing()) {
trinodeRestructering(currN);
}
return currN;
}
private TreeNode trinodeRestructering(TreeNode currN) {
//Not sure what to do here.
return currN;
}
height checking method.
public boolean needBalancing(){
if(height(root) == -1){ // true if we need to balance
return true;
}else{
return false;
}
}
private int height(TreeNode node){
if (node == null)
return 0;
int left = height(node.getLeft());
int right = height(node.getRight());
if (left == -1 || right == -1)
return -1;
if (Math.abs(left - right) > 1) {
return -1;
}
return Math.max(left, right) + 1;
}
I might add that I got an working inOrder method, perhaps I could use it to balance my tree?

Printing branches on a Binary Tree

How do you count number of branches, in this case branches with even integers. Here's what I have so far. It seems to work for a couple of the cases.
public int evenBranches() {
return evenBranches(overallRoot);
}
private int evenBranches(IntTreeNode root) {
if (root == null) {
return 0;
}
int val = 0;
if (root.left != null) {
val += evenBranches(root.left);
} else if (root.right != null) {
val += evenBranches(root.right);
}
if (root.data % 2 == 0) {
return val + 1;
} else {
return val;
}
}
You can modify the evenBranches() method as below: I think It will cover all edge cases, If any testcase is left, let me know, I will fix it.
public int evenBranches() {
return evenBranches(overallRoot, 0);
}
private int evenBranches(IntTreeNode root, int count) {
if(root == null || (root.left == null && root.right == null)) {
return count;
}
if(root.data % 2 == 0) {
count++;
}
count += evenBranches(root.left, count);
count += evenBranches(root.right, count);
return count;
}
You may need to remove the else condition when checking the occurrences in right branch. Otherwise it will check only one side. eg:
private int evenBranches(IntTreeNode root) {
if (root == null) {
return 0;
}
int val = 0;
if (root.left != null) {
val += evenBranches(root.left);
}
if (root.right != null) {
val += evenBranches(root.right);
}
if (root.data % 2 == 0) {
return val + 1;
} else {
return val;
}
}
You can very well achieve the desired results by using a global variable, and applying BFS (breadth first search) on your tree, in this manner:
int evencount = 0; // global-var.
public int evenBranches() {
evenBranches(overallRoot);
return evencount;
}
private void evenBranches(IntTreeNode root) {
if(!root) return;
if( (root.left || root.right) && (root.data % 2 == 0)){
evencount++;
}
evenBranches(root.left);
evenBranches(root.right);
}

Check that every if has a matching endif

I recently had to code up an interpreter for Bitcoin's script language; part of this involved coming up with an algorithm to check that the control flow in a given script made sense (i.e. every OP_IF had a matching OP_ENDIF, every OP_ELSE and OP_ENDIF had a matching OP_IF, etc.).
This is what I came up with:
public class if_else_checker {
public static boolean search(String[] commands, String[] tracker, int if_index) {
boolean seenElse = false;
for (int i = if_index; i < commands.length; i++) {
if (commands[i].equals("OP_ELSE")) {
if (seenElse == true && tracker[i] == null) return false;
if (tracker[i] == null) {
tracker[i] = "OP_ELSE";
seenElse = true;
}
}
else if (commands[i].equals("OP_ENDIF")) {
if (tracker[i] != null && tracker[i].equals("OP_ENDIF"))
{
continue;
}
tracker[i] = "OP_ENDIF";
return true;
}
else if (commands[i].equals("OP_IF")) {
if (tracker[i] != null && tracker[i].equals("OP_IF")) {
continue;
}
tracker[i] = "OP_IF";
if (search(commands, tracker, i + 1) == false) return false;
}
}
return false;
}
public static boolean validate(String[] args)
{
String[] tracker = new String[args.length];
for (int i = 0; i < args.length; i++)
{
if (args[i].equals("OP_IF"))
{
if (tracker[i] == null || !tracker[i].equals("OP_IF"))
{
tracker[i] = "OP_IF";
if (search(args, tracker, i + 1) == false) return false;
}
else continue;
}
else if (args[i].equals("OP_ELSE"))
{
if (tracker[i] == null || !tracker[i].equals("OP_ELSE")) return false;
}
else if (args[i].equals("OP_ENDIF"))
{
if (tracker[i] == null || !tracker[i].equals("OP_ENDIF")) return false;
}
}
return true;
}
public static void main(String[] args)
{
System.out.println(validate(args));
}
}
It works, but I was wondering if there is a way to optimise it/if there is a standard way of doing this? (One optimisation is to have validate() return the index of the OP_ENDIF it finds, rather than a boolean; this would change runtime from quadratic-time to linear).
The best way of solving this is by using a Stack data structure. Every new opening instruction (e.g. OP_IF) is pushed into the stack. When you find a closing instruction (e.g. OP_ENDIF), you pop the top element of the stack and check if it is the corresponding opening instruction for that closing instruction. If so, then it's valid, and you proceed to the next step. In the end, if the stack is empty then the control flow you're checking is correct. Otherwise, it's not.

Categories