Unique Objects into list in java - java

I want to add unique objects into list on the basis of property of object.
class Employee {
protected long employeeId;
protected String firstName;
protected String lastName;
Employee(long employeeId,String firstName,String lastName){
this.employeeId = employeeId;
this.firstName = firstName;
this.lastName = lastName;
}
}
I want to insert object into list on the basis of unique 'employeeId' and 'firstName' , combination should be unique while inserting object into list.

If you want certain containers to work that are able to prevent duplicates of your Employee from being added you need to define how equals is calculated in Employee.
public class Employee {
//...
#Override
public boolean equals(Object obj) {
if (!(obj instanceof Person))
return false;
if (obj == this)
return true;
Person rhs = (Person) obj;
return new EqualsBuilder()
// if deriving: .appendSuper(super.equals(obj))
.append(employeeId, rhs.employeeId)
.append(firstName, rhs.firstName)
.append(lastName, rhs.lastName)
.isEquals();
}
}
If you want certain containers to work that are able to look up your Employee quickly you need to define how hashcode is calculated in Employee.
public class Employee {
//...
#Override
public int hashCode() {
int hash = 1;
hash = hash * 17 + employeeId;
hash = hash * 31 + firstName.hashCode();
hash = hash * 13 + lastName.hashCode();
return hash;
}
}
Most modern IDE's will offer a re-factoring to do this for you. Use the same fields in both so they change together.
Since you don't want duplicates you're best bet is to not use ArrayList but something that pays attention to the hash and will enforce uniqueness for you. Whenever I'm picking a container in java I look at this:
To have all of those available to you, you need to implement not only hashcode and equals but comparator as well.
Just to be clear, the point of the hash is to provide lookup speed. The point of equals is to define what objects are considered identical. The point of comparator is to provide an ordering for the objects.
If you want to include the less frequently used containers try this:

This can be achieved by overwriting the methods equals() and hashCode() - then you can turn to existing java.util collection classes (like HashSet) that use these methods when comparing objects.

Here, equals() & hashCode() methods are overridden to meet the given requirements.
class Employee {
protected long employeeId;
protected String firstName;
protected String lastName;
Employee(long employeeId,String firstName,String lastName){
this.employeeId = employeeId;
this.firstName = firstName;
this.lastName = lastName;
}
public boolean equals(Object o){
if(o == null) return false;
if(!(o instanceof) Employee) return false;
Employee other = (Employee) o;
if(this.employeeId != other.employeeId) return false;
if(! this.firstName.equals(other.firstName)) return false;
return true;
}
public int hashCode(){
return (int) employeeId * firstName.hashCode();
}
}
Here, if two Employee objects are equal, they will also have the same hash code. But, even still two Employee objects can be not equal having the same hash code.
Ex: The hash code is the employeeId is rounded down to an int. That means that many employee id's could result in the same hash code, but these Employee objects would still not be equal, since they don't have the same employee id.

Related

How to update on object at a certain position in an ArrayList?

