Verify if an object exist in a List - java

What I need is: Verify if an object exist in a List comparing some attributes.
I'm in a trouble here with Collections and Comparator. I'm trying to do the verify with this Binary Search:
Collections.binarySearch(listFuncionarioObs2, formFuncionarioObsIns, formFuncionarioObsIns.objectComparator);//Binary search of an object in a List of this Object.
With this comparator:
public int compare(FuncionarioObs func, FuncionarioObs funcToCompare) {
int testCodigo = -1;
if(null != func2.getCodigo()){
testCodigo = func.getCodigo().compareTo(funcToCompare.getCodigo());
}
int testData = func.getData().compareTo(funcToCompare.getData());
int testEvento = func.getEvento().compareTo(funcToCompare.getEvento());
int testAndamento = func.getAndamento().compareTo(funcToCompare.getAndamento());
if(testCodigo == 0 && testData == 0 && testEvento == 0 && testAndamento == 0){
return 0;
}else if(testData == 0 && testEvento == 0 && testAndamento == 0) {
return 0;
}
return -1;
}
But I'm a little bit lost, this is not working and I don't know the best way to do this. Someone can turn on a light for me?
Best regards,
Edited.
I'm sorting the List before the Binary Search with this code:
List<FuncionarioObs> listFuncionarioObsBD = funcionarioObsDAO.getFuncionarioObsById(sigla);
Collections.sort(listFuncionarioObsBD);
The comparator to the sort is:
#Override
public int compareTo(FuncionarioObs func) {
if(this.getCodigo() > func.getCodigo()){
return 1;
}else if(this.getCodigo() == func.getCodigo() ) {
return 0;
}else{
return -1;
}
}

CompareTo
Your compare wont work correctly. Right now it is only comparing the references of the objects. You will have to change this to compare the objects values:
#Override public int compareTo(Account aThat) {
final int BEFORE = -1;
final int EQUAL = 0;
final int AFTER = 1;
//this optimization is usually worthwhile, and can
//always be added
if (this == aThat) return EQUAL;
//primitive numbers follow this form
if (this.fAccountNumber < aThat.fAccountNumber) return BEFORE;
if (this.fAccountNumber > aThat.fAccountNumber) return AFTER;
//booleans follow this form
if (!this.fIsNewAccount && aThat.fIsNewAccount) return BEFORE;
if (this.fIsNewAccount && !aThat.fIsNewAccount) return AFTER;
.
.
.
//all comparisons have yielded equality
//verify that compareTo is consistent with equals (optional)
assert this.equals(aThat) : "compareTo inconsistent with equals.";
return EQUAL;
}
from here
Finding the object
Now comes the next part. As CrtlAltDelete has hinted it dependends of whether your list is sorted or not.
If its sorted ascending: iterate through the objects till you either find one which compareTo returns a Zero (== success) or a One ( == fail).
For an unsorted list you will have to iterate through all objects in search for one that returns a Zero.

Related

How can I sort an Arraylist of objects using multiple criteria on one Comparator?

