Javafx List<Data<Number,Number>> removeAll didn't work - java

I have this :
List<Data<Number, Number>> dataList = new ArrayList<XYChart.Data<Number,Number>>();
List<Data<Number, Number>> dataListSelected = new ArrayList<XYChart.Data<Number,Number>>();
I fill them with some Data<Number,Number> (in reality it is some Double). I do this for the two list.
for (XYZPointModel xyz : pm.getTopoPoints()) {
topoPoints.getData().add(new Data<Number, Number>(xyz.getX(),xyz.getY()));
}
and then i compare the two list. I want to know if i have some data in dataList that i dont have in dataListSelected.
System.out.println(dataList.removeAll(dataListSelected));
I got a FALSE.
Here the containt of my 2 list :
3 : [Data[18.367963,-0.832832,null], Data[30.165189,-0.461874,null], Data[18.808959,-6.575699,null]]
3 : [Data[18.367963,-0.832832,null], Data[30.165189,-0.461874,null], Data[18.808959,-6.575699,null]]
Btw, i don't know why i have a double, double, null. I dont know where the null come from.
Why my removeAll don't work ?

I presume Data is actually JavaFX.XYChart.Data, which has another object node as per docs. Therefore, this is null. I do not know if the "toString" method prints the value of the node object, but it sure seems like it.

ArrayList (and many other collections) use the contains() (which uses equals() method) to determine whether to remove an object in the remove()/removeAll() methods. if you check the following condition:
dataList.get(0).equals(dataListSelected.get(0))
my guess is it would be false, and therefore removeAll fails as well.

1、ArrayList use equals() to remove,
if (o.equals(elementData[i]))
return i;
but XYChart.Data didn't override equals() method, so Data will use method of Object
public boolean equals(Object obj) {
return (this == obj);
}
2、XYChart.Data has three properties
X xValue, Y yValue, Object extraValue
double, double, null

Related

Why does super.hashCode give different results on objects from the same Class?

