How to compare sets without comparing the content - java

So, I have two Sets with elements of my class Capability.
public class Capability {
private String name;
public Capability(){
//
}
public Capability(String name){
this.name = name;
//this.id = count.getAndIncrement();
}
public String getName(){
return name;
}
#Override
public String toString(){
return "Capability: "+name+".";
}
}
Please disregard the value of this class over a String, this is for future expansion.
I'm trying to compare two sets that I've gotten from importing a json file, so they are not the same object, nor contain the same object, just have the same content.
public boolean allCapabilitiesMet(){
int count = 0;
for(Capability taskCap : this.getReqCapabilities()){
for(Capability primCap : this.getPrimitive().getCapabilities())
{
System.out.println(taskCap.equals(primCap));
System.out.println(taskCap.getName().equals(primCap.getName()));
if(taskCap.equals(primCap)){
count++;
}
}
}
return count == this.getReqCapabilities().size();
//return this.getPrimitive().getCapabilities().containsAll(this.getReqCapabilities());
}
The goal is to see if one set is a subset of the other, which I could do with the commented return before I switched to importing from the json file.
The thing is, I could fix this right now by simply changing the if clause to the string comparison, because that does indeed work. This would be terrible once I start adding other fields to the main class.
Is there anything I can do to compare the sets content without manually checking their content?

So I just replaced the equals() and hashCode() methods in Capability after adding an id field.
#Override
public boolean equals(Object obj){
if(this == obj)
return true;
if(obj == null || obj.getClass() != this.getClass())
return false;
Capability cap = (Capability) obj;
return (cap.getName().equals(this.getName()) && cap.getId() == this.getId());
}
#Override
public int hashCode()
{
return (int) this.id;
}
With this, I can use the solution that I'd originally planned for the comparison
public boolean allCapabilitiesMet(){
return this.getPrimitive().getCapabilities().containsAll(this.getReqCapabilities());
}
Is there any issue with this implementation? Sadly, I'll have to add a term to the if statement everytime I want to add a field to Capability. Is there any other way?

Related

Search an ArrayList with multiple fields using only one field from a different ArrayList

If there is an ArrayList for moviesAvailable and the list takes title, year, genre, price. How can I get a list of movies using an ArrayList of genres?
There is a toString method in the Movie class that prints out the movies. When I run the code everything past the if statement doesn't run because the condition is returned as false.
You need to override equals() in your Genre class, so that you can compare instances of that class to each other.
Here's a quick implementation, which can easily be extended for additional functionality:
#Override
public boolean equals(Object o) {
if (!(o instanceof Genre)) {
return false;
}
return ((Genre) o).name.equals(this.name);
}
You need to override equals method in Genre class to make if condition working, see below code
private String name;
Genre(String name){
this.name = name;
}
public boolean hasType(String genre) {
return genre.equals(this.name);
}
public boolean equals(Object o) {
if (!(o instanceof Genre)) {
return false;
}
return this.name.equals(((Genre)o).name);
}

Verify there is a combination of unique string

