How to create a set of deep objects - java

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.

Related

Compare a string value with arraylist<object>

I have a class of persons.
Public class Person{
private String name = "";
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Now I have another class and inside the class I declared an arraylist
public class AddPerson {
public ArrayList<Person> pers = new ArrayList<Person>();
//here we add some persons to the arraylist
pers.add(new Person("Simon"));
pers.add(new Person("Oscar"));
pers.add(new Person("Alfred"));
String name = "Simon";
pers.contains(name); //return false
pers.equals(name); //return false
//I also want to be able to return the value and index of the name in arraylist if it exsists.
if(pers.contains(name)) //return index and value
}
When I try to check if two strings are equal or if a string is already in my list I get both false. I did some research and I saw that I need to override my equals method (and mabey hash and contains method as well). I do not know how to do it and I could not find a good reference how to do it. Please help me to achive that.
Here the problem is you are comparing a Person object with String object using the contains method.So actually in this case there is no point of implementing equals() method and hashCode() methods or using the contains() method to solve this .Only you have to do is check if person's attribute name is equal to your comparing String called name.
Sample solution:
String name="Simon";
Person result = null;//To store the person object if a person name Simon found in the list
int personIndex = -1; //To store the index of founded person
int indexCount =-1;//To maintain the current index value of arrayList
for(Person tempPerson:pers){//Iterating the list to find a person name Simon
indexCount++;
if(pers.getName().equals(name)){
personIndex = indexCount;
result = tempPerson;
}
}
Now you can use the values in personIndex and indexCount variables for future calculations.You can use a condition like if(personIndex >= 0) to check if really a person found with the comparing name after the iteration and then you can get the result variable value.
That is because you are trying to compare a string ("simon") and an object Person built from a string (Person("Simon")). These are two different objects, and it is thus normal to have false as a result.
Whether you want to compare two Person (this would make sense) or you are actually trying to compare a string and a person are it is most certainly a bad idea (you can't actually compare apples and oranges, well this is the same here).
What you might want to do is whether
to override Person's equals method to, for example, return true if they have the same name. => comparing Persons
to iterate on your pers array and fetch it's name property via getName() to compare it with you name string => comparing Strings
1 =>
#Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final Person person = (Person)o;
return name.equals(person.name);
}
#Override
public int hashCode() {
return Objects.hash(name);
}
2 =>
for (final Person person : pers) {
if(name.equals(person.getName())){
// ...
}
}

What's the best way to change attributes of objects stored in an ArrayList or HashMap?

I have to do a little exercise (homework, like a friendlist) in Java, and i'm a little stuck on one of the tasks that i have to implement in my program.
The exercise is about storing some friend-objects with a variety of attributes in a container-class and implementing some methods in the container-class for various tasks on the friend-objects.
The overall exercise is not a problem at all, but i'm quite unconvinced that my solution is the way to go. I hope you can give me some tips here.
The method that is left over, should be something like a "updateFriend" method, with which you can set the value of a given attribute to a new value, straight from the container-class.
I've already set up my friend-class with a handfull of attributes (e.g. prename, lastname, date of birth, adress, and so on) an getters/setters for all of them. I've also implemented the container-class (as an ArrayList), but i can't seem to find an elegant way to implement this specific method. My updateFriend()-method right now takes three parameters.
1.The specific id of the friend-object
2.The name of the attribute that i want to change
3.The new value of the attribute
It uses an enum to check if the entered attribute is an existing attribute and if yes, the method searches the ArrayList for the object that contains that attribute and should overwrite the existing value. It gets a little bulky, as i have implemented a switch on the enum, that calls the fitting setter-method for each attribute of the friend, if the type in attribute exists at all.
So basically the friend-class looks like this:
public class Friend {
private static int friendCount = 1;
private String firstname;
private String lastname;
private LocalDate dateOfBirth;
private String phonenumber;
private String mobilenumber;
private String eMail;
private Adress home;
private int friendID;
//Getters & Setters
...
}
The method that gives me problems in the container-class looks something like this at the moment:
public void updateFriend(int id, String toChange, String newValue)
{
for(Attribute a : attribute.values())
{
if(String.valueOf(a).equalsIgnoreCase(toChange))
{
for(Friend f : friends)
{
int counter = 1;
if(f.getID() == id)
{
switch(a)
{
case FIRSTNAME:
{
f.setPreName(neuerWert);
break;
}
//a case for each attribute
}
I'm quite certain that my take on the given method is messy, slow, and cumbersome. What would be an elegant way of solving this?
Excuse my wording and thanks in advance, greets.
I would suggest 3 performance improvements.
Use HashMap instead of List with key as id. Since, id will be unique, it will take O(1) time to get the relevant object for modification instead of spending O(n) time on List iteration.
You can change the type of toChange parameter from String to enum. This will avoid enum to String conversion and then comparing it.
Since, you are already doing validation of the attribute to be modified and you must be following standard java convention while naming your getters and setters, you can use reflection to call the method on the Friend object by creating the method name from attribute name like set{Attributename}.
Okay, lets start using the enum Attribute to handle all the changes (Since you already holding the attribute values)
Attribute Enum
public enum Attribute {
FIRSTNAME("fname", (friend, name) -> friend.setFirstname(String.valueOf(name))),
LASTNAME("lname", (friend, lname) -> friend.setLastname(String.valueOf(lname))),
DATEOFBIRTH("dob", (friend, dob) -> friend.setDateOfBirth((LocalDate) dob)),
PHONENUMBER("pno", (friend, pno) -> friend.setFirstname(String.valueOf(pno))),
MOBILENUMBER("mno", (friend, mno) -> friend.setFirstname(String.valueOf(mno)));
private String attributeName;
private BiConsumer<Friend, Object> attributeSetter;
public static Attribute getAttributeSetterByName(String attributeName) {
return Arrays.stream(Attribute.values())
.filter(attribute -> attribute.getAttributeName().equalsIgnoreCase(attributeName))
.findFirst()
.orElseThrow(() -> new RuntimeException(String.format("Invalid Attribute name - %s", attributeName)));
//.orElse(null);
}
//Getter, Setter & Args Constructor (Use Lombok to reduce Boiler Plate code)
}
Update Logic
public void updateFriend(int id, String toChange, String newValue) {
Attribute attribute = Attribute.getAttributeSetterByName(toChange);
for (Friend friend : friends) {
if (friend.getId() == id) {
attribute.getAttributeSetter().accept(friend, newValue);
break;
}
}
}
You can use a java.util.function.Consumer<T> object to change an object inside your container where you have all the type safety you get. Instead of having magic strings and string arguments for values, which might not be even for string fields, you can work directly on the objects type:
public void updateFriend(int id, Consumer<Friend> c) {
// find the friend object
Friend found = null;
for (Friend f: this.friends) {
if (f.getId() == id) {
found = f;
break;
}
}
if (found == null) {
throw new IllegalArgumentException("There is no friend object with the given id");
}
// use the friend object.
c.accept(found);
}
You can use this method like this:
container.updateFriend(42, f -> f.setVorName("abc"));
container.updateFriend(9, f -> f.setAddress(some_address_object));

Find index of an object in a list

I have situation where I have a list(required items) that holds a table column result like:
NAME
ADDRESS
AGE
.
.
etc
In my method I get a User object that contains values for user.getName(), user.getAge() etc. I want to know the best way to ensure that every item in the list is present in the user object. The no of items in the list are variable.
public boolean isUserInfoComplete(User user, ArrayList list){
//so, if the list has AGE, the user.getAge() must have some value
}
One way I thought of is maintaining another list that holds values of every user info and checking that against my db list but that is not scalable.
It's not possible to dynamically match your method names with the list contents without reflection (which can be expensive and fragile). You may want to consider keeping your User values in a central Map cache. Here's one way to do that:
public class User {
private enum Field {
NAME,
AGE
//...
}
private Map<String, Object> values = new HashMap<>();
private void putValue(Field field, Object value) {
values.put(field.name(), value);
}
private Object getValue(Field field) {
return values.get(field.name());
}
public void setName(String name) {
putValue(Field.NAME, name);
}
public String getName() {
return (String)getValue(Field.NAME);
}
public void setAge(int age) {
putValue(Field.AGE, age);
}
public Integer getAge() {
return (Integer)getValue(Field.AGE);
}
//...
public boolean isUserInfoComplete(List<String> fields) {
return values.keySet().containsAll(fields);
}
}
You could use reflection to solve this problem if the items in the list match the getters in your User object.
For example, if AGE is in the list, you could use reflection to look for the getAge() method on the User class, call it on the object, and then check the result for null (or switch on the method return type to perform other types of checks).
Here's a starting point for you to experiment with (I haven't compiled or tested it):
public boolean isUserInfoComplete(User user, ArrayList list){
for(String attribute : list) {
String methodName = "get" + attribute.substring(0, 1).toUpperCase() + attribute.substring(1).toLowerCase();
Method method = User.class.getMethod(methodName, null);
if(method != null) {
Object result = method.invoke(user);
if(result == null) {
return false;
}
}
}
return true;
}
This seems like a case where you need reflection. This gives you the opportunity to inspect methods and field from your objects at runtime.
If you know your User-objects etc will follow a java bean standard then you will be able to use the getters for checking, though I see now problem in making your fields public final and checking directly on the fields themselves.
Take a look at https://docs.oracle.com/javase/tutorial/reflect/
You can check it using contains() while looping. This process will be very resource-consuming.
Maybe you can redesign something and simply compare two User objects? Will be faster. You can do it by providing your own implementation of equals and hashcode methods.

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.

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.

Categories

Resources