I have one ArrayList of 10 Objects.
List<Person> persons = new ArrayList<>();
persons.add(new Person("Jean", "Pol", "receptionist", 29));
persons.add(new Person("Jef", "Snijders", "guardener", 42));
persons.add(new Person("Piet", "Peters", "truck driver", 54));
persons.add(new Person("Peet", "Polders", "professor", 63));
persons.add(new Person("Nell", "Van Den Walle", "student", 19));
persons.add(new Person("Nady", "Van Toren", "cleaning", 27));
persons.add(new Person("Jenny", "De Koster", "police", 39));
persons.add(new Person("Malena", "Zetterman", "air traffic controler", 45));
persons.add(new Person("Medina", "Zegers", "test engineer", 25));
persons.add(new Person("Horaire", "Safrina", "manager", 39));
Another method in the applications creates an changed object:
Person changed = new Person("Jean", "Pol", "officer", 30);
How do I find this object in the list, and update the list?
(this should actually update the first object in the list)
This is the Person bean:
public class Person {
private String firstName;
private String lastName;
private String jobTitle;
private int age;
public Person() {
}
public Person(String firstName, String lastName, String jobTitle, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.jobTitle = jobTitle;
this.age = age;
}
// Getters and setters
...
}
Changing an element in the List can be done by :
persons.set(0,changed);
However, finding the index of the element to change requires iterating over the entire List. You should consider using a HashMap instead (where the key identifies the Person).
Then
persons.put (personID, changed);
will replace the Person whose ID is personID (you'll need to have some unique property in the Person class, and override hashCode and equals in order to make that work).
You should tell java about "On what basis should i consider two person equals"? For that you have to override equals() method and do an equality check based on firstName and lastName. However since it is a list, so you will have to iterate over it and use contains(Person p) method to check if it exists (Which will use equals method). Once you find a matching Person object, you can replace it (using the index).
first decide which property or properties can be used to verify if 2 objects are equal then override equals and hashcode for persons and then iterate and compare then replace when found.
the best approach to find the object is to override two methods equals(Object o) and hashCode() within your Person class, this will define a unique Person
your Person class:
class Person {
...
#Override
public boolean equals(Object o){
// your code here
}
#Override
public int hashCode(){
//your code here
}
}
if ordering is not important to you , you can use HashMap
for example :
Person p;//
myMap.put(p.hashCode(),p);
then from other part of your application you get your person;
Person otherP;//
myMap.put(otherP.hashCode(), otherP) // this will replaces the previous person with the same hashCode with the modified code
if you don't want to add a person that is not already there, you can check if it exists first
if(myMap.containsKey(otherP.hashCode()){
myMap.put(otherP.hashCode(), otherP);//
}
generate hashcode and equals methods in your bean class for the variable you want to check for similarity (in your case the firstName property in person class).if using eclipse then pressing ctrl+shift+s shows you the option to generate hashcode and equals methods.
what this does is to override the default equals method. Please make sure that you are selecting only the properties which you know will be same.
if (List.contains(new_object)) { // checking if object is in the list
int indexPos = List.indexOf(new_object); // getting the index if there
dbStockObjList.set(indexPos, new_object);//adding it in the index
}
the hashcode and equals methods for your bean would look like this:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (firstName == null) {
if (other.firstName != null)
return false;
} else if (!firstName.equals(other.firstName))
return false;
return true;
}
hope this answers your question properly.
You can use the List#indexOf(obj) method to get the index of the "identical" object, and update it using List#set(index, object).
Note that it will require you to override equals() method for your object.
Also note: this operation is inefficient, and if the list is large and the number of changes is frequent - consider switching from a list to a "smarter" data type, such as a Set.

Why Hibernate requires us to implement equals/hashcode methods when I have a private id field?

First, consider the snippet,
public class Employee
{
private Integer id;
private String firstname;
private String lastName;
private String department;
// public getters and setters here, i said PUBLIC
}
I create 2 objects with same ids and rest of all the fields are also same.
Employee e1 = new Employee();
Employee e2 = new Employee();
e1.setId(100);
e2.setId(100);
//Prints false in console
System.out.println(e1.equals(e2));
The whole problem starts here
In a real time application, this must return true.
Consequently, everyone knows a solution exists (to implement equals() and hashcode())
public boolean equals(Object o) {
if(o == null)
{
return false;
}
if (o == this)
{
return true;
}
if (getClass() != o.getClass())
{
return false;
}
Employee e = (Employee) o;
return (this.getId() == e.getId());
}
#Override
public int hashCode()
{
final int PRIME = 31;
int result = 1;
result = PRIME * result + getId();
return result;
}
Now, as usual:
Employee e1 = new Employee();
Employee e2 = new Employee();
e1.setId(100);
e2.setId(100);
//Prints 'true' now
System.out.println(e1.equals(e2));
Set<Employee> employees = new HashSet<Employee>();
employees.add(e1);
employees.add(e2);
//Prints ofcourse one objects(which was a requirement)
System.out.println(employees);
I am going through this excellent article Don't Let Hibernate Steal Your Identity. But one thing I have failed to understand completely. The whole problem and its solution discussed above and the linked article were dealing the problems when the 2 Employee object ids were same.
Consider when we have a private setter for id field with the id field generated by the generator class provided in hbm.xml. As soon as i start to persist the Employee objects(and in no way i would be able to change the id), i find no need to implement equals and hashcode methods. I am sure i am missing something, since my intuition says when a particular concept is too much rotated over the web, it must have always been laid in front of you for the sake of avoiding some common errors ? Do i still have to implement those 2 methods when i have a private setter for id field?
If the entity defines a natural business key, then you should use that for equals and hashCode. The natural identifier or business key is consistent across all entity state transitions, hence the hashCode will not change when the JPA entity state changes (e.g. from New to Managed to Detached).
In your example, you are using the assigned identifier, which doesn't change when you persist your entity.
However, if you don't have a natural identifier and you have a generated PRIMARY KEY (e.g., IDENTITY, SEQUENCE), then you can implement equals and hashCode like this:
#Entity
public class Book implements Identifiable<Long> {
#Id
#GeneratedValue
private Long id;
private String title;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Book))
return false;
Book other = (Book) o;
return id != null &&
id.equals(other.getId());
}
#Override
public int hashCode() {
return getClass().hashCode();
}
//Getters and setters omitted for brevity
}
The entity identifier can be used for equals and hashCode, but only if the hashCode returns the same value all the time. This might sound like a terrible thing to do since it defeats the purpose of using multiple buckets in a HashSet or HashMap.
However, for performance reasons, you should always limit the number of entities that are stored in a collection. You should never fetch thousands of entities in a #OneToMany Set because the performance penalty on the database side is multiple orders of magnitude higher than using a single hashed bucket.
The reason why this version of equals and hashCode works is that the hashCode value does not change from one entity state to another, and the identifier is checked only when it's not null.

How to create a set of deep objects

I have a collection of objects, A.
class A{
String name;
Collection<B> listOfB;
}
class B {
String address;
String phone;
int age;
}
I want to create new collection of A objects, where 2 objects have the same name, address, and phone. Can anyone tell me if this is the best way to do this?
I create a map of Key-A. The key would be as follows:
Key {
String name;
String address;
String phone;
}
I only as A objects to the list if their corresponding Key is not present.
If I understand your question correctly you want a map Map<Key, A>. The important thing is that you define equality and the hash code (in case you want a hash map) for the Key:
class Key {
String name;
String address;
String phone;
#Override // override in Object
public boolean equals(Object other) {
if(!other instanceof Key) return false;
Key otherKey = (Key) other;
return name.equals(otherKey.name) && address.equals(otherKey.address) && phone.equals(otherKey.phone); // check for null if fields can be null
}
#Override // override in Object
public int hashCode() {
return name.hashCode() ^ address.hashCode() ^ phone.hashCode(); // or something along those lines
}
}
Also good idea to create a constructor for the Key and make the fields private and final.
I'm not sure how this key is derived though. Ideally, the Key would somehow be derived from A, or - even better - A would have a hashCode and equals method so you do not need a map but you can use a Set. This really depends on the data you want to model though and your question is not clear enough to give a specific recommendation.
First, implement hascode and equals method in class B.
In equals method return true when name,phone and adress are the same.
Second time, create your map like this=
HashMap<B,A> myMap;
A key in a map is always unique.

removig items from arraylist