class Details{
String name;
String age;
String email;
String location;
}
1) If there is List of Details as in List<Details> how to verify for a combination of name and email collectively unique. (i.e) For a single email address there cant be two name entry.
2) How to verify the combination of all fields in the class file is unique.
what would be a perfect data structure to address this ?.
You can hash values by a separator like #, and then find that all uniques or not. Hash value for a Details is name + "#" + "email in the first case, and is name + "#" + age + "#" + email + "#" + location in the second case.
You can using Hashmap to find duplicates if there is any with the specified key (or hash) for each instance of Details.
If you need to achieve unique values-only, you should use Set. You have to use your own equals & hashCode implementation, for example, for case 1):
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Details details = (Details) o;
return Objects.equals(name, details.name) &&
Objects.equals(email, details.email);
}
#Override
public int hashCode() {
return Objects.hash(name, email);
}
If you need all Details members to be unique just update hash & equals implementation with needed properties.
When you overrides an equals method within any object, you can perfectly check the equality of that Object with another one even though they are residing somewhere different within the memory.
So the below code
myList.contains(myObject);
will respond if there is an object within myList that the equals method return true with myObject.
In all major IDEs (like intelliJ, netbeans, eclipse, etc) the IDE will help you to override the equals method accurately. here is the auto-generated code for overriding equals method using intelliJ IDE
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Details details = (Details) o;
return Objects.equals(name, details.name) &&
Objects.equals(email, details.email);
}
Now if you want to avoid duplicate to be added inside your List, you should use Set instead of a List. A Set will add a new element if the combination of hashCode and equals method are different (comparing the existing object and newly intended to add object)
So we have to override hashCode method (generated by intelliJ IDE) if we want to use a flavor of Set class:
#Override
public int hashCode() {
return Objects.hash(name, email);
}
now if you create a Set and try to add two objects with similar name and email inside that Set, the set will only add the first unique name and email address, even thou the other fields of the second object have different values
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
class Details {
String name;
String age;
String email;
String location;
public Details(String name, String age, String email, String location) {
this.name = name;
this.age = age;
this.email = email;
this.location = location;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Details details = (Details) o;
return Objects.equals(name, details.name) &&
Objects.equals(email, details.email);
}
#Override
public int hashCode() {
return Objects.hash(name, email);
}
#Override
public String toString() {
return String.format("(name: %s, age: %s, email: %s, location: %s)", name, age, email, location);
}
public static void main(String[] args) {
Set<Details> mySet = new HashSet<>();
mySet.add(new Details("Mehdi", "12", "123#xyz.com", "Los Angeles"));
mySet.add(new Details("Mehdi", "34", "123#xyz.com", "Las Vegas"));
System.out.println(mySet);
}
}
This is the whole test app. There is something else that worth mentioning. if in any case you have saved your data inside a list and you want to remove the duplicate based on the rules you have (ex name, email) You can try this approach:
Set<Details> mySet = new HashSet<>(myList);
Which myList is your list. so in your app that will be like this:
public static void main(String[] args) {
List<Details> myList = new ArrayList<>();
myList.add(new Details("Mehdi", "12", "123#xyz.com", "Los Angeles"));
myList.add(new Details("Mehdi", "34", "123#xyz.com", "Las Vegas"));
Set<Details> mySet = new HashSet<>(myList);
System.out.println(mySet);
}
and here is the result without any duplicate:
[(name: Mehdi, age: 12, email: 123#xyz.com, location: Los Angeles)]
Besides of using a hash as propposed by #OmG, you could also use a TreeSet with the key being a concatenation of the unique fields, also using a separator between them.
A Set only admits unique keys.

Java POJO collection attributes

I have a POJO something like the one mentioned below. Here I'm referring Set collection attribute in POJO1. I understand that set does not contain duplicate. Do I need to override equals() and hashCode() methods in POJO2? Using a Set here is not really going to helpful unless we override equals and hashCode methods? Please help me to understand little bit more on this context!
public class POJO1 {
private String name;
private Set<POJO2> pj2;
public Company(){
pj2 = new HashSet<>();
}
//setter and getter methods
}
Yes the only way for Java to understand which objects are duplicates is to call equals() method. Default implementation of equals() checks that references of two objects point to the same location in memory.
But depending on exact implementation of your Set you might need to override hashCode/equals or implement Comparable interface.
Since you put objects of POJO2 into HashSet you need to verride hashCodeequalsmethods inPOJO2` class.
You do like this
import java.util.Set;
public class POJO1 {
private String name;
private Set<POJO2> pojo2;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<POJO2> getPojo2() {
return pojo2;
}
public void setPojo2(Set<POJO2> pojo2) {
this.pojo2 = pojo2;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
POJO1 pojo1 = (POJO1) o;
if (name != null ? !name.equals(pojo1.name) : pojo1.name != null) return false;
return pojo2 != null ? pojo2.equals(pojo1.pojo2) : pojo1.pojo2 == null;
}
#Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (pojo2 != null ? pojo2.hashCode() : 0);
return result;
}
}
Learn more at https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#equals(java.lang.Object)

Compare object with deserialised object

I have a Swing application with a JList that works with a custom listmodel.
The model has an ArrayList to store the objects used. The situation is as follows:
Upon closing the application, all objects in the listmodel are serialized (in the default way, the class just implements Serializable), and written to a file via an ObjectOutputStream. When the application starts up, all objects are read from the file and stored in my custom ListModel again.
I'm trying to add a feature to let the user import objects too, from a file he specifies. A static method in another class reads all objects from the file, and returns them in an ArrayList. I then use the for-each loop in my MainForm class to store each object from the returned ArrayList in the ListModel. In the loop, I want to check whether the ListModel already contains a certain object, but this does not work.
In code, I'm doing the following:
for(MyObject o: readObjects) {
if(!myListModel.contains(o)) //listmodel just calls contains() on its ArrayList
myListModel.addElement(o);
}
However, even when the object is already in the ArrayList (I keep importing objects from the same file), they are still added.
The question is, how come objects are not equal anymore when deserialized, and is there a way to compare them anyway?
Here is a simple java object
public class MyObject {
private String firstname;
private String lastname;
private String email;
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((firstname == null) ? 0 : firstname.hashCode());
result = prime * result
+ ((lastname == null) ? 0 : lastname.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;
MyObject other = (MyObject) obj;
if (firstname == null) {
if (other.firstname != null)
return false;
} else if (!firstname.equals(other.firstname))
return false;
if (lastname == null) {
if (other.lastname != null)
return false;
} else if (!lastname.equals(other.lastname))
return false;
return true;
}
}
(hashcode and equals auto generated by eclipse)
If you look at equals() method, you will see that 2 objects are compared on the fields firstname and lastname, and only those 2. This means that if you insert objects of this type in a list, you will be able to use contains(Object o) and if an object contains the same firstname and lastname, you'll find it.
You need to override the equals and hashcode method of your object (many examples are available on SO, for example here.
Once you have done that, the contains method of List will behave as you expect. Your problem does not seem related to serialization. In other words, the following code:
List<MyObject> list = new ArrayList<MyObject>();
list.add(new MyObject());
System.out.println(list.contains(new MyObject()));
will print false if you don't override equals & hashcode whereas it might print true if you do (depending on whether your equals method consider those 2 new MyObject() to be equals or not). Your IDE should have a way to auto generate the code for you.
Once you are happy that the contains method works as expected on your objects, serialising/deserialising should work as expected.

Remove duplicate objects from a ArrayList in Android

I know this has be discussed over and over again here, but none of the examples I've tried worked for me.
What I've got
I access the Call log from Android and I get a list of all calls made. Of course, here I get a lot of duplicates.
First I make a List
List<ContactObject> lstContacts = new ArrayList<ContactObject>();
Then I add objects into it
While (get some record in call log)
{
ContactObject contact = new ContactObject();
contact.SetAllProperties(......)
lstContacts.add(contact);
}
Set<ContactObject> unique = new LinkedHashSet<ContactObject>(lstContacts);
lstContacts = new ArrayList<ContactObject>(unique);
The Contact Object class is simple
public class ContactObject {
public ContactObject() {
super();
}
#Override
public boolean equals(Object obj) {
if (!(obj instanceof ContactObject))
return false;
return this.lstPhones == ((ContactObject) obj).getLstPhones();
}
#Override
public int hashCode() {
return lstPhones.hashCode();
}
private long Id;
private String name;
private List<String> lstPhones;
private String details;
//... getters and settres
}
What I need
I need to have a Contact only once in the list. As I've read around here there are a couple of things that can be done like Set, HashSet, TreeSet. TreeSet seems the best as it keeps the order just as I receive it from the Call log. I've tried to make my code work with it but no success. Could anyone be so kind to give me a sample code based on my example. Thank you for your time.
The Working Solution. Thank you all for your support, you've made my day.
In ContactObject override the two methods
#Override
public boolean equals(Object obj) {
if (!(obj instanceof ContactObject))
return false;
return lstPhones.equals(((ContactObject) obj).getLstPhones());
}
#Override
public int hashCode() {
return (lstPhones == null) ? 0 : lstPhones.hashCode();
}
//Getters and Setters and COnstructor....
Simply use it as
Set<ContactObject> unique = new LinkedHashSet<ContactObject>(lstContacts);
lstContacts = new ArrayList<ContactObject>(unique);
LinkedHashSet which keeps insertion-order can be used in your case.
HashSet: no order.
TreeSet: sorted set, but not keep insertion order.
EDIT: As Software Monkey commented, hashCode() and equals() should be overwritten in ContactObject to fit the hash-based Set.
Remove duplication of Custom Object
Example of Removing duplicate using Comparator
Lets suppose you have a class "Contact"
public class Contact implements Comparable<Contact> {
public String getName() {
return this.Name;
}
public void setName(String name) {
this.Name = name;
}
public String getNumber() {
return this.Number;
}
public void setNumber(String number) {
this.Number = number;
}
///// this method is very important you must have to implement it.
#Override
public String toString() {
return "\n" +"Name=" + name + " Number=" + Number;
}
Here is how you can remove duplicate entries using Set , just pass your list in the function and it will work for you. New list will be returned which will have no duplicated contacts.
public ArrayList<Contact> removeDuplicates(ArrayList<Contact> list){
Set<Contact> set = new TreeSet(new Comparator<Contact>() {
#Override
public int compare(Contact o1, Contact o2) {
if(o1.getNumber().equalsIgnoreCase(o2.getNumber())){
return 0;
}
return 1;
}
});
set.addAll(list);
final ArrayList newList = new ArrayList(set);
return newList;
}
It worked for me so please try and give me your feedback. Thanks
P.S: Credit goes to Nilanchala at this article
For sure you can use TreeSet to store only once but a common mistake is do not override hashCode() and equal() methods:
This can fit for you:
public boolean equals(Object obj) {
if (!(obj instanceof ContactObject))
return false;
return this.id == ((ContactObject) obj).getId(); // you need to refine this
}
public int hashCode() {
return name.hashCode();
}
List<ContactObject> listContacts = new ArrayList<ContactObject>();
//populate...
//LinkedHashSet preserves the order of the original list
Set<ContactObject> unique = new LinkedHasgSet<ContactObject>(listContacts);
listContacts = new ArrayList<ContactOjbect>(unique);
Use Set's instead.
Set's works as an Mathematical collection, so it doesn't allow duplicated elements.
So it checks the equality and the .equals() methods for each element each time you add an new element to it.

Categories

Resources