Skip to content
Advertisement

Add unique elements in HashSet based on attributes of framework provided non-editable object classes

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);

Advertisement

Answer

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
}
User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement