Method "contains".Java List - java

I have Spring MVC appication. I have entity class Examination. I overridden method eqauls so I could use method contains of List interface. When i try to add new exam, I look if i have already added it. But when i pass examination object to mathod contains, I always have different students. For example:
I need to add exam to Student Jone. I try to add it and get another information: Kate : Jone, instead of Jone : Jone. I do not know why i happens because i pass examination object when i set student as Jone.
#Override
public boolean equals(Object arg) {
Examination exam = (Examination) arg;
System.out.println(exam.getStudent().getStudentFullName() + ":" + this.getStudent().getStudentFullName());
if (!this.subject.getSubjectTitle().equals(exam.getSubject().getSubjectTitle()))
return false;
else
return true;
}
piece of code where i try to add exam
examination.setStudent(currentStudent); // set student
examination.setSubject(subjectExam); // set subject
if(es.selectAllExams().contains(examination)) {
return "error";
} else {
es.insertExam(examination); // add to database
return "success";
}

In the equals method you are comparing only titles, not the student name. So if you have two examinations with same title, but different student name they are equal (based on your equals method). Compare also students in the equals method and you should be good. In general it is good practice to override both equals and hashcode methods.
Your implementation of equals method is in general not following best practices for overriding equals method. Google a bit for "java equals method best practices" - you'll find something like this : http://javarevisited.blogspot.sk/2011/02/how-to-write-equals-method-in-java.html
If you are lazy to write your own equals or hashcode methods (or you have other reasons) you can use :
http://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/builder/EqualsBuilder.html
or
http://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/builder/HashCodeBuilder.html

You say you override equals()
so I could use method contains of List interface
However, you don't have to override that method to use contains(). There's a default implementation that's suitable for most purposes; it boils down to "are these objects the same instance?". As a previous responder pointed out, you're breaking this logic with your implementation; all examinations with the same title will be considered the same object, so as long as your list has one examination with the same title as the one you're trying to add, the contains() check will always return true, and you'll never be able to add another one.
If you do want equality to be based on the title and student in question, then the previous answer is correct - you'll want to override both hashCode() and equals(), making sure you consider all fields important to an examination's identity in both methods.

Related

Object equality vs uniqueness

In my hobby Kotlin project I've run into dilema how to implement equals() method in case of a class like:
// I'm using Kotlin-like syntax
class ConfigurationParameter {
val name: String // used for command line option, *.conf file parameter, ENV variable, ...
val allowedValues: Set<String> // the valid values of the configuration parameter
val description: String // used in --help, as a comment above parameter in *.conf file, ...
}
Equality
Now, from my POV, two objects of this class are equal only if they are equal in all their properties. Otherwise they would beheave differently:
In case of name ... that's completely other parameter.
In case of allowedValues ... the validation would differ.
In case of description ... the printed usage help would differ.
Uniqueness
At the same time I don't want two objects with just the same name (but possibly with distinct allowedValues or description) to appear in one set (Set<ConfigurationParameter>).
That would lead to problems like duplicate command line options and the like.
This should not happen
I'm aware of there should not be created two configuration parameters with the same name and distinct other properties in the application in the first place. But let's consider this to be some internal self-check mechanism.
Solution
The only solution I've come at yet is to create a brand new ConfigurationParameterSet (not based on Set) that treats the "sameness" of its items by their name and not by their equals() method.
The problem with this solution is that there must be such a new Set class for every entity class that has equality distinct from its uniqueness.
Question
Is there any well-established generic solution to this equality vs uniqueness dilema?
Instead of your custom set-like class, you can use a Map that uses the name property as the keys. You could also add extension functions so you can use it kind of like a Set. In Java, you'd have to extend the class to add these.
fun MutableMap<String, ConfigurationParameter>.add(parameter: ConfigurationParameter) =
put(parameter.name, parameter)
fun MutableMap<String, ConfigurationParameter>.remove(parameter: ConfigurationParameter) =
remove(parameter.name, parameter)
operator fun Map<String, ConfigurationParameter>.contains(parameter: ConfigurationParameter) =
containsValue(parameter)
If you have lots of classes like this where you want to store them by a name property, you could make an interface with a name property that they can all use and then create the above extension function for any map that uses values that implement the interface:
interface NamedItem { val name: String }
class ConfigurationParameter: NamedItem {
override val name: String,
val allowedValues: Set<String>,
val description: String
}
fun <T: NamedItem> MutableMap<String, T>.add(parameter: T) =
put(parameter.name, parameter)
fun <T: NamedItem> MutableMap<String, T>.remove(parameter: T) =
remove(parameter.name, parameter)
operator fun <T: NamedItem> Map<String, T>.contains(parameter: T) =
containsValue(parameter)
I'm not well versed in Kotlin, but this problem sounds exactly the same in Java. In Java you have two types of equality: (1) reference equality (a == b) where a and b are both references to the same object and (2) hashCode/equals equality. I suspect when you are talking about "uniqueness" that you don't mean reference equality but rather a notion of hash/equals equality where all fields are the same.
What you have isn't a language problem. It's a design problem. You need to decide what makes two objects equal OR take another approach.
So, one way to do this would be to define a method like:
enum Similarity { FULL, NAME }
boolean same(Object object, Similarity similarity)
Then you can call same() from equals() to give the default kind of similarity. You can also imagine making the object sort of modal, where it has a similarity state and the equals method uses that state to decide which kind of similarity to use. The downside of this state is (1) the concern of similarity/equality isn't necessarily best defined by methods in the class itself (separation of concerns) and (2) mutable state is not the best if you can avoid it.
Another, possibly better, approach might be to create two Comparator implementations, where one comparator uses just the name and the other uses all values. This is a very common approach in Java and should be just as easy in Kotlin. Comparators give sort order, but a return value of 0 indicates equality. If you prefer a boolean, you could use the same technique but create an interface like:
interface SimilarityComparator
{
boolean same(Object a, Object b)
}
BTW, if you implement the comparator as a nested class, you can increase encapsulation by obviating the need to expose property values or fields to allow comparison (property getters and setters are bad, see Alan Holub).
https://www.baeldung.com/java-comparator-comparable
Hopefully this helps.
Jon

