Java Unique List - java

I am currently trying to get a List with unique values.
Technically, it should be really simple, and the obvious choice would be a HashSet.
However, I want the properties of my "points" to be the uniqueness criteria and not their "IDs".
After that, I wanted to use the stream().distinct() method. Hoping that that one is using the overridden equals method. Sadly, this won't work either.
Any solution/Idea that works for double[] is welcome as well.
PointType Class
public class PointType{
private double x;
private double y;
public PointType(x,y){
this.x = x;
this.y = y;
}
#Override
public boolean equals(Object other){
if(other instanceof PointType && this.x == other.x && this.y==other.y){
return true;
}
return false;
}
}
I am aware of the flaw of double == double. For a minimum sample, it is sufficient.
Now to the issue:
#Test
public void testUniquness(){
Set<PointType>setA = new HashSet<>();
Set<PointType>setB = new HashSet<>();
ArrayList<PointType> listA= new ArrayList<>();
ArrayList<PointType> listB= new ArrayList<>();
PointType p1 = new PointType(1.0,2.0);
PointType p2 = new PointType(1.0,2.0);
PointType p3 = new PointType(2.0,2.0);
PointType p4 = new PointType(2.0,2.0);
// Trying to use the unique properties of a HashSet
setA.add(p1);
setA.add(p2);
setA.add(p1);
setA.add(p2);
setB.add(p1);
setB.add(p2);
setB.add(p3);
setB.add(p4);
//Now with array lists and streams.
listA.add(p1);
listA.add(p2);
listA.add(p1);
listA.add(p2);
listA = (ArraList<PointType>) listA.stream().distinct().collect(Collectors.toList());
listB.add(p1);
listB.add(p2);
listB.add(p3);
listB.add(p4);
listB = (ArraList<PointType>) listB.stream().distinct().collect(Collectors.toList());
assertTrue(p1.equals(p2)); // Test passes
assertTrue(p3.equals(p4)); // Test passes
assertTrue(setA.size() == 2); // Test passes (obviously)
assertTrue(setB.size() == 2); // Test failes. How can I use my custom equality condition?
assertTrue(listA.size() == 2); // Test passes (obviously)
assertTrue(listb.size() == 2); // Test failes. How can I use my custom equality condition?
}
Any help is appreciated.
For sure, a for loop would solve this too. But there has to be a more elegant way.

First issue, your implementation of equals is not correct, this is what it should look like (auto-generated with IntelliJ) :
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PointType pointType = (PointType) o;
return Double.compare(pointType.x, x) == 0 && Double.compare(pointType.y, y) == 0;
}
Second and most important problem, as the word Hash in HashSet suggests, you should not only implement equals but also hashCode, and this is not only true for hash collections but in general (every time you implement equals, you also implement hash code):
#Override
public int hashCode() {
return Objects.hash(x, y);
}
Once you have done these two things, your test using Set<PointType> setB = new HashSet<>() will pass.