In an ArrayList, I have the same kind of objects. Each object has an id, name, and number as their fields. There is a chance that more than one object will have the same phone number. How can I make the ArrayList in such a way that all the ArrayList objects have distinct phone numbers?
override in your class methods equals() and hashCode(). In equals you will compare by phone number. Generate hashcode from your phone number too.
Now you are ready to use Set interface which will compare your objects by phone numbers automatically and exclude duplicates.
example below:
public class Test {
private int id;
private String name;
private String phoneNumber;
public Test(int id, String name, String phoneNumber) {
this.id = id;
this.name = name;
this.phoneNumber = phoneNumber;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Test test = (Test) o;
return phoneNumber.equals(test.phoneNumber);
}
#Override
public int hashCode() {
return phoneNumber.hashCode();
}
}
now instead of using List<Test> arr = ArrayList<Test>(), use Set<Test> mySet = new HashSet<Test>().
Try Set.
A collection that contains no duplicate elements.
First solution that comes to my mind is to use a HashMap.
Simply create a HashMap with the 'phonenumber' as key and your object as the value. After adding all the elements, you will have the list of objects with unique phone numbers. Simply iterate over this to create the List that you need.

Problem with equals() method in Hibernate

I am developing an application in Hibernate where I have model classes like these:
public class Employee
{
private int ID;
private String name;
private Department department;
//other properties
//constructors, getters and setters
}
Note that the ID is not a value populated by the user and is populated using GenerationType.Identity as the strategy.
Also I have another class Department as follows:
public class Department
{
private int ID;
private String name;
private Set<Employee> employees; //this is actually a HashSet
//other implementations
}
There is a ManyToOne bi-directional relationship between an Employee and a Department.
So to add a new Employee to an existing Department, I do the following
Department existingDepartment = ...;
Employee newEmployee = ...;
existingDepartment.addEmployee(newEmployee);
employee.setDepartent(existinDepartment);
session.save(newEmployee);
Now conceptually two Employee objects are the same if they have the same ID. So my equals() method in the Employee class looks like this:
public boolean equals(Object o)
{
if(!(o instanceOf Employee))
{
return false;
}
Employee other = (Employee)o;
if(this.ID == o.etID())
{
return true;
}
return false;
}
Now the problem is when I create a new Employee(); I do not have its ID, since it will be assigned when it will be persisted. So when I say
existingDepartment.addEmployee(newEmployee);
the internal HashSet of the Department object is effectively using an equals() method which is broken [since it uses a member variable to determine equality that as not been initialized properly].
This seems like a very basic problem but, how do I solve it? Or am I designing my classes totally wrong? Or should my equals method be re-written to compare other values instead of ID, which I guess would be absurd.
This seems like a very basic problem but, how do I solve it? Or am I
designing my classes totally wrong? Or should my equals method be
re-written to compare other values instead of ID, which I guess would
be absurd.
There are two different philosophies concerning this.
a) equals() / hashCode() based on DB id
Drawback: you can't compare persistent and non-persistent objects
b) equals() / hashCode() based on contents
Drawback: two objects with the same id may turn out to be non-equal.
I prefer the second approach, it makes more sense from a Java point of view (although admittedly not from a DB point of view). The only thing I'd want to make sure is that you never mix the approaches.
This has been discussed many times before, btw:
Should I write equals() methods in JPA entities?
What is the best practice when implementing equals() for entities with generated ids
JPA : not overriding equals() and hashCode() in the entities?
(etc.)
Rewrite your equals method, so that it returns false, when o is null:
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Employee other = (Employee) obj;
if (this.id != other.id && (this.id == null || !this.id.equals(other.id))) {
return false;
}
return true;
}
In hibernate usually you can tell it to use a value when it hasn't been saved to db. For example, I have used -1 for an ID which hasn't been stored yet.
You should initialize your ids like this to make sure you get a consistent behavior.
private int ID = -1;
You may add an transient field with an different non-persistent id. (maybe you should upgrade to "long" id).
Something like this for Example
public class Employee {
private static int lastTID = 0;
private int ID = -1;
private transient int tID;
..
public Employee () {
synchronized (getClass()) {
tId = -- lastTID;
}
}
public boolean equals(Object o) {
..
Employee other = (Employee)o;
..
if (ID != -1) {
return ID == other.ID;
} else {
return other.ID == -1 && tID == other.tID;
}
}
In any case you have to assure that there are not saved and unsaved Employee in use.
Another strategy is to save Employee first and then to add it to Department

Categories

Resources