Specific compare/equals for List.contains(obj)

Hi#all and thx in advance,
i currently wondering if there is a great best practice for my situation.
Consider u have an object. Let's say an object of type Customer.
Within a Customer object u have (n-1) fields.
Now u are implementing the hashCode and equals method via eclipse context menu >> Sources >> Generate hashCode() and equals()...
It uses all (n-1) fields to do so. Which is actually not really a bad in the general case.
Additional in a seperate service class within a specific method u have a list of Customers >> List myCustomers.
In this method u implement an if-else case with the condition myCustomers.contains(specificCustomer).
if (myCustomer.contains(specificCustomer)) {
// todo
} else {
// todo
}
So the list.contains(obj) uses internally the equals method of the object. This is the general way to check if a list contains an object.
BUT
In my case i've the standard hashCode and equals allready implemented with all (n-1) fields. And what i want, is to use a/the .contains(obj) method, which checks only specific fields of the object, for example (n-5) fields.
I don't want to adapt the existing hashCode and equals, because other parts of the code allready using this. And if i do so, i can not predict the behavior afterwards.
Also i don't want to write a seperate method within the Customer class and use it only in the if-else-condition.
So, does anyone has an idea, best practice or great pattern to get this done?
You want a specific piece of business logic (the n-5 field equals) to be used so you will have to wrte a specific piece of code somewhere. The simplest which comes to mind is to replace the standard contains call with a custom method which iterates over the list and checks if there is an item which matches using the custom equals logic. If you need this version of equals in more places you could extract it into a separate utility or service class.

Using own implementation of HashSet.add()

I am using a HashSet to add unique Users to the hashset. These users are updated frequently from an online source and when it reads it in from there it gets a new object each time so all the Users get added again.
I have a UserID variable in my user class and would like to use this to add users to the HashSet and ensure uniqueness.
private HashSet<User> userHashSet = new HashSet<User>();
userHashSet.add(user);
//based on the users id it will either add or not
I want to do this without losing efficiency and preferably only one HashSet
You should implement the equals and hashCode method in your User class.
As an example:
#Override
public int hashCode() {
return getUserId().hashCode();
}
#Override
public boolean equals(Object obj) {
return obj instanceof User &&
getUserId().equals(((User) obj).getUserId());
}
You need to implement the hashCode and equals methods in your User class. So long as you implement those two methods correctly then you will get unique entries in your HashSet.
There are many articles about how to implement these correctly (e.g. What issues should be considered when overriding equals and hashCode in Java?), but the easiest way to do it is to get your IDE to auto-generate those methods, or use the Apache Commons helpers.

isEmpty issue while comparing java object

Currenty I have this NULL object for Employee Details to pass if employee details object created is empty
public static final EmployeeDetails NULL = new EmployeeDetails();
But I want to remove this now and use my regular EmployeeDetails object.
I was checking EmployeeDetails obj is empty or not by doing this if(!EmployeeDetails.NULL.equals(empDetails))
but now I dont have that object so I won't be able to do that way. I tried this way but got error saying isEmpty is not defined.
if(!empDetails.isEmpty())
Can someone tell me what I am suppose to do with this.
Thanks
isEmpty() is not defined because you did not define it. This funciton is not included in the handful of methods you get directly from Object and, in any case, an empty condition for your own objects should be defined by you since only you know the internal structure.
Of course, it all depends on what do you need because one person can take an isEmpty() method as valid by doing a simple null check while other person can make a field-by-field check.
In your case, just define an isEmpty method in your class. For example:
public boolean isEmpty() {
//your condition here, for example, I take an EmployeeDetails object
//as empty if it has no employee associated (assuming you can associate
//an employee to it).
return employee == null;
}
Remember to define WHEN do you consider this object to be empty and code the method with that in mind.
Add a method public boolean isEmpty() { // Implementation } ... here add your logic to find out the empty object, for example your previous code was checking the default value for all the properties to find out empty

Can I use only the key field to check equals? And can I use equals through subclasses?

I have a class Player with some fields:
String name;
Point position;
Action action;
The 'name' field is sort of a key, there cannot be 2 players with the same name. So it seems to me they are equals if the 'name' (probably case ignored) is the same. So do I use the String.equalsIgnoreCase(String) only or do I check the other fields as well?
1) Should I check more than the name field in the equals method of Player?
2) Should I throw an error in the equals method if the other fields are not the same?
3) And is it wise to check that one field only for subclasses, because even in subclasses the same name, indicates the same player, or should I choose another method for comparing this? Example:
class MovingPlayer extends Player{
Point destination;
So the 'name' field is still the key (something like an inter-subclass key).
Thanks in advance, Tjen
You should ask yourself the question -- is it possible for two objects with the same name property and two different position properties to exist in your application? If that is true, then you should implement the equals method to use all relevant fields.
You dont throw an error in the equals method. You return true or false.
Your subclasses can override the equals method. In that overridden method, you can check for superclas equals and only if they are equal, you continue with additional checking.

Categories

Resources