Remove duplicate objects from a ArrayList in Android - java

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.

Related

How to compare sets without comparing the content

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?

COMPARABLE - How to see if elements from objects are equals?

Im devoloping a program that i put some elements into my object within an ArrayList... this is my code
import java.util.ArrayList;
public class ListaConcepto {
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<CompararListas> Lista = new ArrayList<CompararListas>();
CompararListas obj1 = new CompararListas("abc", 12.25, "lala", 2);
CompararListas obj2 = new CompararListas("abc", 13.50, "lala", 3);
CompararListas obj3 = new CompararListas("poc", 12.50, "jaja", 1);
Lista.add(obj1);
Lista.add(obj2);
Lista.add(obj3);
}
}
Then... this is my class with the interface Comparable... and i need a method to implement to see if elements are EQUALS then print the result.
public class CompararListas implements Comparable<CompararListas> {
private String referencia;
private double monto;
private String descripcion;
private double NumeroParte;
public CompararListas(String referencia, double monto, String descripcion, double numeroParte) {
this.referencia = referencia;
this.monto = monto;
this.descripcion = descripcion;
this.NumeroParte = numeroParte;
}
public double getMonto() {
return monto;
}
public void setMonto(double monto) {
this.monto = monto;
}
public String getDescripcion() {
return descripcion;
}
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
public double getNumeroParte() {
return NumeroParte;
}
public void setNumeroParte(double numeroParte) {
NumeroParte = numeroParte;
}
public String getReferencia() {
return referencia;
}
public void setReferencia(String referencia) {
this.referencia = referencia;
}
#Override
public int compareTo(CompararListas o) {
// TODO Auto-generated method stub
return 0;
}
}
Remember Comparable is meant to be used to decide the relationship between to items for the purposes of ordering; it's not quite for equality. In fact, there's no requirement that any two comparable items must be equal.
In your case, you may want to order by NumeroParte, which means "Part Number" and seems like a sensible choice. Every type of data has a different way to be organized.
Your method may look something like
public int compareTo(ComprarListas o){
return this.NumeroParte - o.getNumeroParte();
}
Which works because both are integer numbers.
Or the appropriate property of your object - you may want to order based on price instead for instance.
If you want to know just if an object is equal to another you have to implemet the equals method (which doesn't requiere you to declare any interface, every class can do it)
#Override
public boolean equals(Object o) {
CompararListas other = (CompararListas) o;
return ...
}
with this method you can return what makes the two objects equals to you, if you just have to look for referencia.equals(other.referencia) or if you have to compare every property like referencia.equals(other.referencia) && monto == other.monto && ...
But if you want to compare elements in order to do something like ordering them there you have to implement the Comparable interface and implement the compareTo method
#Override
public int compareTo(CompararListas o) {
if (NumeroParte < o.NumeroParte)
return -1;
else if (NumeroParte > o.NumeroParte)
return 1;
return 0;
}
This will make the objects able to compare each other and for example know if one is "smaller" than other according to your criteria (in this example I only used NumeroParte and made it explicit to be easy to understand, but the key is that you can use any criteria you want to compare the objects and if you want order them later).
These are solutions to different problems, you have to identify which one is the better in your case.

Get command of a hashmap with a parameter different than the key in the hashmap?

I have a custom class as my key in my hashmap like so
// In the main function
HashMap<Drink, boolean> drinkMap = new HashMap<>();
// What I would like to be able to do:
drinkMap.get("beer");
// My drink Class which is used as the key
public class Drink implements Comparable<String> {
private String name;
private String info;
public String getName() {
return Name;
}
public Drink(String name, String info) {
this.name = name;
this.info = info;
}
}
What I want to do is have the get method for the hashmap compare the string that is passed in to Drink.name and if they are the same then return that hashmap entry, but I cannot figure out how to get this to work.
I tried implementing the equals and hashcode methods in my Drink class like so:
#Override
public int hashCode() {
return Name.hashCode();
}
#Override
public boolean equals(Object o) {
return o instanceof String && o.equals(Name);
}
But when I would do hashMap.get("beer") it kept returning null even though I know there exists a Drink object with the name "beer" in the map.
This is a terrible idea. You should always query a map with the same type (or a subtype thereof) as the intended key. Not doing that only opens you up to problems (as I'm sure you've started to notice).
You should consider either making the key of your map a String type, or querying your map by Drink.
(As to why your specific case isn't working: "beer".equals(drink) != drink.equals("beer").)

General way to group objects by several properties in Java

I have a collection of items:
class Item {
String type;
boolean flag;
int size;
...
}
There are a few possible types (say "a", "b" and "c") and therefore several possible combinations of type-flag values (["a" ; false], ["a" ; true"], ["b" ; false], ... ). I need to collapse items that have same combination of values, so I have collapse method with this signature
Item collapse(Collection<Item> items)
What I need is to divide input items list into groups that have same type and flag values
List<Collection<Item>> getGroups(Collection<Item> items) // method I need
so I could collapse each group
List<Item> r = getGroups(items).stream().map(Item::collapse).collect(toList());
So I could create a Map of Maps or make some composite keys, but it requires some boilerplate code which I'd like to avoid. In future I can have more attributes for grouping, so the solution should not be hardcoded on these two properties, but be easily extendable for new ones.
How can this be done nicely? Is there a well-known solution for the problem?
You can use Collectors.groupingBy:
public static void main(String[] args) {
List<Item> list = new ArrayList<>();
list.add(new Item(1, true, 1));
list.add(new Item(1, true, 2));
list.add(new Item(1, false, 3));
list.add(new Item(1, false, 4));
list.add(new Item(2, true, 5));
list.add(new Item(2, false, 6));
Collection<List<Item>> result = list.stream()
.collect(Collectors.groupingBy(x -> Arrays.<Object>asList(x.keyA, x.keyB)))
.values();
for (List<Item> items : result) {
System.out.println(items);
}
}
static class Item {
Integer keyA;
Boolean keyB;
Integer value;
public Item(Integer keyA, Boolean keyB, Integer value) {
this.keyA = keyA;
this.keyB = keyB;
this.value = value;
}
#Override
public String toString() {
return "Item{" +
"keyA=" + keyA +
", keyB=" + keyB +
", value=" + value +
'}';
}
}
In order to know which Item logically equals some other Item for collapsing, I'd put the responsibility of that on the Item class itself. You could override the equals method, but if you're gonna put them in a Set somewhere this might lead to undesirable results, so a separate method used for checking might be best.
Another option is to take those fields that would be used for this check and turn them into an inner class of Item. equals and hashCode could then be overridden for the inner class only and its instances used as a key for a Map.
None of this is going to automagically include any new fields you add later, however. So it'll be up to whoever maintains the class to make sure anything that needs to be included in the check (or equals/hashCode) is added to the method(s).
The only way I can really think of to get close to this is to use reflection. If anything that must be taken into account is only put in an inner class, that would work. If you must keep it on the Item class directly, perhaps defining an annotation (with runtime retention) could be useful. The code doing the checking (or equals/hashCode if used) could reflect upon the class and use every annotated field.
The annotation could look something like this:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface CollapseField {
}
And then used like this:
class Item {
#CollapseField
String type;
#CollapseField
boolean flag;
int size;
...
}
The code using it would then need to check which fields are annotated and get their values (both of these actions using reflection) and check equality with other objects to establish which belong together. Since this might impact performance quite a bit, using caching for something like a hash code would be a good idea.
In the end, I'm not sure if it's worth it over hard-coding the used values unless you're gonna use this in a large amount of classes or the number of fields could become quite large.
Finally, it may seem odd for Java, but perhaps using the properties pattern instead of fields may make sense. Although you'll lose some type safety. Steve Yegge made a long but interesting post about it: http://steve-yegge.blogspot.be/2008/10/universal-design-pattern.html
That's pretty much all I can come up with off the top of my head. As far as I know, there is no standard approach. Maybe someone knows of some convenient library offering a solution.
EDIT: here's an example where the fields to be used for the key are made into an inner class, which implements equals and hashCode so it can be used as the key for a Map:
import java.util.Objects;
public class Item {
int size;
final Key key;
public class Key {
String type;
boolean flag;
public Key(String type, boolean flag) {
this.type = type;
this.flag = flag;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
#Override
public int hashCode() {
int hash = 5;
hash = 89 * hash + Objects.hashCode(this.type);
hash = 89 * hash + (this.flag ? 1 : 0);
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Key other = (Key) obj;
if (!Objects.equals(this.type, other.type)) {
return false;
}
if (this.flag != other.flag) {
return false;
}
return true;
}
}
public Item(String type, boolean flag, int size) {
key = new Key(type, flag);
this.size = size;
}
public String getType() {
return key.type;
}
public void setType(String type) {
this.key.type = type;
}
public boolean isFlag() {
return key.flag;
}
public void setFlag(boolean flag) {
this.key.flag = flag;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public Key getKey() {
return key;
}
}
The getters and setters on the Item level delegate some of the fields to the Key. Note that the getters and setters in Key might not be necessary if you're only going through Item, since the fields are directly accessible to the containing class. If you need to add a field that must be part of the key, add it to Key. If it must not be used for identification, add it directly to Item. equals and hashCode can be easily auto-generated by any decent IDE if you must update them.
Do note that this solution might break if you use the class in some framework that does reflection or introspection. Depending on how it's approached, the fields in Key might end up being seen as properties of Item (due to the getters/setters) or not. Something like JPA or an EJB container approaching fields directly via reflection could fail to work with this.
Putting here my current approach with composite key, but still waiting for fresh ideas
Collection<Collection<Item>> getGroups(Collection<Item> items) {
Map<String, Collection<Item>> itemsByKey = new HashMap<>();
for (Item item : items) {
String key = item.key();
Collection<Item> byKey = itemsByKey.get(key);
if (byKey == null) {
itemsByKey.put(key, (byKey = new ArrayList<>()));
}
byKey.add(item);
}
return itemsByKey.values();
}
class Item {
public String key() { type + "-" + flag }
New fields can be added easily, but string keys do not look great.

How can I order TreeMaps or ArrayLists holding Persons based on their ID, name, or birthdate?

I have tried almost everything and I can't seem to get my lists to order themselves.
Here's some code:
private List<Person> names = new ArrayList<Person>();
private Map<Integer, Person> peopleMap = new TreeMap <Integer, Person>();
for(int i = 0; i<20; i++)
{
Person personOne = new Person();
peopleMap.put(personOne.id,personOne);
names.add(personOne);
}
Collections.sort(names);
run();
}
My Person class:
public class Person implements Comparable {
public String name;
public int id;
public Date birthdate;
static int idRecord = 0;
The values are filled with randoms. My date has a date format.
I also have a toString method inside my person class, but for some reason when I try to print my maps it gives me the hashcode (this is the hashcode right?) Person#a62fc3.
Here is my toString inside the person clasS:
public String toString()
{
char tab = '\t';
return ("ID Number: "+id+tab+" Name: "+tab+name+tab+" Birthdate: "+(birthdate.toString()));
}
I should add that I am not able to call my toString method inside my person class. Because it is printing Person#a62fc3.
public void sortByID()
{
char tab = '\t';
for (int i = 1; i<20; i++)
System.out.println((peopleMap.get(i)).toString());
//System.out.println("ID Number: "+(peopleMap.get(i).id)+tab+" Name: "+tab+peopleMap.get(i).name+tab+" Birthdate: "+peopleMap.get(i).birthdate);
run();
}
The commented code will work but the code calling the toString does not print what it should
Compare to method inside of my Person class:
public int compareTo(Object obj) {
Person o = (Person) obj;
if (this.id == o.id) { return 0; }
if (this.id > o.id) { return 1; }
if (this.id < o.id) { return -1; }
return 0;
I can provide more code if it's needed.
Compare by name method and it's output. Should I make an arrayList to store my values in and then sort it in that?
public void sortByName()
{
// char tab = '\t';
for(int j = 1; j<20; j++)
{
// System.out.println("ID Number: "+(names.get(j).id)+tab+" Name: "+tab+peopleMap.get(j).name+tab+" Birthdate: "+peopleMap.get(i).birthdate);
//Person p = names.get(j);
System.out.println(names.get(j).toString());
}
}
Output:
Person#10b30a7
Person#1a758cb
Person#1b67f74
Person#69b332
Person#173a10f
Person#530daa
Person#a62fc3
Person#89ae9e
Person#1270b73
Person#60aeb0
Person#16caf43
Person#66848c
Person#8813f2
Person#1d58aae
Person#83cc67
Person#e09713
Person#de6f34
Person#156ee8e
Person#47b480
Thanks
Well, I can't pinpoint the exact problem, I have a few suggestions.
Maps aren't sorted.
In general, an Map is not sorted, so you will not be able to sort the keys of the map. If you want to sort the Map use the SortedMap interface.
Use Generics when possible
The Comparable interface is generic. You should probably be implementing Comparable<Person>
Then your compareTo() method should look like this:
public int compareTo(Person p) {
if (this.id > p.id) return 1;
else if (this.id < p.id) return -1;
else return 0;
}
The difference between Comparator<Person> and Comparable<Person>
You need to take a look at the Comparator interface as well as the Comparable interface.
Your Person should implement comparable in that way that you usually want a person to be sorted. Then you should write some implementations of Comparator.
public classPersonNameComparator implements Comparator<Person> {
public int compare(Person p1, Person p2) {
return p1.name.compareTo(p2.name);
}
}
The importance of using the #Override annotation
It is important to always use the #Override annotation whenever you are trying to override a method of a super class or implement an interface method. The following are a few links regarding why this is a good idea:
Overriding the java equals() method quirk
When do you use Java's #Override annotation and why?
One issue that I see is that TreeMap sorts by key not by value. Your compareTo will not be used in the sorting of the tree since it is the value in the map. Since the key in the map is the id the the items in the tree should be sorted by the id of the person.
How do you know that the map isn't sorted? Can you show us some output that shows that it is not? Are you by any chance changing the ID of the Person after it gets put into the map?
Oh, and what is names compared to personMap? Also, are the ids really contiguous starting from 1? What does this code spit out:
for (Person person : peopleMap.values()) {
System.out.println(person);
}
did you use the #Override method to make sure that you are actually overriding the toString method? It looks like it is still printing out the default toString() (ie the value of the pointer to the object).
see : comparator API.
"The ordering imposed by a Comparator c on a set of elements S is said to be consistent with equals if and only if (compare((Object)e1, (Object)e2)==0) has the same boolean value as e1.equals((Object)e2) for every e1 and e2 in S."
I don't see an equals method in your Person class. The default implementation of equals compares identity. And if you override equals, you must define hashCode two.
And this question : Consistent Equals() results, but inconsistent TreeMap.containsKey() result
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
public class Person implements Comparable<Person> {
public final String name;
public final int id;
public final Date birthdate;
public Person(int id, String name, Date birthdate) {
this.id = id;
this.name = name;
this.birthdate = birthdate;
}
public static void main(String[] args) {
List<Person> list = new ArrayList<Person>();
for (int i = 10; i > 0; i--) {
list.add(new Person(i, "name" + String.valueOf(i), new Date()));
}
System.out.println(list);
Collections.sort(list);
System.out.println(list);
}
#Override
public boolean equals(Object other) {
if (!(other instanceof Person)) {
return false;
}
return this.id == ((Person)other).id;
}
#Override
public int hashCode() {
return 41 * id;
}
#Override
public String toString() {
return "Person<" + id + ">";
}
#Override
public int compareTo(Person other) {
if (!(other instanceof Person)) {
throw new IllegalArgumentException();
}
return this.id - ((Person)other).id;
}
}
Outputs :
[Person<10>, Person<9>, Person<8>, Person<7>, Person<6>, Person<5>, Person<4>, Person<3>, Person<2>, Person<1>]
[Person<1>, Person<2>, Person<3>, Person<4>, Person<5>, Person<6>, Person<7>, Person<8>, Person<9>, Person<10>]

Categories

Resources