I have a class DebugTo where if I have two equal instances el1, el2 a HashSet of el1 will not regard el2 as contained.
import java.util.Objects;
public class DebugTo {
public String foo;
public DebugTo(String foo) {
this.foo = foo;
}
#Override
public int hashCode() {
System.out.println(super.hashCode());
return Objects.hash(super.hashCode(), foo);
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
DebugTo that = (DebugTo) o;
return Objects.equals(foo, that.foo);
}
}
var el1 = new DebugTo("a");
var el2 = new DebugTo("a");
System.out.println("Objects.equals(el1, el2): " + Objects.equals(el1, el2));
System.out.println("Objects.equals(el2, el1): " + Objects.equals(el2, el1));
System.out.println("el1.hashCode(): " + el1.hashCode());
System.out.println("el2.hashCode(): " + el2.hashCode());
Objects.equals(el1, el2): true
Objects.equals(el2, el1): true
1205483858
el1.hashCode(): -1284705008
1373949107
el2.hashCode(): -357249585
From my analysis I have gathered that:
HashSet::contains calls hashCode not equals (relying on the Objects.equals(a, b) => a.hashSet() == b.hashSet())
super.hashCode() gives a different value both times.
Why does super.hashCode() give different results for el1 and el2? since they are of the same class, they have the same super class and so I expect super.hashCode() to give the same result for both.
The hashCode method was probably autogenerated by eclipse. If not answered above, why is super.hashCode used wrong here?
Because the default implementations of the equals and hashCode methods (which go hand in hand - you always override both or neither) treat any 2 different instances as not equal to each other. If you want different behaviour, you override equals and hashCode, and do not invoke super.equals / super.hashCode, or there'd be no point.
HashSets work as follows: They use .hashCode() to know which 'bucket' to put the object into, and if 2 objects end up in the same bucket, equals is used only on those very few objects to double check.
In other words, these are the rules:
If a.equals(b), then b.equals(a) must be true.
a.equals(a) must always be true.
If a.equals(b) and b.equals(c), a.equals(c) must be true.
If a.equals(b), a.hashCode() == b.hashCode() must be true.
The reverse of 4 does not hold: If a.hashCode() == b.hashCode(), that doesn't mean a.equals(b), and hashset does not require it.
Therefore, return 1; is a legal implementation of hashCode.
If a class has really bad hashcode spread (such as the idiotic but legal option listed in bullet 6), then the performance of hashset will be very bad. e.g. set.containsKey(k) which ordinarily takes constant time, will take linear time instead if your objects are all not-equal but have the same hashCode. Hence, do try to ensure hashcodes are as different as they can be.
HashSet and HashMap require stable objects, meaning, their behaviour when calling hashCode and equals cannot change over time.
From the above it naturally follows that overriding equals and not hashCode or vice versa is necessarily broken.
Breaking any of the above rules does not, generally, result in a compiler error. It often doesn't even result in an exception. But instead it results in bizarre behaviour with hashsets and hashmaps: You put an k/v pair in the map, and then immediately ask for the value back and you get null back instead of what you put in, or something completely different. Just an example.
NB: One weird effect of all this is that you cannot add equality-affecting state to subclasses, unless you apply a caveat that most classes including all classes in the core libraries don't apply.
Imagine as an example that we invent the notion of a 'coloured' arraylist. You could have a red '["Hello", "World"]' list, and a blue one:
class ColoredArrayList extends ArrayList {
Color color;
public ColoredArrayList(Color c) {
this.color = color;
}
}
You'd probably want an empty red list to not equal an empty blue one. However, that is impossible if you intend to follow the rules. That's because the equals/hashCode impl of ArrayList itself considers any other list equal to itself if it has the same items in the same order. Therefore:
List<String> a = new ArrayList<String>();
ColoredList<String> b = new ColoredList<String>(Color.RED);
a.equals(b); // this is true, and you can't change that!
Therefore, b.equals(a) must also be true (your impl of equals has to say that an empty red list is equal to an empty plain arraylist), and given that an empty arraylist is also equal to an empty blue one, given that a.equals(b) and b.equals(c) implies that a.equals(c), a red empty list has to be equal to a blue empty list.
There is an easy solution for this that brings in new problems, and a hard solution that is objectively better.
The easy solution is to define that you can't be equal to anything except exact instances of yourself, as in, any subclass is insta-disqualified. Imagine ArrayList's equals method returns false if you call it with an instance of a subclass of ArrayList. Then you could make your colored list just fine. But, this isn't necessarily great, for example, you probably want an empty LinkedList and an empty ArrayList to be equal.
The harder solution is to introduce a second method, canEqual, and call it. You override canEqual to return 'if other is instanceof the nearest class in my hierarchy that introduces equality-relevant state'. Thus, your ColoredList should have #Override public boolean canEqual(Object other) { return other instanceof ColoredList; }.
The problem is, all classes need to have that and use it, or it's not going to work, and ArrayList does not have it. And you can't change that.
Project Lombok can generate this for you if you prefer. It's not particularly common; I'd only use it if you really know you need it.

