I wrote a class that checks whether two integer intevals overlap.
However I don't like this solution to much. I think that it's possible to do it in a better and simpler way.
public class IntegerInterval implements Interval {
private Integer start;
private Integer end;
public IntegerInterval(Integer start, Integer end) {
this.start = start;
this.end = end;
}
public Integer getStart() {
return start;
}
public Integer getEnd() {
return end;
}
public boolean isOverlapping(IntegerInterval other) {
if (other != null) {
return isInInterval(start, other) || isInInterval(end, other)
|| isInInterval(other.start, this) || isInInterval(other.end, this);
}
return false;
}
public boolean isInInterval(Integer number, IntegerInterval interval) {
if (number != null && interval != null) {
if(interval.getStart() == null && interval.getEnd() != null) {
return number.intValue() <= interval.getEnd().intValue();
}
if(interval.getStart() != null && interval.getEnd() == null) {
return number.intValue() >= interval.getStart().intValue();
}
if(interval.getStart() == null && interval.getEnd() == null) {
return true;
}
return interval.getStart() <= number && number <= interval.getEnd();
}
else if(number == null && interval != null) {
return interval.getStart() == null && interval.getEnd() == null;
}
return false;
}
}
The following code should be simpler:
public boolean isOverlapping(IntegerInterval other) {
if (other == null) return false; // for readability's sake, this condition is pulled out
// overlap happens ONLY when this's end is on the right of other's start
// AND this's start is on the left of other's end.
return (((this.end == null) || (other.start == null) || (this.end.intValue() >= other.start.intValue())) &&
((this.start == null) || (other.end == null) || (this.start.intValue() <= other.end.intValue())));
}
UPDATE If compare by Date as #Adam actually asked, the code would be:
private static boolean dateRangesAreOverlaping(Date start1, Date end1,
Date start2, Date end2) {
return (((end1 == null) || (start2 == null) || end1.after(start2)) &&
((start1 == null) || (end2 == null) || start1.before(end2)));
}
You should wrap start and end in a specific Comparable class that is able to encapsulate null. This way you only need to invoke compareTo in isInInterval and don't need to bother with null.
That class could also explicitly represent positive and negative infinity.
EDIT:
If you add a type parameter <T extends Comparable<T>> to the class declaration and declare the types of start and end type as Comparable<T> then you can use any type that implements Comparable with your Interval, not only Integer.
Assuming start < end. There should be 3 the checks for position of start relative to other: left, middle and right (right is for completeness as there is no intersection possible). So here are 2 remaining checks:
(start <= other.start && end >= other.start) ||
(start >= other.start && start <= other.end)
// start > other.end means no intersection as end > start > other.end
If you do checks for location of start as if than second chech can be just (start <= other.end):
if (start <= other.start) return end >= other.start;
else if (start <= other.end) return true;
else return false;
Adjust "=" portions for your needs and add you null checks appropriately (i.e. use SpaceTrucker answer to make comaprison with null hidden inside class).
Related
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?
I wanted to sort list of records in my using multiple comparators.
It contains null values as well for some fields.
I used java collections, to sort it, but it works only for one filed which is places at top.
My code:
Collections.sort(userListResult, new Comparator<UserDto>()
{
#Override
public int compare(UserDto userDto1, UserDto userDto2)
{
if (userDto1.getLastLoginDate() != null && userDto2.getLastLoginDate() != null && userDto2.getLastLoginDate().compareTo(userDto1.getLastLoginDate()) != 0) {
return userDto2.getLastLoginDate().compareTo(userDto1.getLastLoginDate());
}
if (userDto1.getLastLoginDate() != null && userDto2.getLastLoginDate() == null) {
return -1;
}
if (userDto1.getLastLoginDate() == null && userDto2.getLastLoginDate() != null) {
return 1;
}
if (userDto1.getLastActionDate() != null && userDto2.getLastActionDate() != null && userDto2.getLastActionDate().compareTo(userDto1.getLastActionDate()) != 0) {
return userDto2.getLastActionDate().compareTo(userDto1.getLastActionDate());
}
if (userDto1.getLastActionDate() == null && userDto2.getLastActionDate() == null) {
return 0;
}
if (userDto1.getLastActionDate() == null && userDto2.getLastActionDate() != null) {
return 1;
}
if (userDto1.getLastActionDate() != null && userDto2.getLastActionDate() == null) {
return -1;
}
if (userDto1.getLastName() != null && userDto2.getLastName() != null && userDto1.getLastName().compareTo(userDto2.getLastName()) != 0) {
return userDto1.getLastName().compareTo(userDto2.getLastName());
}
if (userDto1.getLastName() != null && userDto2.getLastName() == null) {
return -1;
}
if (userDto1.getLastName() == null && userDto2.getLastName() != null) {
return 1;
}
if (userDto1.getFirstName().compareTo(userDto2.getFirstName()) != 0) {
return userDto1.getFirstName().compareTo(userDto2.getFirstName());
}
if (userDto1.getFirstName() != null && userDto2.getFirstName() == null) {
return -1;
}
if (userDto1.getFirstName() == null && userDto2.getFirstName() != null) {
return 1;
}
return 0;
}
});*/
I wanted to sort descending of LastLoginDate, LastActionDate. Either or both fields might be null in some cases.
And ascending of names. And to display all other null field values(LastLoginDate, LastActionDate) at last.
But when i sort, with this code it takes only the first defined method to sort.
Example, if i have 3 records having same, "LastLoginDate" then it should check sort with "LastActionDate" values. But its not sorted with this logic.
So i added another algorithm to sort, since some fields has null values it is not properly sorted.
My second logic:
if (userDto1.getLastLoginDate() != null && userDto2.getLastLoginDate() != null && userDto2.getLastLoginDate().compareTo(userDto1.getLastLoginDate()) != 0) {
compareLastLoginDate = userDto2.getLastLoginDate().compareTo(userDto1.getLastLoginDate());
}
if (userDto1.getLastActionDate() != null && userDto2.getLastActionDate() != null && userDto2.getLastActionDate().compareTo(userDto1.getLastActionDate()) != 0) {
compareLastActionDate = userDto2.getLastActionDate().compareTo(userDto1.getLastActionDate());
}
if (userDto1.getLastName() != null && userDto2.getLastName() != null && userDto1.getLastName().compareTo(userDto2.getLastName()) != 0) {
compareLastName = userDto1.getLastName().compareTo(userDto2.getLastName());
}
if (compareLastLoginDate == 0) {
return ((compareLastActionDate == 0) ? compareLastName : compareLastActionDate);
}
else {
return compareLastLoginDate;
}
Here i am getting the integer values to compare it "compareLastActionDate, compareLastName, compareLastLoginDate".
I used simple date format to filter the list.
Here is my working code,
public int compare(UserDto userDto1, UserDto userDto2) {
DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
if (userDto1.getLastLoginDate() != null && userDto2.getLastLoginDate() != null && userDto2.getLastLoginDate().compareTo(userDto1.getLastLoginDate()) != 0) {
String Date1 = format.format(userDto1.getLastLoginDate());
String Date2 = format.format(userDto2.getLastLoginDate());
if (Date1.equals(Date2)) {
if (userDto1.getLastActionDate() != null && userDto2.getLastActionDate() != null && userDto2.getLastActionDate().compareTo(userDto1.getLastActionDate()) != 0) {
String Date3 = format.format(userDto1.getLastActionDate());
String Date4 = format.format(userDto2.getLastActionDate());
if (Date3.equals(Date4)) {
if (userDto1.getLastName() != null && userDto2.getLastName() != null && userDto1.getLastName().compareTo(userDto2.getLastName()) != 0) {
return userDto1.getLastName().compareTo(userDto2.getLastName());
}
} else {
return userDto2.getLastActionDate().compareTo(userDto1.getLastActionDate());
}
}
} else {
return userDto2.getLastLoginDate().compareTo(userDto1.getLastLoginDate());
}
}
if (userDto1.getLastLoginDate() != null && userDto2.getLastLoginDate() == null) {
return -1;
}
if (userDto1.getLastLoginDate() == null && userDto2.getLastLoginDate() != null) {
return 1;
}
if (userDto1.getLastLoginDate() == null && userDto2.getLastLoginDate() == null) {
return userDto1.getLastName().compareTo(userDto2.getLastName());
} }
I would recommend you to use the java8 comparators, it provides some helpful methods to compare based in multiple attributes with thenComparing().
If you don't want to / can't use, try two wrap part of that repetitive code in one or multiple custom Comparator or in a helper method.
Here, for simplicity, the following helper method is used (in java8 you can use method references):
public static <R extends Comparable<? super R>> int nullSort(R r1, R r2,
boolean ascending,
boolean nullsFirst) {
R rs1 = ascending ? r1 : r2, rs2 = ascending ? r2 : r1;
if (rs1 != null && rs2 != null) return rs1.compareTo(rs2);
else if (r1 == null && r2 == null) return 0;
else if (r1 == null) return (nullsFirst ? -1 : 1);
else return (nullsFirst ? 1 : -1);
}
In your method, you could use this nullSort() method, to check first for lastLoginDate descending and nulls last (false, false), then lastActionDate again descending and nulls last.
A difference with your comparator is that you stop evaluating here when both are null. It would be better that when lastLoginDate and lastActionDate are nulls you keep ordering by lastName and firstName. If you don't want this, you can add if (result == 0) return 0; after the second if in the method below.
Then after the dates are compared, you compare lastName and then firstName both ascending and nulls last (true, false).
#Override
public int compare(UserDto dto1, UserDto dto2) {
int result;
if ((result = nullSort(dto1.getLastLoginDate(), dto2.getLastLoginDate(),
false, false)) != 0) return result;
if ((result = nullSort(dto1.getLastActionDate(), dto2.getLastActionDate(),
false, false)) != 0) return result;
if ((result = nullSort(dto1.getLastName(), dto2.getLastName(),
true, false)) != 0) return result;
if ((result = nullSort(dto1.getFirstName(), dto2.getFirstName(),
true, false)) != 0) return result;
return 0;
}
I am iterating over two collections and check if both collections contain
the same elements. I can't use Java 8.
edit 1 year after:
I created the method in the question to check if two Collections contain the same elements, without thinking about the fact that I am passing two Collection implementations into the method.
But Collection does not determine how elements are sorted. And I am iterating over the collections. Thus, some implementation of Collection could save elements in random order, while containing the same elements.
Both collections contain elements that are comparable and the content
is defined as equal, if all elements return a x.compareTo(y) with 0.
Two values are defined as different, if one of them is null, but not the other.
I want to find an elegant way to compare on nullity and prevent
a null check on the final compareTo().
My current implementation:
public static <T extends Comparable<T>> boolean isSame(#Nullable Collection<T> a, #Nullable Collection<T> b) {
if (a == null || b == null) {
return (a == null && b == null);
}
if (a.size() != b.size()) {
return false;
}
Iterator<T> aIt = a.iterator();
Iterator<T> bIt = b.iterator();
while (aIt.hasNext()) {
T aValue = aIt.next();
T bValue = bIt.next();
if (aValue == null || bValue == null) {
if (aValue == null ^ bValue == null) {
return false;
}
//both null, don't compare, continue looping...
} else if (aValue.compareTo(bValue) != 0) {
return false;
}
}
return true;
}
I want to continue the while loop, if both values are null, because that is
defined as equal.
But I am struggling with this part:
if (aValue == null || bValue == null) {
if (aValue == null ^ bValue == null) {
return false;
}
}
Question:
Is there a more elegant and readable way to compare on nullity, do a further compare if both are not null, return false if only one is null, and continue the loop, if both values are null?
The sequence as follows should work well:
if(aValue == null && bValue == null) continue; // both null; continue
if(aValue == null || bValue == null) return false; // any null; return false
if(aValue.compareTo(bValue) != 0) { // both non-null; compare
return false;
}
In Java8, you can build a Comparator that would replace comparison sequence at cost of creating an extra object (you will need to decide if you care about that):
Comparator<T> cmp = Comparator.nullsLast(Comparator.naturalOrder());
The compararor will take care of null comparison for you (since you assume that two nulls are equal):
while (aIt.hasNext()) {
T aValue = aIt.next();
T bValue = bIt.next();
if (cmp.compare(aValue, bValue) != 0) {
return false;
}
}
So I understand what is going on inside of this method but am totally lost on how to write it. when i run this method i get with in my program i get. compareTo(T) in java.lang.Comparable cannot be applied to (java.lang.Comparable) on the BOLDED lines so hopefully you can point out what it is without me having to post all the different c;asses with it.
public boolean isBST(BinaryTreeNode<T> tree){
Comparable<T> temp1 = (Comparable<T>) tree.lLink.info;
Comparable<T> temp2 = (Comparable<T>) tree.rLink.info;
Comparable<T> temp3 = (Comparable<T>) tree.info;
if (tree == null)
return true;
else if (tree.lLink != null) // && tree.lLink.info > tree.info)
return false ;
**else if ( temp1.compareTo(temp3) > 0 )**
return false;
else if (tree.rLink != null) // && tree.rLink.info <= tree.info)
return false;
**else if ( temp2.compareTo(temp3) > 0 )**
return false;
else
return isBST(tree.lLink) && isBST(tree.rLink);
}//closes boolean isBST
I need to compare two binary search trees and see if they are equal or not.
I developed following code that uses recursion.
private boolean compareTrees(BinaryTreeNode n1, BinaryTreeNode n2)
{
if(n1.getNodeData() != n2.getNodeData())
return false;
else
{
if(n1.left != null && n2.left != null)
compareTrees(n1.left, n2.left);
if(n1.right != null && n2.right != null)
compareTrees(n1.right, n2.right);
}
return true;
}
The problem is that if two nodes are not equal, the method will return false but because I use recursion, the return value will be overridden to true no matter what. I have been stuck with this problem for all day and nothing worked for me. I searched online but I didn't find anything relevant to my code.
Is there any way to break from all nested methods and return value to the first method?
You need to return the result of the subtree comparison:
boolean b1, b2;
if(n1.left != null && n2.left != null)
b1 = compareTrees(n1.left, n2.left);
if(n1.right != null && n2.right != null)
b2 = compareTrees(n1.right, n2.right);
return b1 && b2;
But why not just deal with nulls before-hand?
private boolean compareTrees(BinaryTreeNode n1, BinaryTreeNode n2)
{
if (n1 == null || n2 == null)
return n1 == n2; // i.e. both null
if (n1.getNodeData() != n2.getNodeData())
return false;
return compareTrees(n1.left, n2.left) && compareTrees(n1.right, n2.right);
}
I would do it changing the order:
private boolean compareTrees(BinaryTreeNode n1, BinaryTreeNode n2)
{
boolean equalLeft = false;
boolean equalRight = false;
if(n1.getNodeData() == n2.getNodeData())
{
if(n1.left != null && n2.left != null){
equalLeft = compareTrees(n1.left, n2.left);
} else{
equalLeft = true;
}
if(n1.right != null && n2.right != null){
equalRight = compareTrees(n1.right, n2.right);
} else{
equalRight = true;
}
return equalLeft && equalRight;
} else{
return false;
}
}
Try to face the problem avoiding null values and using equals() method instead of == comparison for your nodes. I shoud do it this way:
private boolean compareTrees(BinaryTreeNode n1, BinaryTreeNode n2){
//avoid nulls :TDD
if (n1==null && n1==n2)
return true;
if ((n1==null && n2!=null) || (n2==null && n1!=null))
return false;
//ensure logic without nulls, comparing with equals() method
boolean areEquals = n1.getNodeData().equals(n2.getNodeData());
//compare left
areEquals = areEquals && compareTrees(n1.left, n2.left);
//if still equals, compare right
if(areEquals) areEquals = areEquals && compareTrees(n1.right, n2.right);
return areEquals;
}
Effectively, your code could reduce to:
private boolean compareTrees(BinaryTreeNode n1, BinaryTreeNode n2)
{
if(n1==null || n2==null) return n1==n2;
return (n1.getNodeData()==n2.getNodeDate()) && compareTrees(n1.left, n2.left) && compareTrees(n1.right, n2.right)
}
I will tell you couple of problems your code has.
Termination criteria when root is null (it will always happen in the end).
Return statements in recursive calls. You are always returning the true in the end.
PS: If you add NULL checks (explained in 1), you need not to add null checks in the subsequent recursive calls. Now the second half of your code will look like:
return compareTrees(n1.left, n2.left) && compareTrees(n1.right, n2.right);