how to get a specific java object from java.util.List<Object> using equals method
exemple :
Class Person{
String name;
int age;
//...
#Override
public boolean equals(String str) {
return (this.name.equals(str.name));
}
}
main{
List<Person> list = personDao.findAll();
if(list.contain("joe")){
// how to get the Person named joe ????
}
}
If you want to get a reference to the specific Person in the List:
Person foo = null;
for (Person p : list) {
if (p.getName().equals("joe")) {
foo = p;
break;
}
}
// now foo is your person (if he exists) or null (if he doesnt exist);
This will only find the first Person named joe.
The signature of your Person.equals() method is incorrect... argument type should always be "Object", like this:
#Override
public boolean equals(Object obj) {
return (obj instanceof Person) && ((Person) obj).name.equals(this.name);
}
Then jlordoless's suggestion of using list.contains(new Person("name") will work.
Simply iterate. There's no method on the List interface that returns an object from the list.
Person joe = null;
List<Person> persons = personDao.findAll();
for (Person thisPerson : persons) {
if ("joe".equals(thisPerson.getName())) {
joe = thisPerson;
break;
}
}
This will set Person joe to the first person named joe. If you're looking for the last one in the collection, remove the break statement.
I think that you have a design flaw here. What happens if there are 2 joe-s?
If you don't have duplicate names you can create a getter for the name in Person:
public String getName() {
return name;
}
and after that you can do this:
Person joe = null;
for(Person person : list) {
if("joe".equals(person.getName()) {
joe = person;
break;
}
}
This way you avoid creating unnecessary instances of Person.
You should not use equals like that. Overriding equals is somewhat complex, you have to verride hashcode too, for example. A list-based solution with minimal changes:
Class Person{
String name;
int age;
//...
public String getName() {
return name;
}
}
main {
List<Person> list = personDao.findAll();
Person foundPerson = null;
for(Person person : list) {
if (person.getName().equals("joe")) {
foundPerson = person;
break;
}
}
if (foundPerson != null) {
// do something
}
}
However, I would consider using a Map<String, Person>, with name as key. Overriding equals in Person while ignoring age does not sound very good, really.
#Hyde: Can not answer on your answer. Overriding "hashCode" is required if and only if Hashing is used. If person wants to use HashMap or Hashing related other APIs than only he must override hashCode and equals... Otherwise "equals" works fine.
Related
I am trying to generate a HashSet containing unique Employee instances. Uniqueness should be established based on the object properties.
The problem is that I end up with having duplicates.
Note that Employee class is provided by a framework, and it's not possible to provide custom implementations for equals() and hashCode().
Employee class:
public class Employee {
private long employeeId;
private String name;
// getters, setters
#Override
public String toString() {
return "Employee{" +
"employeeId=" + employeeId +
", name='" + name + '\'' +
'}';
}
}
Map<String, Set<Employee>> ackErrorMap = new HashMap<>();
Employee emp = new Employee(1,"bon");
Employee emp2 = new Employee(1,"bon");
ackErrorMap.computeIfAbsent("key1",
x -> new HashSet<>()).add(emp);
ackErrorMap.computeIfAbsent("key1",
x -> new HashSet<>()).add(emp2);
This would result in a Set mapped to the Key "key1" containing both emp and emp2, although object attributes are same.
Is there any way to discard Employee with the same name? Or would it better to use ArrayList?
Possible example using Arraylist
ackErrorMap.computeIfAbsent("key1",
x -> new ArrayList<>()).add(emp);
You can override the equals and hashCode methods in the Employee class. The equals method should return true if two objects are considered equal, and the hashCode method should return the same value for two objects that are considered equal.
class Employee {
private int id;
private String name;
public Employee(int id, String name) {
this.id = id;
this.name = name;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return id == employee.id &&
Objects.equals(name, employee.name);
}
#Override
public int hashCode() {
return Objects.hash(id, name);
}
}
With these changes, when you add emp and emp2 to the HashSet, only one of them will be added, because they will be considered equal based on the equals method.
You can create a custom type wrapping the class coming from the framework and implement the equals/hashCode contract according to your requirements.
That's how such wrapper might look like (for the purpose of conciseness I'm using a Java 16 record, but it can be implemented as a class as well).
public record EmployeeWrapper(Employee employee) {
#Override
public boolean equals(Object o) {
return o instanceof EmployeeWrapper other
&& employee.getName().equals(other.employee.getName());
}
#Override
public int hashCode() {
return Objects.hash(employee.getName());
}
}
And you can use with a Map of type Map<String,Set<EmployeeWrapper>> to ensure uniqueness of the Employee based on the name property.
I would also advise to make one step further and encapsulate the Map into a class which would expose the methods covering all scenarios of interaction with the Map (like add new entry, get employees by key, etc.), so that your client would not dial wrappers, but only with employees and wrapping and unwrapping would happen within the enclosing class.
Here's how it might be implemented:
public class AcrErrors {
private Map<String, Set<EmployeeWrapper>> ackErrorMap = new HashMap<>();
public void addEmployee(String key, Employee employee) {
EmployeeWrapper wrapper = new EmployeeWrapper(employee);
ackErrorMap
.computeIfAbsent(key, x -> new HashSet<>())
.add(wrapper);
}
public List<Employee> getEmployees(String key) {
return ackErrorMap.getOrDefault(key, Collections.emptySet()).stream()
.map(EmployeeWrapper::employee)
.toList();
}
// other methods
}
You need to override equals() and hashCode() inside Employee class.
Or you can use lombok’s #EqualsAndHashCode annotation in your Employee class.
public static void getIndividualsList() {
List<Employee> individualList = new ArrayList<>();
Employee individualDtls = new Employee();
individualDtls.setDependentId(0);
individualDtls.setDateOfBirth("06/06/1998");
individualDtls.setEeId(1L);
individualDtls.setFullName("MICHAEL K HERNANDEZ");
individualDtls.setCovered(false);
individualDtls.setDependentType("Self");
individualList.add(individualDtls);
List<Employee> individualList1 = new ArrayList<>();
Employee individualDtls1 = new Employee();
individualDtls1.setDependentId(0);
individualDtls1.setDateOfBirth("06/06/1998");
individualDtls1.setEeId(1L);
individualDtls1.setFullName("MICHAEL K HERNANDEZ");
individualDtls1.setCovered(false);
individualDtls1.setDependentType("Self");
individualList1.add(individualDtls1);
individualList.removeAll(individualList1);
for (Employee employee : individualList) {
System.out.println(employee.getDateOfBirth());
}
}
Why removeAll is not working properly?
Applied removeAll but still getting data. Can someone please let me know why its not working?
Based on your comment: "In the employee class there is restriction. So its not allowing to modify like adding any other methods or comparator. Thats the problem. Thats why trying to find another solution for that."
If you have any unique field, maybe you can use that field to iterate over your first list to remove the required items.
import java.util.*;
public class MyClass {
public static void main(String args[]) {
List<Employee> listA = new ArrayList<>();
listA.add(new Employee("gokul", "1", "gokul#domain.com"));
listA.add(new Employee("user", "2", "user#domain.com"));
List<Employee> listB = new ArrayList<>();
listB.add(new Employee("user", "2", "user#domain.com"));
for (Employee eB : listB) {
for (Employee eA : listA) {
if (eA.id.equals(eB.id)) {
listA.remove(eA);
break;
}
}
}
for (Employee e : listA) {
System.out.println(e.name);
}
}
static class Employee {
String name;
String id;
String email;
public Employee(String name, String id, String email) {
this.name = name;
this.id = id;
this.email = email;
}
}
}
Both remove and removeAll relies on equal (and hashCode!) implementation. Most probably you didn't implement one of this methods.
Without custom implementation of equal an object equals only himself. Two variables must reference exactly the same object to be equal. Custom implementation looks like this:
class Employee {
....
public boolean equals(Object obj) {
if (obj instanceof Employee) {
Employee oo = (Employee)obj;
return Objects.equal(fullName, oo.fullName) && Objects.equal(dateOfBirth, oo.dateOfBirth)
} else {
return false;
}
}
public int hashCode() {
return Objects.hashCode(fullName, dateOfBirth);
}
}
I took fullName and dateOfBirth to ensure equality. You must manually add all the fields you think important for two objects to be equal (spoiler - it is not trivial).
If you cannot modify the object Employee there are to ways:
do it manually as described by "Gokul Nath KP"
take some other collection structure instead of ArrayList. For example TreeSet uses an optionally provided custom Comparator instance instead of equal-Method.
I have Overridden equals method of Person class comparing the name attribute of class and if they are equal returning back true from equals method.
When i am creating the instance of Person object and using it as key in hashmap, while retrieving using a new object with same name i am not able to retrieve back the associated value from hashMap.
Below is my
import java.util.HashMap;
import java.util.Map;
public class ToStringTest {
public static void main(String[] args) {
Person person = new Person("Jack", "California");
Map<Person,String> personsMap = new HashMap<>();
personsMap.put(person,"MyCar");
Person otherPerson = new Person("Jack", "California");
System.out.println(personsMap.get(otherPerson));
}
}
class Person {
String name;
String city;
public Person(String name, String city) {
this.name = name;
this.city = city;
}
#Override
public String toString() {
return "Name : " + this.name + ", City : " + this.city;
}
#Override
public boolean equals(Object o) {
Person person = (Person) o;
if(person.name.equals(this.name)){
return true;
}
return false;
}
}
This is printing null while retrieving from using otherPerson object.
Could someone please explain this behavior.
When you add new person in map personsMap.put(person,"MyCar"); first of all if key is not null the position where the element will be put is determined. It is determined by calling hashcode method for key. There are a few steps after but it doesn't matter for this example.
Since you don't override hashcode() your person and otherPerson will have different hashcode.
The same happens when you try to get value by some key. To find position where the element is, hashcode() will be invoked. But otherPerson has different hashcode and it will lead to position where is no item (null)
equals() is used when at the same position there are many elements (in list or tree structure). Then to find the right item they will be compared by equals() method
If a woman has a partner, erase that partner's partner.
I need to retrieve her partner then make that partner single.
How do I combine getPartner() with erasePartner()
if (w.hasPartner() == true) {
w.getPartner().erasePartner();
}
import java.util.*;
public class Person {
public static final int NOBODY = -1;
private String name;
private List<Integer> preferences;
private List<Integer> oldPreferences;
private int partner;
public Person(String name) {
this.name = name;
preferences = new ArrayList<Integer>();
oldPreferences = new ArrayList<Integer>();
erasePartner();
}
public void erasePartner() {
partner = NOBODY;
}
public boolean hasPartner() {
return partner != NOBODY;
}
public int getPartner() {
return partner;
}
public void setPartner(int partner) {
this.partner = partner;
}
public String getName() {
return name;
}
public boolean hasChoices() {
return !preferences.isEmpty();
}
public int getFirstChoice() {
return preferences.get(0);
}
public void addChoice(int person) {
preferences.add(person);
oldPreferences.add(person);
}
public List<Integer> getChoices() {
return preferences;
}
public int getPartnerRank() {
return oldPreferences.indexOf(partner) + 1;
}
}
Right now you have partner as an int. You cannot call methods on an int since it is a primitive type. However this doesn't make much sense in your context since you are erasing w's partner and not the partner's partner. Instead just call:
if (w.hasPartner() == true) {
w.erasePartner();
}
Also your code doesn't make much sense. Why is the partner an int? Why wouldn't it be a Person object. You could change your code to:
private Person partner;
And
public Person getPartner() {
return partner;
}
So that partner points to another Person object instead of an int
You need to be able to get the Persona that the partner field corresponds to if you want to erase the partner's partner.
Given the code that you have, every Person should have a corresponding id that is an int, which is the value the partner field stores. You'll need some way to store these mappings such as a Map<Integer, Persona> that lives outside the Person class, and whenever you create a Person you have to put it in the map along with its id (which you can generate however you like as long as it's unique).
With this map you can then access the Person that corresponds to the partner int and erase its partner
if (w.hasParter()) {
Person partner = personMap.get(w.getPartner());
partner.erasePartner()
}
assuming the partner exists in the map (which you may want to check for).
Okay I kinda figured it out. I did it another way. Goes through each person and test's to see if person p is partner with firstChoice then deletes the p's partner. Kinda long way it seems but it works :)
for (Person p : list1) {
if (p.getPartner() == firstChoice) {
p.erasePartner();
}
I have two classes, Person and Employee. Employee extends Person.
I have a method that reads a Person from storage, and am writing the method that reads an Employee.
I'd like to reuse the method I have for Person to read the properties that are the same for Employee, without copy-pasting the code, but can't seem to find a way to do it.
public Person getPersonFromStorage() {
Person person = new Person();
// ... logic
return person;
}
public Employee getEmployeeFromStorage() {
Employee employee = new Employee();
// ... logic for employee-specific fields
// I want to read the fields inherited from Person here
return employee;
}
I cannot cast the retrieved Person from getPersonFromStorage, because it is not an Employee. It could be, because it's not another subtype either, but it's not.
I could do the following:
public Person getPersonFromStorage(Person person) {
if(person==null) { person = new Person(); }
// ... logic
return person;
}
public Employee getEmployeeFromStorage() {
Employee employee = (Employee) getPersonFromStorage(new Employee());
// ... logic for employee-specific fields
return employee;
}
But I'd like to avoid this complexity if I can. I have the feeling I'm overlooking something elementary. Is there a better way to solve this?
Just offering a different architecture that I usually use in this instance. If you're talking about "from storage", to me that means some sort of persistent structure. Text File, database, etc. For the following example, let's assume you have your values in a text file.
Assume a file employees.txt, which contains one employee:
// Dave's person details.
Dave
Manchester
32
// Dave's employee details
Assassin
Mostly North Korea.
Then you've got a class Person, that looks a little like this:
public class Person
{
private String name;
private String location;
private int age;
public Person(String name, String location, int age)
{
// blah blah blah.
}
}
And a class Employee that looks like this:
public class Employee extends Person
{
private String jobTitle;
private String area;
public Employee() {
// etc.
}
}
In your Person class, you can create a constructor designed to read the parameters for a Person. Something like:
public Person(Scanner file)
{
this.name = file.nextLine();
this.location = file.nextLine();
this.age = file.nextInt();
file.nextLine(); // Make sure you're pointing at the new line!
}
And in your Employee class, you can create a constructor designed to read the parameters for an employee, while calling it's superclass to deal with the other values.
public Employee(Scanner file)
{
super(file);
this.jobTitle = scanner.nextLine();
this.area = scanner.nextLine();
}
Then all you have to do to call this is something like:
Scanner s = new Scanner("employees.txt");
Person p = new Employee(s);
or to make it more compact:
Person p = new Employee(new Scanner("employees.txt"));
This will go and parse the file, and return an object, while wrapping up all the logic for actually reading the file inside the classes that need the data.
Not a text file?
Well, it's not really vital. The important thing is just passing an object up the call chain, and those methods are doing what that particular class needs to do, then passing on the object. If it's a database row, it's exactly the same principle.
Your second code example is the way to go, except you don't even need the null check line. Just pass in a non-null Person that you've instantiated elsewhere.
For even better abstraction, see if you can make Person into an abstract class.
More elegant way is to overload Employee constructor to be able to create Employee instance from parent Person instance.
public Employee getEmployeeFromStorage() {
Employee employee = new Employee(getPersonFromStorage());
// ... logic for employee-specific fields
return employee;
}
You could use a protected factory method.
protected Person createNewInstance() { return new Person(); }
and use this in your getPersonFromStorage() method. The subclass would then override this method, thus changing the return type into an Employee, which you can then use like in your second example.
public class Person {
public Person getPersonFromStorage() {
Person person = createNewInstance();
// ... logic
return person;
}
protected Person createNewInstance() {
return new Person();
}
}
public class Employee extends Person {
public Employee getEmployeeFromStorage() {
Employee employee = (Employee) getPersonFromStorage();
// ... logic for employee-specific fields
return employee;
}
protected Person createNewInstance() {
return new Employee();
}
}
Alternatively you could also create an Employee constructor based on a Person
public class Employee extends Person {
public Employee(Person person) {
super();
// copy all fields from person
}
public static Employee getEmployeeFromStorage() {
Employee employee = new Employee(getPersonFromStorage());
// ... logic for employee-specific fields
return employee;
}
}
I also added static to the methods, assuming you're intending to create new instances with no direct relationship to existing objects. This would not work with the first variant.