Java (ArrayList check with object's int)

I have to make an ArrayList that contains an object, the object has one int for year lets say 1
and I don't what another object with the same year 1.
If one object has the int = 1 , i dont want another object with that int(1) in my list.
i want to deny it.
Should I try using equal?
something like
#Override
public boolean equals(Object o){
Object object = (Object)o;
return this.getInt.equals(object.getInt());
}
Either use a Set...which explicitly disallows duplicates, or check if the list contains the element on insertion.
#Override
public boolean add(T element) {
if(contains(element)) {
return false;
} else {
return super.add(element);
}
}
Overriding equals wouldn't get you very far, as you'd be overriding it for the List itself (i.e. you'd be checking if two lists were equal).
Perhaps you can try using a HashMap linked that links that "int" with the object. That could be:
Map<Integer, Object> map = new HashMap<>();
map.put(object.getInt(), object);
...
//Each time you put a new object you could try this:
if(!map.contains(object.getInt()))
map.put(object.getInt, object);
//And you can retrieve your object by an int
int a = 1;
Object obj = map.get(1);
In this case, as the value is of type int, you can use equal operator.
public boolean equals(Object o){
Object object = (Object)o;
return (this.getInt()==object.getInt());
}
For this kind of requirement, ArrayList is not suggestible. As mentioned in the other answers try using HashMap.
Yes, you can. When you call
myArrayList.contains(myObejct);
the ArrayList will invode myObejct's equals method. So you can tell if the object is already in you list.
And I think you can change you method a little,
#Override
public boolean equals(Object o){
if (!(o instanceof YourClass))
return false;
YourClass object = (YourClass)o;
return this.getInt.equals(object.getInt());
}
because if you don't, the method "getInt" might cause a MethodNotFound exception.
Well, that is one way to approach the problem.
Your equals will probably work provided that you change Object object = (Object)o; to cast to the real class.
However, equals ought to cope with the case where o is not of the expected type. The contract requires you should return false rather than throwing a ClassCastException ...
You would then use list.contains(o) to test if an object with the same int value exists in the list. For example:
if (!list.contains(o)) {
list.add(o);
}
But when you override equals, it is best practice to also override hashcode ... so that your class continues to satisfy the equals / hashcode invariants. (If you neglect to do that, hash-based data structures will break for your class.)
However, this won't scale well, because the contains operation on an ArrayList has to test each element in the list, one at a time. As the list gets longer, the contains call takes longer ... in direct proportion; i.e. O(N) ... using Big O complexity notation.
So it may be better to use a Set implementation of some kind instead on ArrayList. Fepending on which set implementation you choose, you will get complexity of O(1) or O(logN). But the catch is that you will either have to to implement hashcode (for a HashSet or LinkedHashSet), or implement either Comparable or a Comparator (for a TreeSet).

Do I use list.remove correctly?

Assume that I know that the list SomeList contains thatObj. Does the following code remove reference to thatObj from SomeList or not?
SomeClass el = (SomeClass) thatObj.clone();
SomeList.remove(el);
Can't find through the reference if this method compares objects somehow. Intuition suggests that it should use Object.equals which returns true if references point to the same object, hence this code will not work.
If not then an additional question: how to remove from list if don't have the reference but know all the members of the object in question?
Can't find through the reference if this method compares objects somehow. Intuition suggests that it should use Object.equals which returns true if references point to the same object, hence this code will not work.
Yes, you are right.
If not then an additional question: how to remove from list if don't have the reference but know all the members of the object in question?
Two possibilities:
override the equals method in your class, create a new instance with all known members and call remove passing the newly created instance as a parameter
iterate through all the objects inside the list and remove the one that has the members equal to the values you have
remove method internally uses the equals method to check for the object in the list. If equal returns true then it will be removed. Overriding the equals method will allow to remove the objects properly. For your reference here is the code of ArrayList remove method:
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
Override the equals method on the class - here is the javadoc. Also look at Overriding the java equals() method quirk and Overriding equals and hashCode in Java.
Search the list to find the member by returning the index, then get the object and remove it. You can also remove it by the index. The code
SomeList.indexOf()
could help you to get the index of the object that override equals() and hashCode().

Why does my custom equals method (doubles and integers) not work?

I have a custom equals to check the equality of my object called Pair.
class Pair implements Comparable <Parr> {
double coef;
int power;
Pair(double a, int b) {
coef = a;
power = b;
}
My custom equals method is (located in class pair):
#Override
public boolean equals(Object o) {
if (!(o instanceof Pair))
return false;
Pair that = (Pair) o;
return that.coef == this.coef && that.power == this.power;
}
I've checked with print my object if the objects are the same, and they are indeed the same.
1.0 1 2.0 0
1.0 1 2.0 0
I call my custom equals from a different file, called Test.
class Test {
public static void main(String[] args) {
orig = pol1.differentiate().integrate();
System.out.print(orig);
if (orig.equals(pol1))
System.out.println(" (is equal.)");
else
System.out.println(" (is not equal.)");
And my class Polynomial, which is an arraylist with objects of Pair inside.
class Polynominal implements PolynominalInterface {
ArrayList<Pair> terms = new ArrayList<Pair>();
I looked on the internet, and I found that I cannot use == in my Equals method, but I'm using Intergers and Doubles, so equals() would not work.
Can anyone point me in the right direction?
If orig and pol1 are instances of Polynomial then this
if (orig.equals(pol1))
would only work if you implement Polynomial#equals() as well; which would iterate the two ArrayLists and make sure individual Pairs are equal (using Pair#equals() of course).
Ok, thanks to Ravi Thapliyal I found the solution.
After adding an custom equals method in my Polynominal class, the problem was fixed.
#Override
public boolean equals(Object o) {
if (!(o instanceof Polynomial))
return false;
Polynomial that = (Polynomial) o;
return that.terms.equals(terms);
}
Use the Double.compare(double, double) method instead of ==.
Floating point comparison is "fuzzy" in Java.
You would need to implement a Polynomail.equals() method something like the following:
public boolean equals(Object o) {
if (!(o instanceof Polynomial)) return false;
Polynomial other = (Polynomial) o;
if (this.terms==null && other.terms==null) return true;
// A suitable equals() method already exists for ArrayList, so we can use that
// this will in turn use Pair.equals() which looks OK to me
if (this.terms!=null && other.terms!=null) return this.terms.equals(other.terms);
return false;
}
Two issues come to mind: the first is that the default hashCode() method will seldom return the same value for any two distinct object instances, regardless of their contents. This is a good thing if the equals() method will never report two distinct object instances as equal, but is a bad thing if it will. Every object which overrides Object.equals() should also override Object.hashCode() so that if x.equals(y), then x.hashCode()==y.hashCode(); this is important because even non-hashed generic collections may use objects' hash codes to expedite comparisons. If you don't want to write a "real" hash function, simply pick some arbitrary integer and have your type's hashCode() method always return that. Any hashed collection into which your type is stored will perform slowly, but all collections into which it is stored should behave correctly.
The second issue you may be seeing is that floating-point comparisons are sometimes dodgy. Two numbers may be essentially equal but compare unequal. Worse, the IEEE decided for whatever reason that floating-point "not-a-number" values should compare unequal to everything--even themselves.
Factoring both of these issues together, I would suggest that you might want to rewrite your equals method to chain to the equals method of double. Further, if neither field of your object will be modified while it's stored in a collection, have your hashCode() method compute the hashCode of the int, multiply it by some large odd number, and then add or xor that with the hashCode of the double. If your object might be modified while stored in a collection, have hashCode() return a constant. If you don't override hashCode() you cannot expect the equals methods of any objects which contain yours to work correctly.

Java add remove methods of sets

Why does the method add(<T> element) and remove(Object o) accept different arguments?
For example in a Set<Short> you add short elements. Why does the method remove accepts Object? If you can't add any other data type, why would you remove other data type?
Thank you.
add(<T> element) : to ensure that just a T element is added.
remove(Object o) : you can delete the T element even if it's a referenced by an Object reference.
For instance :
Set<Short> set = new HashSet<Short>();
Short number = 2;
set.add(number);
Object numberObject = number;
set.remove(numberObject) // it will remove 2 from the set.
why would you remove other data type? we're not removing another data type, but we can remove data even if it is referenced by an Object reference (like in the example).
The remove(obj) method, removes the object such that (obj == null ? e ==null : obj.equals(e)) is true. It's because of the equals(Object) method, which takes an Object, that obj and e doesn't have to be of the same type.

Categories