I have list (array list)that can contain many instances (between 500-3000 instances ).
during the program some function need to access to this list (many times)and search for specific instance or more ,to get the instance\s they need loop on the list and provide parentName and name (which is string) and are not uniqe key .
my question is since the list need to be accessed many time there is a way to define/design it better that the access to the list can be more efficient?
Please keep in mind that the functions that need to get instance/s from the list
cannot provide full key the can provide only name and parentName which can have more that one instance.
List<Obj> myList = new ArrayList<Obj>();
class obj
{
parentName
Name
type
curr
....
Use a Map<MyEntry, List<Obj>> where MyEntry is a class enclosing parent name and name as such:
public final class MyEntry
{
private final String parentName;
private final String name;
private final int hashCode;
public MyEntry(final String parentName, final String name)
{
this.parentName = parentName;
this.name = name;
hashCode = 31 * parentName.hashCode() + name.hashCode();
}
// Override .equals() and .hashCode()
#Override
public int hashCode()
{
return hashCode;
}
#Override
public boolean equals(final Object o)
{
if (this == o)
return true;
if (o == null)
return false;
if (getClass() != o.getClass())
return false;
final MyEntry other = (MyEntry) o;
return parentName.equals(other.parentName)
&& name.equals(other.name);
}
// Have a nice string representation
#Override
public String toString()
{
return "parent name: " + parentName + ", name: " + name;
}
}
You can, for instance, have a method in your Obj which returns the matching MyEntry object. Also, if you use Guava, have a look at MultiMap.
You will notice that the hash code is precomputed: this can be done since the MyEntry class is immutable. This allows for very fast usage as keys for a Map.
(edit: added .toString())
Related
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.
I have a class name "Users" and have 2 elements (int)userId and (String)userName.
Let's said
Users obj1 = new Users(10, "User1");
Users obj2 = new Users(11, "User2");
So I want to compare obj1 to obj2
element by element
10 compare to 11,
"User1" compare to "User2".
From the research i do from web. It looks like impossible to do it whether convert it to 2d array to compare or whatever method. Is there any method to do this kind of things?
I actually want to do an audit trail so i have the object before changes and after changes, so whatever element that have changed will insert a new record in the audit_trail table with the before value and after value.
I'm a newbie to programming i tried my best to think a way but it just doesn't work. Is there any other way of doing this by SQL? i using ng-admin as (front-end) and API java http to do a update (back-end).
You need to implement the Comparable<Users> interface. If you want equality check too, then you have to override
boolean equals(Object)
and
int hashCode()
Read:
https://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html
and
Why do I need to override the equals and hashCode methods in Java?
From your question, We can compare two different objects.
Please implement the equals method to do your operations available in Comparable<Users>.
Let's say as a example below,
Class obj1 = new Class(1, "raja");
Class obj2 = new Class(2, "thiru");
The id and name are a public variable of the class. Then
override the function as,
public boolean equals(Object obj)
{
return (this.id == obj.id && this.name.equals(obj.name.equals));
}
Thanks.
You should override the .equals() method, making your Users class as follows:
public class Users {
private int mId;
private String mName;
public Users(int pId, String pName) {
mId = pId;
mName = pName;
}
public int getId() {
return mId;
}
#Override
public boolean equals(Object pObject) {
return (pObject instanceof Users && ((Users) pObject).getId() == mId);
}
}
I'd probably create a BeanDelta object
public class PropertyDelta {
private String propertyName;
private Object value1;
private Object value2;
// constructor & getters
}
public class BeanDelta<T> {
private Class<T> type;
private List<PropertyDelta> propertyDeltas = new ArrayList<>();
public BeanDelta(Class<T> type) {
this.type = type;
}
// getters
}
Then you could write a reflection based method
public <T> BeanDelta<T> getDelta(T o1, T, o2) {
Class<T> type = o1.getClass();
Method[] methods = type.getMethods();
BeanDelta<T> delta = new BeanDelta<>(type);
for (Method meth : methods) {
boolean isGetter = method.getParameterTypes().length == 0 && !method.getReturnType().equals(void.class) && meth.getName().startsWith("get");
if (isGetter) {
Object v1 = meth.invoke(o1);
Object v2 = meth.invoke(o2);
if (!Objects.equal(v1, v2)) {
String propertyName = meth.getName().substring(3);
delta.propertyDeltas.add(new PropertyDelta(propertyName, v1, v2));
}
}
}
return delta;
}
Check it out the solution proposed for do that.
http://www.codejava.net/java-core/collections/sorting-a-list-by-multiple-attributes-example
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").)
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.
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.