I have 2 custom Java classes;
private MyCustomClass1 obj1;
private MyCustomClass2 obj2;
Each of them has multiple attributes as below;
MyCustomClass1 {
attr1,
attr2,
commonattrId,
attr3
}
MyCustomClass2 {
attr4,
attr5,
commonattrId,
attr6
}
So as you can see, there is a common attribute in each of them (commonattrId) which just to add is a Long
There is also a composite class defined as below;
MyCompositeClass {
MyCustomClass1 obj1;
MyCustomClass2 obj2;
}
Now one of my query execution returns below list;
List myList1
and there is another query execution which returns me below list;
List myList2
My question is can I combine the above 2 lists given I have a commonattrId ?
slightly long but the idea is to override equals in MyClass1 and MyClass2:
public static void main(String[] args) throws IOException {
List<MyClass1> myClass1s = new ArrayList<MyClass1>();
myClass1s.add(new MyClass1(1, 1));
myClass1s.add(new MyClass1(2, 2));
List<MyClass2> myClass2s = new ArrayList<MyClass2>();
myClass2s.add(new MyClass2(3, 1));
myClass2s.add(new MyClass2(4, 2));
List<MyComposite> allMyClasses = new ArrayList<MyComposite>();
for(MyClass1 m : myClass1s) { // note: you should take the shorte of the two lists
int index = myClass2s.indexOf(m);
if(index != -1) {
allMyClasses.add(new MyComposite(m, myClass2s.get(index)));
}
}
System.out.println(allMyClasses);
}
static class MyClass1 {
int attr1;
long commonAttrId;
public MyClass1(int attr, long commonAttr) {
this.attr1 = attr;
this.commonAttrId = commonAttr;
}
#Override
public int hashCode() {
int hash = 5;
hash = 83 * hash + (int) (this.commonAttrId ^ (this.commonAttrId >>> 32));
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if(obj instanceof MyClass2) {
return this.commonAttrId == ((MyClass2)obj).commonAttrId;
}
if(obj instanceof MyClass1) {
return this.commonAttrId == ((MyClass1)obj).commonAttrId;
}
return false;
}
#Override
public String toString() {
return "attr1=" + attr1 + ", commonAttrId=" + commonAttrId;
}
}
static class MyClass2 {
int attr2;
long commonAttrId;
public MyClass2(int attr, long commonAttr) {
this.attr2 = attr;
this.commonAttrId = commonAttr;
}
#Override
public int hashCode() {
int hash = 5;
hash = 83 * hash + (int) (this.commonAttrId ^ (this.commonAttrId >>> 32));
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if(obj instanceof MyClass1) {
return this.commonAttrId == ((MyClass1)obj).commonAttrId;
}
if(obj instanceof MyClass2) {
return this.commonAttrId == ((MyClass2)obj).commonAttrId;
}
return false;
}
#Override
public String toString() {
return "attr2=" + attr2 + ", commonAttrId=" + commonAttrId;
}
}
static class MyComposite {
MyClass1 myClass1;
MyClass2 myClass2;
public MyComposite(MyClass1 a, MyClass2 b) {
myClass1 = a;
myClass2 = b;
}
#Override
public String toString() {
return "myClass1=" + myClass1 + ", myClass2=" + myClass2;
}
}
I don't know all the parameters of your problem but there are probably better ways to do this. For example: have both MyClass1 and MyClass2 inherit from a common class (i.e. MyBaseClass) and create a collection of that instead of the composite class MyCompositeClass.
Or instead of Lists you could have sets and create a set intersection.
You could create a map from id to the object for one of the lists and then iterate through the other to create the new List using the data from the map.
List<MyCompositeClass> combine(List<MyCustomClass1> myList1, List<MyCustomClass2> myList2) {
// create map
Map<Long, MyCustomClass1> idToObj = new HashMap<>();
for (MyCustomClass1 o : myList1) {
idToObj.put(o.commonattrId, o);
}
// construct result list
List<MyCompositeClass> result = new ArrayList<>();
for (MyCustomClass2 o : myList2) {
MyCustomClass1 o1 = map.get(o.commonattrId);
if (o1 != null) {
MyCompositeClass combined = new MyCompositeClass();
combined.obj1 = o1;
combined.obj2 = o;
result.add(combined);
}
}
return result;
}
This will only add all possible combinations of objects from both lists, if commonattrId values are pairwise distinct in each list, but since the field name has "Id" as suffix, I made an educated guess...
Related
I have a test for testing that adding the same Edge (Arista) but with the same vertices (but flipped order) is the same (this is not a directed graph).
And this is strange because the two first assertions passes OK (adding Edge1 and Edge2 will result in edges.sizes = 1 because they are the same, theoretically).
But then when testing that edges.contains(Edge2) returns false.
Why could it have worked when testing addition (to not add it duplicated) but does not work when testing contains()?
This is the code:
#Test
public final void testAristaWithSameVerticesIsNotAddedTwice() throws Exception {
Grafo grafo = new Grafo();
Vertice vertice1 = new Vertice("Vertice 1");
Vertice vertice2 = new Vertice("Vertice 2");
grafo.agregarVertice(vertice1);
grafo.agregarVertice(vertice2);
Arista arista = new Arista(vertice1, vertice2, 10);
Arista arista2 = new Arista(vertice2, vertice1, 10);
grafo.agregarArista(arista);
grafo.agregarArista(arista);
assertEquals(1, grafo.getAristasQuantity());
assertTrue(grafo.hasArista(arista));
assertTrue(grafo.hasArista(arista2)); // fails here
}
Grafo class:
private HashSet<Arista> aristas;
public boolean hasArista(Arista arista) {
return this.aristas.contains(arista);
}
Arista class
package entities;
public class Arista {
protected Vertice vertice1;
protected Vertice vertice2;
protected int peso;
public Arista(Vertice vertice1, Vertice vertice2, int peso) {
this.vertice1 = vertice1;
this.vertice2 = vertice2;
this.peso = peso;
}
public Vertice getVertice1() {
return vertice1;
}
public Vertice getVertice2() {
return vertice2;
}
public int getPeso() {
return peso;
}
public void setPeso(int peso ) {
this.peso = peso;
}
public int hashCode() {
return vertice1.hashCode() + vertice2.hashCode();
}
public boolean equals(Arista arista) {
if (arista == this) {
return true;
}
if ((arista.getVertice1() == this.vertice1 && arista.getVertice2() == this.vertice2)
|| (arista.getVertice2() == this.vertice1 && arista.getVertice1() == this.vertice2)) {
return true;
}
return false;
}
}
I found out that the equals() wasn't overriding the parent definition because it was not well defined. So it wasn't being called.
Correct way is:
#Override
public boolean equals(Object object) {
if (object instanceof Arista) {
Arista arista = (Arista) object;
if (arista == this) {
return true;
}
if ((arista.getVertice1() == this.vertice1 && arista.getVertice2() == this.vertice2)
|| (arista.getVertice2() == this.vertice1 && arista.getVertice1() == this.vertice2)) {
return true;
}
}
return false;
}
I'm trying to make a generic tuple class. It stores its elements as an ArrayList. Of course, this class should override hashcode and equals methods.
How could I make hashcode method for this class? You see, in the code, I am having trouble.
Also, for the equals method, why does the compiler force me to use the '?'. Why couldn't I just use the T?
public static class Tuple<T> {
ArrayList<T> tuple = new ArrayList<>();
public Tuple(ArrayList<T> items) {
for (T item : items) {
tuple.add(item);
}
}
#Override
public int hashCode() {
T sum = ???;
for (T item : tuple) {
sum += item.hashCode();
}
return sum;
}
#Override
public boolean equals(Object o) {
if (o instanceof Tuple<?>) {
Tuple<?> tup= (Tuple<?>) o;
if (tup.tuple.size() != this.tuple.size()) {
return false;
}
for (int i = 0; i < this.tuple.size(); i++) {
if (this.tuple.get(i) != tup.tuple.get(i)) {
return false;
}
}
return true;
} else {
return false;
}
}
}
As mentioned in the comments, we should delegate the hashCode and the equals methods to the ArrayList<T> tuple instance variable. For the hashCode it's trivial. For the equals it's just a little more complicated than that because we don't want our custom Tuple to be equals with an ArrayList. So here it is:
public class Tuple<T> {
// I made this private because I'm pedantric ;)
private final ArrayList<T> tuple = new ArrayList<>();
// this does the same as your code, it's just easier to read
public Tuple(ArrayList<T> items) {
tuple.addAll(items);
}
#Override
public int hashCode() {
return tuple.hashCode();
}
// generated by eclipse
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Tuple other = (Tuple) obj;
if (tuple == null) {
if (other.tuple != null)
return false;
} else if (!tuple.equals(other.tuple))
return false;
return true;
}
}
If you want to deal with the case when the tuple can be null, then you can use a slightly more complex hashCode:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((tuple == null) ? 0 : tuple.hashCode());
return tuple.hashCode();
}
In general, I don't like to write these methods myself. Usually, I make my IDE to generate the stuff. All I need to take care of is to re-generate it when I add new fields. Apache HashCodeBuilder and EqualsBuilder are also great alternatives.
EDITED/SOLVED still looking for better answer
I do have a answer here How do I remove repeated elements from ArrayList? and it is working my question is relation to ONLY list and not Set. Since through out application flow list has been implemented and it is difficult for me to change all List references to Set reference as https://docs.oracle.com/javase/7/docs/api/java/util/List.html has get(..) Set doesn't have get(..).
Each object getting added is a new Instance.
hi i Have a model class
public final class MyClass implements Comparable<MyClass> {
public static final int APP = 0;
public static final int FILE = 1;
public static final int FOLDER = 2;
private String name;
private String path;
private String pkg;
private Long size;
private boolean selected;
private Integer type;
public MyClass(String name, String path, String pkg, Long size, boolean selected, int type) {
this.name = name;
this.path = path;
this.pkg = pkg;
this.size = size;
this.selected = selected;
this.type = type;
if (!TextUtils.isEmpty(path)) {
File file = new File(path);
if (file.exists()) {
this.size = file.length();
}
}
}
public String getName() {
return name;
}
public String getPath() {
return path;
}
public String getSize() {
return FileUtils.getReadableFileSize(size);
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
public Integer getType() {
return type;
}
public String getPkg() {
return pkg;
}
#Override
public String toString() {
return "{Pkg=" + pkg + ", Path=" + path + ", size=" + size + ", hashcode: " + hashCode() +"}";
}
#Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
MyClass myclass = (MyClass) o;
if (path != null ? !path.equals(myclass.path) : myclass.path != null)
return false;
if (pkg != null ? !pkg.equals(myclass.pkg) : myclass.pkg != null)
return false;
return size != null ? size.equals(myclass.size) : myclass.size == null;
}
#Override
public int hashCode() {
int result = path != null ? path.hashCode() : 0;
result = 31 * result + (pkg != null ? pkg.hashCode() : 0);
result = 31 * result + (size != null ? size.hashCode() : 0);
return result;
}
#Override
public int compareTo(MyClass myclass) {
return myclass.getType().compareTo(this.type);
}
}
But when i create common object, these object are getting added to list, even though their hashcode is same it is duplicate in list. Anything i may be doing wrong here.
Output of toString of List is as follows:
[{
Pkg = com.a.bc,
Path = /data/app / com.a.bc - 1 / base.apk,
size = 1800820,
hashcode: -908060882
}, {
Pkg = com.a.b.c,
Path = /data/app / com.a.b.c - 1 / base.apk,
size = 21279534,
hashcode: 1116685502
}, {
Pkg = com.a.b.c,
Path = /data/app / com.a.b.c - 1 / base.apk,
size = 21279534,
hashcode: 1116685502
}]
Here is some dirty implementation but this is not what i was looking for...
private final class DuplicateFilterArrayList<E> extends ArrayList<E> {
private DuplicateFilterArrayList() {
}
#Override
public boolean add(E object) {
if (object instanceof MyClass) {
if (!contains(object)) {
return super.add(object);
} else {
Logger.error(TAG, "Object already exists go home");
return false;
}
} else {
throw new IllegalArgumentException("Unsupported Object type " + object.getClass().getSimpleName());
}
}
#Override
public boolean contains(Object object) {
if (object instanceof MyClass) {
MyClass otherMyClassObjec = (MyClass) object;
for (E myClassItem : this) {
MyClass newMyClass = (MyClass) myClassItem;
if (newMyClass.equals(otherMyClassObjec) && newMyClass.hashCode() == otherMyClassObjec.hashCode()) {
return true;
}
}
}
return false;
}
}
Before adding to the list, check if the instance is already in the Collection by using the contains method on the Collection (List in this case). Then you can choose to not add again if it's already contained. It should work if you've correctly implemented the equals and hashCode methods.
Create your own class(MYArrayList) which extend ArrayList. Make return type as list itself. Rest all will be same.
List list = new MyArrayList
MyArrayList extends ArrayList<>
override add method of MyArrayList such that before adding MYClass in List its Check if this List contains it already or not.
But remember this will degrade the performance since it will iterate over all elements in list just to check duplicacy in insertion.
Here's the situation: I want to test 2 objects for uniqueness based on 2 different ID's. Example:
// Note I'm using JSON notation to keep things simple; the actual code
// is with Java Objects
// OBJECT A
{
main_id: 0,
id_a: 123,
id_b: 456
}
// OBJECT B
{
main_id: 1,
id_a: 123,
id_b: 456
}
// OBJECT C
{
main_id: 2,
id_a: 123,
id_b: 789
}
In the Example, Objects A and B are the same because id_a and id_b are the same, and Object C is different.
To determine this in the code, I'm planning on converting both ID's to a string and concatenating them together with a separator char in the middle (e.g., "{id_a},{id_b}"), then adding them to a Set<String> to test for uniqueness.
My question is, is there a better way? (By better, I mean more efficient and/or less kludgy)
If you want to use HashSet, you can override hashCode and equals to exclusively look at those two members.
Hash code: (31 is just a prime popularly used for hashing in Java)
return 31*id_a + id_b;
Equals: (to which you'll obviously need to add instanceof checks and type conversion)
return id_a == other.id_a && id_b == other.id_b;
If you don't want to bind these functions to the class because it's used differently elsewhere, but you still want to use HashSet, you could consider:
Creating an intermediate class to be stored in the set, which will contain your class as a member and implement the above methods appropriately.
Use your string approach
Use HashSet<Point> - Point is not ideal for non-coordinate purposes as the members are simply named x and y, but I do find it useful to have such a class available, at least for non-production code.
Alternatively, if you want to use TreeSet, you could have your class implement Comparable (overriding compareTo) or provide a Comparator for the TreeSet, both of which would compare primarily on the one id, and secondarily on the other.
The basic idea would look something like this:
if (objectA.id_a != objectB.id_a)
return Integer.compare(objectA.id_a, objectB.id_a);
return Integer.compare(objectA.id_b, objectB.id_b);
Not sure this is any more efficient or less kludgy. You could keep the original hashcode/equals using the main id (as per your comment) and then create a wrapper that has a hashcode/equals for the composite ida, idb. Maybe over the top for what you need though.
CompositeIdEntity.java
public interface CompositeIdEntity {
long getIdA();
long getIdB();
}
Entity.java
public class Entity implements CompositeIdEntity {
private final long mainId;
private final long idA;
private final long idB;
public Entity(long mainId, long idA, long idB) {
this.mainId = mainId;
this.idA = idA;
this.idB = idB;
}
#Override
public long getIdA() {
return idA;
}
#Override
public long getIdB() {
return idB;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (mainId ^ (mainId >>> 32));
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Entity other = (Entity) obj;
if (mainId != other.mainId)
return false;
return true;
}
#Override
public String toString() {
return "Entity [mainId=" + mainId + ", idA=" + idA + ", idB=" + idB
+ "]";
}
}
CompositeIdWrapper.java
public class CompositeIdWrapper {
private final CompositeIdEntity compositeIdEntity;
public CompositeIdWrapper(CompositeIdEntity compositeIdEntity) {
this.compositeIdEntity = compositeIdEntity;
}
public CompositeIdEntity getCompositeIdEntity() {
return compositeIdEntity;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ (int) (compositeIdEntity.getIdA() ^ (compositeIdEntity
.getIdA() >>> 32));
result = prime * result
+ (int) (compositeIdEntity.getIdB() ^ (compositeIdEntity
.getIdB() >>> 32));
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CompositeIdWrapper other = (CompositeIdWrapper) obj;
if (compositeIdEntity.getIdA() != other.compositeIdEntity.getIdA())
return false;
if (compositeIdEntity.getIdB() != other.compositeIdEntity.getIdB())
return false;
return true;
}
}
Test.java
import java.util.HashSet;
import java.util.Set;
public class Test {
public static void main(String[] args) {
Entity en1 = new Entity(0, 123, 456);
Entity en2 = new Entity(1, 123, 456);
Entity en3 = new Entity(2, 123, 789);
Entity en4 = new Entity(2, 123, 456);
Entity en5 = new Entity(1, 123, 789);
// Set based on main id
Set<Entity> mainIdSet = new HashSet<>();
mainIdSet.add(en1);
mainIdSet.add(en2);
mainIdSet.add(en3);
mainIdSet.add(en4);
mainIdSet.add(en5);
System.out.println("Main id set:");
for (Entity entity : mainIdSet) {
System.out.println(entity);
}
// Set based on ida, idb
Set<CompositeIdWrapper> compositeIdSet = new HashSet<>();
compositeIdSet.add(new CompositeIdWrapper(en1));
compositeIdSet.add(new CompositeIdWrapper(en2));
compositeIdSet.add(new CompositeIdWrapper(en3));
compositeIdSet.add(new CompositeIdWrapper(en4));
compositeIdSet.add(new CompositeIdWrapper(en5));
System.out.println("Composite id set:");
for (CompositeIdWrapper wrapped : compositeIdSet) {
System.out.println(wrapped.getCompositeIdEntity());
}
}
}
Output
Main id set:
Entity [mainId=1, idA=123, idB=456]
Entity [mainId=2, idA=123, idB=789]
Entity [mainId=0, idA=123, idB=456]
Composite id set:
Entity [mainId=0, idA=123, idB=456]
Entity [mainId=2, idA=123, idB=789]
See this, Here I override the equals() and hashcode() to ensure uniqueness on "name" field of a Person object
public class SetObjectEquals {
Person p1 = new Person("harley");
Person p2 = new Person("harley");
public void method1() {
Set<Person> set = new HashSet<Person>();
set.add(p1);
set.add(p2);
System.out.println(set);
}
public static void main(String[] args) {
SetObjectEquals obj = new SetObjectEquals();
obj.method1();
}
}
class Person {
String name;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.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 (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
Person(String name) {
this.name = name;
}
}
This is my class:
public class MultiSet<E> extends AbstractCollection<E>
{
private int size = 0;
private Map<E, Integer> values = new HashMap<E, Integer>();
public MultiSet()
{
}
public MultiSet(Collection<E> c)
{
addAll(c);
}
#Override
public boolean add(E o)
{
throw new UnsupportedOperationException();
}
#Override
public boolean remove(Object o)
{
throw new UnsupportedOperationException();
}
public Iterator<E> iterator()
{
return new Iterator<E>()
{
private Iterator<E> iterator = values.keySet().iterator();
private int remaining = 0;
private E current = null;
public boolean hasNext()
{
return remaining > 0 || iterator.hasNext();
}
public E next()
{
if (remaining == 0)
{
remaining = values.get(current);
}
remaining--;
return current;
}
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
public boolean equals(Object object)
{
if (this == object) return true;
if (this == null) return false;
if (this.getClass() != object.getClass()) return false;
MultiSet<E> o = (MultiSet<E>) object;
return o.values.equals(values);
}
public int hashCode()
{
return values.hashCode()*163 + new Integer(size).hashCode()*389;
}
public String toString()
{
String res = "";
for (E e : values.keySet());
//res = ???;
return getClass().getName() + res;
}
public int size()
{
return size;
}
}
So basically, i need to implement my add/remove-methods correctly, to add or remove elements to/from the Set.
To me, it seems like my equals is correct, but Eclipse says that in the line:
MultiSet<E> o = (MultiSet<E>) object;
there is an unchecked cast from object to Multiset<E>
Any thoughts?
Also, in my toString method, i'm not 100% sure how to define "res"?
Thanks,
// Chris
use this instead:
MultiSet<?> o = (MultiSet<?>) object;
this is necessary due to how generics are implemented in java.