I'm trying to implement a Comparator in Java using Kotlin Collections (SortedSet). In my arraylist I have multiple objects of Arraylist<Payment>(). I also have a payments Hashmap with some of the payments HashMap<String, Integer>() in getPaymentIds(), String value is my payment id, Integer is the count
My sorting order is first all the payments contained in the Hashmap with a value greater than 1 sorted by the Integer value (count), then payments not in the Hashmap
This is what I have been playing with but the problem is it returns a Set with only one element
public final int compare(final Payment o1, final Payment o2) {
boolean b2 = true;
if (getPaymentIds().get(o2.getId()) != null) {
final Integer count = getPaymentIds().get(o2.getId());
if (Intrinsics.compare(count, 1) >= 0) {
b2 = false;
}
}
boolean b = false;
if (getPaymentIds.get(o1.getId()) != null) {
final Integer value3 = getPaymentIds.get(o1.getId());
if (Intrinsics.compare(value3, 1) < 0) {
b = true;
}
}
return ComparisonsKt.compareValues(b2, b);
I have played around with this code and the closest am getting is receiving the sorted set with only one element
You can cascade in your checks. In my example, I am using a, b and c as the value fields in Payment
public int compareTo(Payment p1, Payment p2) {
int diff = p2.a == null ? -1 : p2.a - p1.a == null ? -1 : p1.a; // if a is a number, than p2.a - p1.a correctly returns the order (as per compareTo's api) between them
if (diff != 0) return diff;
diff = p2.b == null ? -1 : p2.b - p1.b == null ? -1 : p1.b;
if (diff != 0) return diff;
diff = p2.c == null ? -1 : p2.c - p1.c == null ? -1 : p1.c;
return diff; // if diff is still 0, the objects are equal, and either order is acceptable
}

Trying to compare the contents two Iterators, how?

EDIT: With your help I managed to fix my problem. I have edited my code to now show how I had to have it set up to get it working.
Currently I am having trouble coding a part which compares the content of two iterators. As part of the requirements for my assignment, I need to use a linkedlist to store the individual characters of the entered String. I have gotten to the point where I have two iterators which would contain the input one way and the reverse way.
String palindrom = input.getText();
String [] chara = palindrom.split (""); //this is successfully splitting them, tested.
int length = palindrom.length( ); // length == 8
System.out.println (length); //can use this for how many checks to do?
LinkedList ll = new LinkedList(Arrays.asList(chara));
Iterator iterator = ll.iterator();
Iterator desIterator = ll.descendingIterator();
/*while(iterator.hasNext() ){
System.out.println(iterator.next() );
}
while(desIterator.hasNext() ){
System.out.println(desIterator.next() );
}*/
boolean same = true;
while(iterator.hasNext()){
if(!iterator.next().equals(desIterator.next())){
same = false;
break;
}
}
And using the System.out I can see that they are being stored correctly, but I don't know how to check if the iterators store the same contents. What would be one of the simplest methods to compare the two iterators or convert them into something I can compare? To clarify I want to verify they contain the same elements in the same order.
boolean same = true;
while(iterator.hasNext()){
if(!desIterator.hasNext() || !iterator.next().equals(desIterator.next())){
same = false;
break;
}
}
System.out.println(same);
You need to iterate over both iterators simultaneously, i.e. with one loop. Here is a general comparison function (0 when equal, < 0 when A < B, > 0 when A > B):
static <T extends Comparable<S>, S> int compare(Iterator<T> a, Iterator<S> b) {
while (a.hasNext() && b.hasNext()) {
int comparison = a.next().compareTo(b.next());
if (comparison != 0) {
return comparison;
}
}
if (a.hasNext())
return 1;
if (b.hasNext())
return -1;
return 0;
}
To just check if they are equal, this can be simplified:
static <T, S> boolean equals(Iterator<T> a, Iterator<S> b) {
while (a.hasNext() && b.hasNext()) {
if (!a.next().equals(b.next())) {
return false;
}
}
if (a.hasNext() || b.hasNext()) {
// one of the iterators has more elements than the other
return false;
}
return true;
}
Guava implements this as Iterators.elementsEqual.
In both answers throw NullPointerException, if iterator.next() == null. This method is more optimal.
public static boolean equals(Iterator i1, Iterator i2) {
if (i1 == i2) {
return true;
}
while (i1.hasNext()) {
if (!i2.hasNext()) {
return false;
}
if (!Objects.equals(i1.next(), i2.next())) {
return false;
}
}
if (i2.hasNext()) {
return false;
}
return true;
}

How to filter list using custom equality

I have a list of objects. The Object looks similar to this one:
class Data {
...
private X somethig;
private Y somethigElse;
public boolean customEquals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Data)) {
return false;
}
Data other = (Data) obj;
if (something == null) {
if (other.something != null) {
return false;
}
} else if (!something.equals(other.something)) {
return false;
}
if (somethigElse == null) {
if (other.somethigElse != null) {
return false;
}
} else if (!somethigElse.equals(other.somethigElse)) {
return false;
}
return true;
}
public boolean equals(Object obj) {
...
}
public int hashCode() {
...
}
getters/setters
}
I need to filter the list to get distinct objects from it.
Note the equals and hashCode methods are implemented (they use another fields) and I can't use equals for this task. So the equality is not defined by equals but by 'something' and 'somethigElse' properties. How can I do that?
I have tried:
final Comparator<Data> comparator = new Comparator<Data>() {
#Override
public int compare(Data o1, Data o2) {
return o1.customEquals(o2) ? 0 : 1;
}
};
Set<Data> set = new TreeSet<Data>(comparator);
set.addAll(list);
System.out.println(set);
But the set still contains some of the objects several times.
That's because you are not providing a proper ordering function.
TreeSet sorting algorithm makes a few assumptions, like:
compare(a, b) > 0 => compare(b, a) < 0
compare(a, b) > 0 && compare(b, c) > 0 => compare(a, c) > 0
and so on.
Implement a proper comparison, not just an 'equals' and it should work.
final Comparator<Data> comparator = new Comparator<Data>() {
#Override
public int compare(Data o1, Data o2) {
int k = o1.getSomething().compareTo(o2.getSomething();
if (k != 0) {
return k;
}
return o1.getSomethingElse() - o2.getSomethingElse();
}
};
That is because TreeSet is a SortedSet, and you are telling that every new element that arrives is greater than others but itself.
Let's make a simplification to have a shorter example of what's going on, suppose that we are using just numbers with your comparator and the storage is an array (and binary search) rather than a tree because it's easier to represent.
We got number 1, it's the only element, so the array is [1] now.
We get a 0 now, but as you are telling the set that it's bigger, we get [1, 0].
Let's add a 3 now for example, we'll get [1, 0, 3].
Add another 1, the bisection will try to compare it with the middle element 0 and see that it's greater, go to the other half, compare with the 3 and it's greater again, so we get [1, 0, 3, 1].
If we add another 1, now it will see that the element is already there and won't add it, but if you add any other number and it happens that its repetitions are not in any of the bisection points, it will always end up at the end of the array.
Probably you need to fix your compare():
public int compare(Data o1, Data o2)
{
int i = o1.getSomething().compareTo(o2.getSomething());
if (i != 0) return i;
return o1.getSomethingElse()- o2.getSomethingElse();
}

Sorting an array using two different criteria

I am trying to sort an array using the insertion sort algorithm. The array is filled with WordNode elements that include a word field (inputted from a text file) and a frequency field (to measure the number of times the particular word appears in the text file). I have implemented the sort so that words are sorted by frequency (from lowest to highest), but I also want to sort alphabetically if frequencies are equal. How can I sort using two different criteria at the same time? Below is my sort code.
public static void sort(ArrayUnorderedList<WordNode> array) {
//create stacks for insertion sort
LinkedStack<WordNode> sorted = new LinkedStack<WordNode>();
LinkedStack<WordNode> temp = new LinkedStack<WordNode>();
//while the array has elements to be sorted
while(!array.isEmpty()) {
//remove current element from array
WordNode currentNode = array.removeFirst();
//while the sorted stack meets sorting criteria
while((!sorted.isEmpty()) && (sorted.peek().getFrequency() < currentNode.getFrequency())) {
//push elements to temp stack
temp.push(sorted.pop());
}
//push current element to sorted stack
sorted.push(currentNode);
//while the temp stack has elements to be replaced
while(!temp.isEmpty()) {
//push elements to sorted stack
sorted.push(temp.pop());
}
}
//replace sorted elements in array
while(!sorted.isEmpty()) {
array.addToRear(sorted.pop());
}
}
AppClay's answer is absolutely correct, but if you are interested in "tidying it up", create a helper that implements Comparator.
class WordNodeComparator implements Comparator<WordNode> {
#Override
public int compare(WordNode lhs, WordNode rhs) {
int result = lhs.getFrequency() - rhs.getFrequency();
if (result == 0) {
return lhs.getWord().compareTo(rhs.getWord());
}
else {
return result;
}
}
}
Then you simply create an instance of it, and use it in your loop:
while((!sorted.isEmpty()) && (nodeComparator.compare(sorted.peek(), currentNode) < 0)
Not only does this make the code easier to read and test, it's now trivial to swap out different Comparator implementations as needed.
Update this line:
while((!sorted.isEmpty()) && (sorted.peek().getFrequency() < currentNode.getFrequency())) {
to:
while((!sorted.isEmpty()) && (sorted.peek().getFrequency() < currentNode.getFrequency() || (sorted.peek().getFrequency() == currentNode.getFrequency() && sorted.peek().getWord().compareTo(currentNode.getWord()) < 0))) {
Add this to your code:
int cmp = sorted.peek().getFrequency() - currentNode.getFrequency();
if (cmp == 0)
cmp = sorted.peek().getWord().compareTo(currentNode.getWord());
while((!sorted.isEmpty()) && cmp < 0) {
//push elements to temp stack
temp.push(sorted.pop());
cmp = sorted.peek().getFrequency() - currentNode.getFrequency();
if (cmp == 0)
cmp = sorted.peek().getWord().compareTo(currentNode.getWord());
}
I'm assuming that getFrequency() returns an integer and that the actual word in a WordNode is accessed by the getWord() method. Using the above we compare first by frequency, and if both frequencies are equal then we compare alphabetically
EDIT :
A nicer solution, define this helper method:
private static boolean compare(LinkedStack<WordNode> sorted, WordNode current) {
int cmp = sorted.peek().getFrequency() - current.getFrequency();
if (cmp == 0)
cmp = sorted.peek().getWord().compareTo(current.getWord());
return cmp;
}
And then change the first inner loop in your code to this:
while (!sorted.isEmpty() && compare(sorted, currentNode) < 0) {
//push elements to temp stack
temp.push(sorted.pop());
}
Use Guava lib:
public static List<WordNode> sort(List<WordNode> src){
List<WordNode> result = Lists.newArrayList(src);
Collections.sort(result, new Comparator<WordNode>(){
#Override public int compare(WordNode w1, WordNode w2) {
return ComparisonChain.start()
.compare(w1.frequency, w2.frequency)
.compare(w1.word , w2.word)
.result();
}});
return result;
}

Sorting a custom type array by a string attribute?

I have an array of a custom type that I want to sort by one of its String attributes. For some reason, the following code is producing wrong results. Could you point out where I might have made a mistake?
class PatientLNComparator implements Comparator<Patient>{
#Override
public int compare(Patient p1, Patient p2) {
String p1_LN = (p1 == null) ? null : p1.last_name;
String p2_LN = (p2 == null) ? null : p2.last_name;
if(p2_LN == null)
return -1;
else if(p1_LN == null)
return +1;
else if(p1_LN.equals(p2_LN))
return 0;
else if(p1_LN.compareTo(p2_LN) > 0)
return -1;
else
return +1;
}
}
One problem to start with - your comparator is inconsistent if you give it two patients with null names, or two null patient references. In particular:
Patient p1 = null;
Patient p2 = null;
int x = comparator.compare(p1, p2);
int y = comparator.compare(p2, p1);
The signs of x and y ought to be different - but they'll both be -1.
After that, it depends on how you want to compare the names. I would usually use
return p1_LN.compareTo(p2_LN);
if you want to sort in ascending order. Note that to sort in descending order you shouldn't just return -p1_LN.compareTo(p2_LN), as if the comparison returns the Integer.MIN_VALUE, the negation won't work. Instead you'd want to return p2_LN.compareTo(p1_LN);.
Note that if you're using this scheme, you don't need to call p1_LN.equals(p2_LN) either - that will be handled by the compareTo call.
You want patient to be ordered by alphabetical by last name, null patients and null last names up front?
class PatientLNComparator implements Comparator<Patient>{
#Override
public int compare(Patient p1, Patient p2) {
String p1_LN = (p1 == null) ? null : p1.last_name;
String p2_LN = (p2 == null) ? null : p2.last_name;
if (p1_LN == null && p2_LN == null)
return 0;
else if (p2_LN == null)
return -1;
else if(p1_LN == null)
return +1;
else
return p1_LN.compareTo(p2_LN);
}
}
To be stable, it really should order by some other fields, like first name, when last names are equal.
I'm assuming you want natural string ordering for this.
First of all, as it is, your compareTo branch is giving inversed results. Don't know if that's what you intended or not (as in you're saying p1 is greater than p2 when the p1's string is lower than p2's).
Furthermore, you can ditch the .equals branch of the if. The compareTo already handles this case.
Therefore a simple
if(p2_LN == null && p1_LN == null)
return 0;
else if(p1_LN == null)
return +1;
else if(p2_LN == null)
return -1;
else return p1_LN.compareTo(p2_LN)
would suffice.
I would use Guava's Ordering class for this:
class Patient {
// ...
public static final Function<Patient, String> GET_LAST_NAME =
new Function<Patient, String>() {
public String apply(Patient from) {
if (from == null) return null;
return from.last_name;
}
};
public static final Comparator<Patient> BY_LAST_NAME =
Ordering.natural()
.onResultOf(GET_LAST_NAME)
.nullsFirst();
}
This will resolve the issue with inconsistent comparison of nulls. It also makes it easy to add a secondary order (e.g. first name):
public static final Comparator<Patient> BY_LAST_NAME =
Ordering.natural()
.onResultOf(GET_LAST_NAME)
.compound(Ordering.natural().onResultOf(GET_FIRST_NAME))
.nullsFirst();

Categories