You always need to override equals() together hashCode(). This requirement is reflected in the documentation:
API Note:
It is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for
the hashCode method, which states that equal objects must have equal
hash codes.
You can find more information on how to implement equals/hashCode contract properly on SO, and in other sources, for instance, there's a separate item dedicated to this topic in "Effective Java" by Joshua Bloch.
That how the correct implementation might look like (for conciseness I've used Java 16 Pattern matching for instanceof):
public class PointType {
private double x;
private double y;
public PointType(double x, double y) {
this.x = x;
this.y = y;
}
#Override
public boolean equals(Object other) {
return other instanceof PointType o
&& Double.compare(x, o.x) == 0
&& Double.compare(y, o.y) == 0;
}
#Override
public int hashCode() {
return Objects.hash(x, y);
}
}

Related

How to correctly implement equals(), hashCode() if the values are within a range of each other?

The criteria is that equals() method where the objects are considered equal if the value of the double variable is within +/- 10 of the other object's value of the double variable.
I'm not sure how to correctly implement hashCode() so that the hashCode would be equal if it satisfies the conditions of the equals() method.
I would really appreciate your input! Thanks!
public class Test
{
private double value;
private boolean isEqualValues (final double valueOne, final double valueTwo)
{
if(valueOne == valueTwo)
{
return true;
}
else if((valueOne - valueTwo <= 10) &&
(valueOne - valueTwo >= -10))
{
return true;
}
else
{
return false;
}
#Override
public boolean equals(final Object o)
{
if (this == o)
{
return true;
}
if (o == null || getClass() != o.getClass())
{
return false;
}
Test test = (Test) o;
if(isEqualValues(test.value, value))
{
return true;
}
else
{
return false;
}
}
//How to implement hashCode()
#Override
public int hashCode()
{
//unsure how to correctly implement hashCode() so that the hashCode would be equal if it
//satisfies the conditions of the equals() method above
}
}
There's no way to consistently implement this, since equals() demands transitivity:
It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
new Test(1), new Test(9) and new Test(14) would fail that test (assuming a trivial one-argument constructor that assigns its argument to value).
One way to work around that is to not check for the absolute distance, but "categorize" your objects using some formula, for example take the floor of value / 10 and compare that.
This way some "close" values like new Test(9) and new Test(11) would compare as not-equal, but other than that you'd get a similar result to what you described.
private long getEquivalenceGroup() {
return Math.floorDiv((long) value, 10);
}
#Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Test test = (Test) o;
return test.getEquivalenceGroup() == this.getEquivalenceGroup();
}
#Override
public int hashCode()
{
return Long.hashCode(getEquivalenceGroup());
}
As long as getEquivalenceGroup() is implemented in a stable manner this will produce "groups" of slightly different objects that still compare as equal and has a valid hashCode() implementation.
Note: if you want a comparison as described in the question but you don't necessarily need it to be returned by equals() then adding a boolean isClose(Test other) is perfectly fine. The only problem is you are trying to implement the equals method specifically with that semantic.
You can't and you shouldn't.
You should implement a comparator and do such operations using that.

How to compare class objects? [duplicate]

This question already has answers here:
What is the difference between == and equals() in Java?
(26 answers)
Closed 1 year ago.
I want to compare two objects. However, it tells me they do not match when I run it, even though they do. Please let me know what I am doing wrong, thank you. Here is my code:
Player p1 = new Player("Mo", "Food", 200.0,0.0);
Player p2 = new Player("Mo", "Food", 200.0,0.0);
System.out.println(p1.equals(p2)); // -- false
Your equals() is calling the default Object class's method, which compares the Objects's identity*, that is, p1==p2, not its contents. This is the default equals from Object:
public boolean equals(Object obj) {
return (this == obj);
}
If you want to define your logic in order to decide if two Players are equal, you need to override:
equals() method
hashCode() method to honor the equals-hash contract
class Player {
private String firstName;
private String lastName;
private double score;
private double rating;
// ...
#Override
public int hashCode() {
return Objects.hash(firstName, lastName, score, rating);
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
Player other = (Player) obj;
return Objects.equals(firstName, other.firstName) && Objects.equals(lastName, other.lastName)
&& score == other.score && rating == other.rating;
}
// ...
}
Java SE 16
Java 16 Records feature helps you get rid of all this boilerplate code. Most of the ceremonial code mentioned above is automatically available to you with just the following declaration:
record Point(String firstName, String lastName, double score, double rating) { }
If you want to work with Collections:
Implement Comparable -- this won't let you directly compare, but useful for collections
Implement Comparator
If you want to work with Collections, in order to be able to sort, for example, you could implement the Comparable interface. This avoids breaking the equals and hashcode contract, which is pretty difficult to achieve manually.
As Holger comments (he's giving me some lessons today) use this when there's a numeric based ordering involved.
If not, you can use the Comparator interface.
In this example I'm using Comparable:
class Player implements Comparable<Player>
{
#Override
public int compareTo(Player p2)
{
/* Allows returning : (0)-> this==p2 | (1)-> this>p2 | (-1)-> this<p2
Anyway, the meaning of 1 and -1 is up to you.
if (this.name.equals(p2.name) && ...)
return 0;
else if ...
return 1;
return -1; */
}
}
* Thanks to Henry Twist for pointing this out.
with the new record class type this is finally addressed.
record Player(String name, String item, double val1, double val2){}
Player p1 = new Player("Mo", "Food", 200.0, 0.0);
Player p2 = new Player("Mo", "Food", 200.0, 0.0);
System.out.println("equals:"+p1.equals(p2));
System.out.println("==:"+(p1==p2));
will print
equals:true
==:false

How do you make the expected object in an assertEquals method a pose (in a function, see code below) instead of an integer or double?

I am trying to code a Unit Test for a method translatePose (code below).
public TrcPose2D translatePose(double xOffset, double yOffset)
{
final String funcName = "translatePose";
TrcPose2D newPose = clone();
double angleRadians = Math.toRadians(newPose.angle);
double cosAngle = Math.cos(angleRadians);
double sinAngle = Math.sin(angleRadians);
newPose.x += xOffset*cosAngle + yOffset*sinAngle;
newPose.y += -xOffset*sinAngle + yOffset*cosAngle;
if (debugEnabled)
{
dbgTrace.traceInfo(funcName, "xOffset=%.1f, yOffset=%.1f, Pose:%s, newPose:%s",
xOffset, yOffset, this, newPose);
}
return newPose;
} //translatePose
I wrote out a quick template, which looks like:
public class TrcPose2DTest {
#Test
public void testTranslatePose() {
assertEquals(0.0, new TrcPose2D(0,0));
assertEquals(4.46, new TrcPose2D(4,2).translatePose(4,2).x, 1e-9);
assertEquals(3.58, new TrcPose2D(4,2).translatePose(4,2).y, 1e-9);
The problem is that assertEquals is trying to compare a double to a pose generated by the method translatePose. However, I am unsure of how to make the expected value a pose, like the way the actual value is. I thought about making a variable called TrcPose2DResult and setting that equal to a pose, but I am not sure exactly of how to implement this.
It seems the problem is your first assertion:
assertEquals(0.0, new TrcPose2D(0,0));
As you are comparing the double 0.0 to a TrcPose2D object in that assert.
I would check the x and y values of that TrcPose2D object instead as you do in the other two assertions which are also a double value:
assertEquals(0.0, new TrcPose2D(0,0).x, 1e-9);
assertEquals(0.0, new TrcPose2D(0,0).y, 1e-9);
And leave the other two assertions as they are to test the translatePose method.
After seeing the values in assertEquals, I'm assuming that the translatePose(4,2) returns a TrcPose2D with x = 4.46 and y = 3.58. This is equivalent with creating the object like this: new TrcPose2D(4.46,3.58) . Now to test the actual objects, you have first to override the equals() method in TrcPose2D for the x and y attributes:
public class TrcPose2D {
public double x;
public double y;
//constructor
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TrcPose2D trcPose2D = (TrcPose2D) o;
return Double.compare(trcPose2D.x, x) == 0 &&
Double.compare(trcPose2D.y, y) == 0;
}
}
Now that the equals method have been overridden, we can write our test like this:
public void testTranslatePose() {
TrcPose2D expectedResult = new TrcPose2D(4.46, 3.58);
TrcPose2D poseBeforeTranslate = new TrcPose2D(4, 2);
TrcPose2D actualResult = poseBeforeTranslate.translatePose(4, 2);
assertEquals(expectedResult, actualResult);
}
EDIT : we needed to override the equals() method because the assertEquals() behind the scenes is doing something like this :
if (expectedResult == actualResult)
return true;
return false;
And when we compare two objects with == the .equals(Object o) method is invoked. And in our case, to correctly compare our 2 poses, we needed to check if the current x value is equal to o.x value and same for y.

Why does my equals method not recognize object variables?

I am simply trying to write an equals method that compares students names and sections. If the names and sections are the same, then the equals method should print true. Otherwise it should print false.
Below is what I have so far.
public class Student {
private String name;
private int section;
public Student(String name, int section) {
this.name = name;
this.section = section;
}
public boolean equals(Object y) {
if (this.name.equals(y.name) && this.section.equals(y.section)) {
return true;
}
else {
return false;
}
}
}
The error is with y.name and y.section. Eclipse tells me name and section cannot be resolved to a field.
My question is, can anybody show me how to fix my code so that I can compare student names and sections using the .equals() method?
#Override // you should add that annotation
public boolean equals(Object y) {
Your y is any Object, not necessarily a Student.
You need to have code like
if (y == this) return true;
if (y == null) return false;
if (y instanceof Student){
Student s = (Student) y;
// now you can access s.name and friends
Hmm.. I'm not sure, but I think Eclipse should this function too - 'add standard equals method' - use it and then your IDE generate absolutely right equals method... But it is about coding speed optimization. Now let's tell about equals method. Normally equals method contract defines transitiveness on itself... So if a equal to b then b equal to a. In this case it is recommended to have strict restrictions:
public boolean equals(Object x) {
if (x == this) {
return true; // here we just fast go-out on same object
}
if (x == null || ~x.getClass().equals(this.getClass())) {
return false; // in some cases here check `instanceof`
// but as I marked above - we should have
// much strict restriction
// in other ways we can fail on transitiveness
// with sub classes
}
Student student = (Student)y;
return Objects.equals(name, student.name)
&& Objects.equals(section, student.section);
//please note Objects - is new (java 8 API)
//in order of old API usage you should check fields equality manaully.
}
You are missing to type cast Object to Student class;
Student std = (Student)y;

getting an object from an arrayList with objects attribute

I have 2 classes.
public class klass1 {
String bir;
String myID;
klass1(String bir, String myID)
{
this.bir=bir;
this.myID=myID;
}
}
.
import java.util.*;
public class dd {
public static void main(String[] args) {
ArrayList<Object> ar=new ArrayList();
ar.add(new klass1("wer","32"));
ar.add(new klass1("das","23"));
ar.add(new klass1("vz","45"));
ar.add(new klass1("yte","12"));
ar.add(new klass1("rwwer","43"));
ar.remove(new klass1("vz","45"));//it's not worked!!!
System.out.println(ar.size());
}
}
What I want is removing or getting an object from array list with object's second attribute. How can I do that? Is there an easy way for it?
Just implement the equals method in the class Klass1.
public class Klass1 {
String bir;
String myID;
Klass1(String bir, String myID)
{
this.bir=bir;
this.myID=myID;
}
public boolean equals(Object o){
if(o instanceof Klass1)
return ((Klass1)o).myID.equals(myID);
else
return false;
}
}
Its because you are trying to delete a new object which isnt in the arraylist. When you use new klass1("vz","45") you are creating a new instance of this class which isnt in the arraylist.
What the system does internally is to compare those classes using equals. Why this doesn't work is explained in the following code:
Object o1 = new Object();
Object o2 = new Object();
System.out.println(o1 == o2); // false, obviously
System.out.println(o1.equals(o2)); // false
System.out.println(o1); // java.lang.Object#17046822
System.out.println(o2); // java.lang.Object#22509bfc
You can tell by the number following the # that these objects have a different hash values, and this is what the equals function of Object does check.
This is relevant for your klass, because unless you overwrite equals, you will use the equals of Object. And if you implement equals you should always implement hashcode as well. Because both tell you something about whether or not two objects are the "same", and if the one says something else than the other, some part of your code might get confused.
How to properly implement equals for your class:
#Override
public int hashCode() {
int hash = 7;
hash = 17 * hash + Objects.hashCode(this.bir);
hash = 17 * hash + Objects.hashCode(this.myID);
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final klass1 other = (klass1) obj;
if (!Objects.equals(this.bir, other.bir)) {
return false;
}
if (!Objects.equals(this.myID, other.myID)) {
return false;
}
return true;
}
This can be done in most IDEs btw with a shortcut (i.E. alt-insert in Netbeans). Note that I did this in Java 7 using Objects. If you are in Java 6, you need to manually type(a == b) || (a != null && a.equals(b)); with the appropriate objects to compare.
Creating a proper hashcode is not always trivial, for more complex objects you might want to read a bit about hashcodes first. For simple objects: multiply primes with something.
The equals method is usually trivial, it is just important to first check for null and for class equality. This is often forgotten by programmers and a common source for NullPointerExceptions and ClassCastExceptions.

Categories