I'm new to programming and I've got a task to make a swing/GUI crud where you are able to put in people/animal/movies into an array etc (I'm currently doing people) and it will generate mail, username etc. I've done all that but I'm missing one thing.
Now - I want to give each element in the array an unique ID of some sort where if 1 person has ID 25 for instance, so there can't be another element with the same ID 25 unless i remove that specific element.
I use a Jframe and then a java public class where i have get's and set's for my Jframe.
Sorry but I'm new - thank you.
Java.lang.Object has methods called hasCode() and equals(). These methods play a significant role in the real time application. However its use is not always common to all applications.
hashCode()
As you know this method provides the has code of an object. Basically the default implementation of hashCode() provided by Object is derived by mapping the memory address to an integer value. If look into the source of Object class , you will find the following code for the hashCode. public native int hashCode(); It indicates that hashCode is the native implementation which provides the memory address to a certain extent. However it is possible to override the hashCode method in your implementation class.
equals()
This particular method is used to make equal comparison between two objects. There are two types of comparisons in Java. One is using “= =” operator and another is “equals()”. I hope that you know the difference between this two. More specifically the “.equals()” refers to equivalence relations. So in broad sense you say that two objects are equivalent they satisfy the “equals()” condition. If you look into the source code of Object class you will find the following code for the equals() method.
So, lets create a class Person overriding these methods:
public class Person {
private Integer personId;
private String fullName;
public Integer getPersonId() {
return personId;
}
public void setPersonId(Integer personId) {
this.personId = personId;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
#Override
public String toString() {
return "Person [personId=" + personId + ", fullName="
+ fullName + "]";
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((fullName == null) ? 0 : fullName.hashCode());
result = prime * result + ((personId == null) ? 0 : personId.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 (fullName == null) {
if (other.fullName != null)
return false;
} else if (!fullName.equals(other.fullName))
return false;
if (personId == null) {
if (other.personId != null)
return false;
} else if (!personId.equals(other.personId))
return false;
return true;
}
}
and now our main class to create and manage duplicate objects of class Person:
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class UniqueArrayExample {
//To remove duplicates objects we use here HashSet util collection
private static Set<Person> personSet = new HashSet<Person>();
public static void main(String[] args) {
//creating four objects of Person Class
Person person1 = new Person();
Person person2 = new Person();
Person person3Dupl = new Person();
Person person4 = new Person();
//third object person3Dup1 is duplicate of Object person1
person1.setPersonId(12341);
person2.setPersonId(12342);
person3Dupl.setPersonId(12341);
person4.setPersonId(12344);
person1.setFullName("Suresh Kumar");
person2.setFullName("Mahesh Singh");
person3Dupl.setFullName("Suresh Kumar");
person4.setFullName("Rajkumar Singh");
//Add those Person class Object to Set<Person> collection
personSet.add(person1);
personSet.add(person2);
personSet.add(person3Dupl);
personSet.add(person4);
//So here after getting all Objects to Iterator and by
//printing those will not give you the person1 Object duplicate.
Iterator<Person> iterator = personSet.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next().toString());
}
}
}
here output generated as above class:
Person [personId=12342, fullName=Mahesh Singh]
Person [personId=12341, fullName=Suresh Kumar]
Person [personId=12344, fullName=Rajkumar Singh]
the duplicate object possibles to remove by use of HashSet, because of overriding hashCode() and equals() methods inside class Person, if you remove those methods from the class Person, Set collection will add all Objects of Person and will remove the redundancy.
Hope this would help you.
Related
UPDATE: The answer is actually in the documentation:
Note: Great care must be exercised if mutable objects are used as set
elements. The behavior of a set is not specified if the value of an
object is changed in a manner that affects equals comparisons while
the object is an element in the set.
Case closed, thanks everyone!
Edit: The referenced topic about duplicates in a hash sets does have the same point, however it does not answer my question: Why is the documentation not saying anything about that a set is only guaraneteed to work with immutable objects?
edit2: I do understand what happens. The set of course cannot know when the hashcode of the entities change after they have been added. But the point is that imo the documentation should clearly state that sets only work properly with immutable objects.
I've been working with Java for more than 5 years now, and don't laugh, but only now I realized something about the Sets. I thought I understood what a set is, namely what the doc says:
A collection that contains no duplicate elements. More formally, sets
* contain no pair of elements e1 and e2 such that * e1.equals(e2), and at most one null element.
But, this is totally not true?! See here:
public static void main(String[] args) {
Set<Entity> entitySet = new HashSet<>();
Entity e1 = new Entity("One");
Entity e2 = new Entity("Two");
entitySet.add(e1);
entitySet.add(e2);
e2.name = "One"; // !
System.out.println("Objects equal:" + e1.equals(e2));
Iterator<Entity> iterator = entitySet.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
static class Entity {
String name;
Entity(String name) {
this.name = name;
}
#Override
public boolean equals(Object obj) {
if (!(obj instanceof Entity)) {
return false;
}
return name.equals(((Entity) obj).name);
}
#Override
public int hashCode() {
return name.hashCode();
}
#Override
public String toString() {
return "Entity[name=" + name + "]";
}
Output:
Objects equal:true
Entity[name=One]
Entity[name=One]
So, I guess the thing about sets not containing duplicates is only true when we deal with immutable entries? But why is the doc not saying anything about it? I was never really aware of this. The problem with this of course is that the entites could contain any number of further fields that are not part of the equality definition; and they might be different in those fields. I'm thinking about something like this:
public static void main(String[] args) {
Set<Entity> entitySet = new HashSet<>();
Entity e1 = new Entity("Public", true);
Entity e2 = new Entity("Secret", false);
entitySet.add(e1);
entitySet.add(e2);
e2.name = "Public";
Iterator<Entity> iterator = entitySet.iterator();
// print only public entity (e1)
while (iterator.hasNext()) {
Entity e = iterator.next();
if (e.equals(e1)) {
System.out.println(e);
}
}
}
static class Entity {
String name;
boolean mayBeDisplayedToUser;
Entity(String name, boolean mayBeDisplayedToUser) {
this.name = name;
this.mayBeDisplayedToUser = mayBeDisplayedToUser;
}
#Override
public boolean equals(Object obj) {
if (!(obj instanceof Entity)) {
return false;
}
return name.equals(((Entity) obj).name);
}
#Override
public int hashCode() {
return name.hashCode();
}
#Override
public String toString() {
return "Entity[name=" + name + ", may be displayed:" + mayBeDisplayedToUser + "]";
}
}
Output:
Entity[name=Public, may be displayed:false] Entity[name=Public, may be
displayed:true]
So.. I'm quite puzzled right now. Am I the only one that was not aware of this?
You are adding the items to the HashSet while they are unique and then mutating the items after the fact. The containing HashSet has no idea that you broke the set contract by changing obj.name.
I have an ArrayList of Objects. I want to see if that ArrayList contains an Object with a particular field.
TeamBuilder.java
public class TeamBuilder {
public static void main(String[] args) {
Team team = new Team();
team.addMember(new TeamMember("myID"));
System.out.println(team.containsMember("myID")); //false
}
}
TeamMember.java
public class TeamMember {
private String id;
public TeamMember(String id) {
this.id = id;
}
public String getID() {
return this.id;
}
#Override
public boolean equals(Object o) {
if (o instanceof TeamMember) {
o = ((TeamMember) o).getID();
}
return o.equals(this.getID());
}
}
Team.java
import java.util.ArrayList;
public class Team {
private ArrayList<TeamMember> members = new ArrayList<>();
public boolean addMember(TeamMember teamMember) {
if (members.contains(teamMember)) {
return false;
}
members.add(teamMember);
return true;
}
public boolean containsMember(String eid) {
System.out.println(members.get(0).equals(eid)); //true
System.out.println(members.contains(eid)); //false
if (members.contains(eid)) {
return true;
}
return false;
}
}
I do not want to use a loop and I do not want to overwrite arrayList.contains().
I was expecting .contains() to iterate through my list of TeamMember's and return true when it found one that was equal to the Object passed. The two Objects are equal, but the .contains() method is returning false.
How can I elegantly check to see if a Team contains a TeamMember with the specified ID? I was under the impression I could avoid a for loop because of Java method: Finding object in array list given a known attribute value, but I am not able to get it to work.
The most elegant solution is to
First and foremost fix your equals method so that it fulfills equals contract --- meaning if a.equals(b) then b.equals(a) must be true.
You should never have a TeamMember object be equal to a String. That will result in the possibility of hard to debug side effects and bugs, that you really don't want.
The equals method should never throw an exception. He should first check for reference equality, then class sameness, then field equality.
Your TeamMember class should also override hashCode() and it should use the same fields as the equals method does.
then set up a HashMap<String, TeamMember> that matches ID Strings TeamMember objects.
Be sure that your ID's are immutable.
Fill your Map with String-TeamMember pairs
Call get(String key) when you need a TeamMember that matches an ID.
So I have a class called Person which looks like this
public class Person {
private String personName;
public String toString(){
return personName;
}
public Person(String personName){
this.personName = personName;
}
}
and another class in which I am creating the object(s) person
public class IdanJavaTask {
public static void main(String[] args) {
Person p1 = new Person("Bob");
System.out.println("p1 : " + p1);
Person p2 = new Person("Joe");
System.out.println("p2 :" + p2);
}
}
so far everything is fine and my print statement is
p1: Bob
p2: Joe
Now I want to create a new object, p3 and set it equal to p1
my class now looks like this:
public class IdanJavaTask {
public static void main(String[] args) {
Person p1 = new Person("Bob");
System.out.println("p1 : " + p1);
Person p2 = new Person("Joe");
System.out.println("p2 :" + p2);
Person p3 = new Person (p1);
System.out.println("p3 equal to p1:" + p3.equals(p1));
}
}
when I try to do this I get the following error message:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The constructor Person(Person) is undefined
at vehicleassignment.IdanJavaTask.main(IdanJavaTask.java:13)
I am thinking I will need to add a method to my main (Person) class, but I do not know why or what to add? why can't I just set the objects equal to eachother?
There are two ways to interpret "set the objects equal to each other".
One is that you want p1 and p3 to refer to the same object. Like how Clark Kent and Superman are two names (references) for the same person. This would be accomplished by:
Person p1 = new Person("Jim");
Person p3 = p1;
In this scenario, if anything happens to p1, that same thing has happened to p3. If you kill Clark Kent, you have killed Superman (as they are one and the same). Java determines equality with the equals(Object o) method - two objects a and b are equal iff a.equals(b) and b.equals(a) return true. These two objects will be equal using the base Object definition of equality, so you don't have to worry about that.
The other way to interpret your meaning is to create a new person object, which happens to be an exact copy of the first person. In order to do this, you'd have to add another constructor to your person class that takes a person as an argument:
public class Person {
private String personName;
public String toString(){
return personName;
}
public Person(String personName){
this.personName = personName;
}
public Person(Person personToCopy){
this.personName = personToCopy.personName;
}
}
With this setup, you can do what you're doing in your main.
Person p1 = new Person("Bob");
Person p3 = new Person(p1); //Will have name Bob.
In order to make p1 and p3 equal, we have to teach the Person class to use its fields for checking equality. We can do this by overriding the equals method in class person.
public boolean equals(Object o){
if(! (o instanceof Person)) return false; //a Person can't be equal to a non-person
Person p = (Person) o;
return personName == null && p.personName == null || personName.equals(p.personName);
}
Whenever we overwrite the equals method, it is good practice to also overwrite the hashcode method, which returns a unique int for each Object. Since the only field that a Person object has is its name, we can simply use that hashcode.
public int hashCode(){
return personName == null ? 0 : personName.hashCode();
}
So all together, our Person class looks like this:
public class Person {
private String personName;
public String toString(){
return personName;
}
public Person(String personName){
this.personName = personName;
}
public Person(Person personToCopy){
this.personName = personToCopy.personName;
}
public boolean equals(Object o){
if(! (o instanceof Person)) return false; //a Person can't be equal to a non-person
Person p = (Person) o;
return personName == null && p.personName == null || personName.equals(p.personName);
}
public int hashCode(){
return personName == null ? 0 : personName.hashCode();
}
}
Person p3 = new Person(p1);
This is known as a copy constructor. You'll need to define it explicitly, in this case:
public Person(Person p) {
this.personName = p.personName;
}
You also need to override the equals() method (and the hashCode() method) in order to use it, otherwise the equals() method of the root class Object would be used, which always returns false:
#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 (personName == null) {
if (other.personName != null)
return false;
} else if (!personName.equals(other.personName))
return false;
return true;
}
See What issues should be considered when overriding equals and hashCode in Java?.
No object in Java has a default copy constructor, with the exception of autoboxed objects like Float or Integer which are indeed copied.
This means that under all circumstances it's your duty to define a copy constructor and specify what is copied, in your example:
public Person(Person other) {
this.personName = other.personName;
}
Since in Java everything is passed by reference a plain assignment would just make 2 variables point to the same instance, eg:
Person p1 = new Person("Joe");
Person p2 = p1;
// now both point to the same object, not intended behavior in your case
If you want p3 to reference p2, meaning a change to p2 will update p3 and vice versa, just do
Person p3 = p2;
if you want to clone the data and have two distinct, but 'equal' copies of the person, you can implement a constructor in Person that takes a Person and copies the values into the classes fields.
It is because of your Person class constructor. You defined it to accept String type only and you gave it an object "p1" when you instantiate it. Editing the constructor or creating a new one will solve it.
I have 3 fields for defining a unique object, e.g.: region, name, platform which are string values.
A list of such objects has to be compared with another list of such objects. I was thinking to create an object for that such as
Class myObject{
private String region;
private String name;
private String platform;
}
and then create a list of them to compare each objects in both the lists. Somebody please provide me a better solution for this problem.
I have two tables with columns, id,region, platform, name, zone, count ,etc and the values repeat in this table. Another table has id, region, platform, name zone. First table gives the list of reserved AWS EC2 instances and second table the list of AWS EC2 instances which are running now. I need to find out if all the reserved instances are currently running or is there anything unutilized.
Can anyone suggest a good solution for this problem.
You need to extend your class definition with and equals() and hashCode() method.
See the following articles: Equality on artima.com & Java Equals & HashCode on ideyatech.com. Google for many more.
You can then use a Set or iterate through a list using equals as the test to help generate a collection of unique objects.
To check if the two objects are equal, you need to override equals() and should override hashCode() also. More detail can be found : here
Below is a sample class MyClazz with a test MyClazzTest to help you understand:
MyClazz
package com.my.test;
public class MyClazz {
private String region;
private String name;
private String platform;
public MyClazz(String region, String name, String platform) {
this.region = region;
this.name = name;
this.platform = platform;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result
+ ((platform == null) ? 0 : platform.hashCode());
result = prime * result + ((region == null) ? 0 : region.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;
MyClazz other = (MyClazz) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (platform == null) {
if (other.platform != null)
return false;
} else if (!platform.equals(other.platform))
return false;
if (region == null) {
if (other.region != null)
return false;
} else if (!region.equals(other.region))
return false;
return true;
}
}
MyClazzTest
package com.my.test;
import org.junit.Assert;
import org.junit.Test;
public class MyClazzTest {
#Test
public void equalObject() {
MyClazz object = new MyClazz("UK", "Chris", "Window");
MyClazz duplicateObject = new MyClazz("UK", "Chris", "Window");
Assert.assertTrue(object.equals(duplicateObject));
}
#Test
public void notEqualObject() {
MyClazz object = new MyClazz("UK", "Chris", "Window");
MyClazz differentObject = new MyClazz("US", "Chris", "Window");
Assert.assertFalse(object.equals(differentObject));
}
}
For the edited question:
Create a POJO Class with all fields, representing a composite key.
Implement equals and hashCode methods for it.
Create 2 collections of POJOs, filling them with data from tables.
Use one of the methods of CollectionUtils of ApacheCommons to play with your collections. E.g.: CollectionUtils#isSubCollection
I am using a map and want to use a value object as a map key..and a list as value. The value object has 2 properties first name, second name..i want to return map.containsKey() as true if both properties matched by some key in the same map..
I tried to use comparator as below
public class comaparatorEx implements Comparator<Test>{
public static void main(String args[]){
Map m= new HashMap<Test,List<String>>();
Test t = new Test();
t.setFirstname("vamsi");
t.setSecondname("priya");
List descriptionList=new ArrayList();
descriptionList.add("description1");
m.put(t, descriptionList);
Test t2 = new Test();
t2.setFirstname("vamsi");
t2.setSecondname("priya");
if(m.containsKey(t2)){
System.out.println("user found");
}
}
public int compare(Test o1, Test o2) {
if((o1.firstname.equals(o2.firstname) )&& o1.secondname.equals(o2.secondname))
return 0;
else return 1;
}
}
this is the value object i am using
public class Test {
String firstname;
String secondname;
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getSecondname() {
return secondname;
}
public void setSecondname(String secondname) {
this.secondname = secondname;
}
}
But it returns false for me..please help me ..thanks in advance
For a HashMap, you need to overwrite equals and hashCode in your class.
Possible implementation:
class Test
{
...
#Override
public int hashCode()
{
return 31*firstname.hashCode() + secondname.hashCode();
}
#Override
public boolean equals(Object obj)
{
// basic type validation
if (!(obj instanceof Test))
return false;
Test t = (Test)obj;
return firstname.equals(t.firstname) && secondname.equals(t.secondname);
}
}
Comparator is for comparison-based collections such as TreeMap. To use this, supply an instance of this class in the constructor:
Map m = new TreeMap<Test,List<String>>(new comaparatorEx());
But there is a problem with your compare function - there needs to be logical ordering between the elements (there isn't as you never return -1). String has a compareTo, which you can just use:
public int compare(Test o1, Test o2) {
int result = o1.firstname.compareTo(o2.firstname);
if (result == 0)
return o1.secondname.compareTo(o2.secondname));
else
return result;
}
HashMap uses the hashCode() and equals() methods, internally, to determine e.g. what buckets to look in, and whether the objects in that bucket are the same. You will need to implement both for your Test class, otherwise it will effectively default to reference equality (i.e. are they the exact same object)
You need override the hashcode() and equals() methods to give meaningful equality between the Test object.
HashMap insertions is bassed on the hashcode.
When we pass an both key and value to put() method to store on HashMap , it uses key object hashcode() method to calculate hashcode and they by applying hashing on that hashcode it identifies bucket location for storing value object and keys equals () method will be used to identify correct key value pair in HashMap .
Read more: http://javarevisited.blogspot.com/2011/02/how-hashmap-works-in-java.html#ixzz2fDozSqmi
you would have to override the default equals method in your test class.
you can write something like this.
#Override
public boolean equals(Object o) {
if(null != o && o instanceof test && o.attr1.equals(this.attr1)) return true;
else return false;
}
containskey In map looks at the equals method. More info in the java docs
The implementation of equals i have given is just an example. For a proper implementation you should read this