I have two objects of same type.
Class A {
String a;
List b;
int c;
}
A obj1 = new A();
A obj2 = new A();
obj1 => {a = "hello"; b = null; c = 10}
obj2 => {a = null; b = new ArrayList(); c = default value}
Can you please let me know what is the best way to combine this objects into single object?
obj3 = {a = "hello"; b = (same arraylist from obj2); c = 10}
This works as long as you have POJOs with their own getters and setters. The method updates obj with non-null values from update. It calls setParameter() on obj with the return value of getParameter() on update:
public void merge(Object obj, Object update){
if(!obj.getClass().isAssignableFrom(update.getClass())){
return;
}
Method[] methods = obj.getClass().getMethods();
for(Method fromMethod: methods){
if(fromMethod.getDeclaringClass().equals(obj.getClass())
&& fromMethod.getName().startsWith("get")){
String fromName = fromMethod.getName();
String toName = fromName.replace("get", "set");
try {
Method toMetod = obj.getClass().getMethod(toName, fromMethod.getReturnType());
Object value = fromMethod.invoke(update, (Object[])null);
if(value != null){
toMetod.invoke(obj, value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
I am using Spring Framework. I was facing the same issue on a project.
To solve it i used the class BeanUtils and the above method,
public static void copyProperties(Object source, Object target)
This is an example,
public class Model1 {
private String propertyA;
private String propertyB;
public Model1() {
this.propertyA = "";
this.propertyB = "";
}
public String getPropertyA() {
return this.propertyA;
}
public void setPropertyA(String propertyA) {
this.propertyA = propertyA;
}
public String getPropertyB() {
return this.propertyB;
}
public void setPropertyB(String propertyB) {
this.propertyB = propertyB;
}
}
public class Model2 {
private String propertyA;
public Model2() {
this.propertyA = "";
}
public String getPropertyA() {
return this.propertyA;
}
public void setPropertyA(String propertyA) {
this.propertyA = propertyA;
}
}
public class JustATest {
public void makeATest() {
// Initalize one model per class.
Model1 model1 = new Model1();
model1.setPropertyA("1a");
model1.setPropertyB("1b");
Model2 model2 = new Model2();
model2.setPropertyA("2a");
// Merge properties using BeanUtils class.
BeanUtils.copyProperties(model2, model1);
// The output.
System.out.println("Model1.propertyA:" + model1.getPropertyA(); //=> 2a
System.out.println("Model1.propertyB:" + model1.getPropertyB(); //=> 1b
}
}
Maybe something like
class A {
String a;
List<..> b;
int c;
public void merge(A other) {
this.a = other.a == null ? this.a : other.a;
this.b.addAll(other.b);
this.c = other.c == 0 ? this.c : other.c;
}
}
A a1 = new A();
A a2 = new A();
a1.a = "a prop";
a2.c = 34;
a1.merge(a2);
A.merge might return a new A object instead of modifing current.
Just accommodating boolean sync. and case sensitive(camel notation)
public boolean merge(Object obj){
if(this.equals(obj)){
return false;
}
if(!obj.getClass().isAssignableFrom(this.getClass())){
return false;
}
Method[] methods = obj.getClass().getMethods();
for(Method fromMethod: methods){
if(fromMethod.getDeclaringClass().equals(obj.getClass())
&& (fromMethod.getName().matches("^get[A-Z].*$")||fromMethod.getName().matches("^is[A-Z].*$"))){
String fromName = fromMethod.getName();
String toName ;
if(fromName.matches("^get[A-Z].*")){
toName = fromName.replace("get", "set");
}else{
toName = fromName.replace("is", "set");
}
try {
Method toMetod = obj.getClass().getMethod(toName, fromMethod.getReturnType());
Object value = fromMethod.invoke(this, (Object[])null);
if(value != null){
toMetod.invoke(obj, value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
return true;
}
If you create getters and setters for the attributes, you can use the copyProperties method from Commons BeanUtils.
Add this method to your POJO, then use it like myObject.merge(newObject). It uses generics to loop through your POJO's fields, so you don't mention any field names:
/**
* Fill current object fields with new object values, ignoring new NULLs. Old values are overwritten.
*
* #param newObject Same type object with new values.
*/
public void merge(Object newObject) {
assert this.getClass().getName().equals(newObject.getClass().getName());
for (Field field : this.getClass().getDeclaredFields()) {
for (Field newField : newObject.getClass().getDeclaredFields()) {
if (field.getName().equals(newField.getName())) {
try {
field.set(
this,
newField.get(newObject) == null
? field.get(this)
: newField.get(newObject));
} catch (IllegalAccessException ignore) {
// Field update exception on final modifier and other cases.
}
}
}
}
}
There is a dynamic solution to merge any two objects which require Reflection and Recursion.
public <T> T merge(T local, T remote, ArrayList<String> listOfClass)
throws IllegalAccessException, InstantiationException {
Class<?> clazz = local.getClass();
Object merged = clazz.newInstance();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
Object localValue = field.get(local);
Object remoteValue = field.get(remote);
if (localValue != null) {
if (listOfClass.contains(localValue.getClass().getSimpleName())) {
field.set(merged, this.merge(localValue, remoteValue, listOfClass));
} else {
field.set(merged, (remoteValue != null) ? remoteValue : localValue);
}
} else if (remoteValue != null) {
field.set(merged, remoteValue);
}
}
return (T) merged;
}
Variable Description:
local: The object on to which the other will be merged
remote: The object which will be merged to the local object
listOfClass: The ArrayList of custom classes in the given object
The function returns a merged object which is good to go.
Kudos! :)
In your very special case it looks like you want a new object that takes the real values from both instances. Here is an implementation that will do that. The method should be add to class A so that it can access the fields.
public A specialMergeWith(A other) {
A result = new A();
result.a = (a == null ? other.a : a);
result.b = (b == null ? other.b : b);
result.c = (c == DEFAULT_VALUE ? other.c : c);
return result;
}
public static Object mergeObjects(Object source, Object target) throws Exception {
Field[] allFields = source.getClass().getDeclaredFields();
for (Field field : allFields) {
if(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())){
continue;
}
if (!field.isAccessible() && Modifier.isPrivate(field.getModifiers()))
field.setAccessible(true);
if (field.get(source) != null) {
field.set(target, field.get(source));
}
}
return target;
}
Using java reflection, support only for the same class.
Related
Is there a nice way to iterate over object fields using reflection?
The main problem is in object can be another object therefore it's needed to iterate over another object's properties too.
For example I have AllInclusiveDetails object
public class AllInclusiveDetails {
#JsonProperty("renters_business")
private String rentersBusiness;
#Valid
#NotNull
#JsonProperty("main_property_owner_details")
private ShortCustomer mainPropertyOwnerDetails;
#Valid
#NotNull
#JsonProperty("main_tenant_details")
private ShortCustomer mainTenantDetails;
}
And ShortCustomer is
public class ShortCustomer {
#NotNull
#Positive
private Long id;
#NotEmpty
#JsonProperty("full_name")
private String fullName;
#JsonProperty("organization_number")
private String organizationNumber;
#PastOrPresent
private LocalDate birthdate;
}
I want to iterate over AllInclusiveDetails object fields using reflection and if there is another object in it , I want to iterate over that object fields too.
The main purpose is to track if value of the same field in two different object are equal or not and if not to save old value and the new one.
Does this satisfy your requirement:
for(Field field : AllInclusiveDetails.class.getDeclaredFields()) {
if(field.getType()== ShortCustomer.class) {
//Do your logic here
}
}
Here's a way to get all the fields of a class and a method to recursively use reflection to compare fields. Play around with main to test it, it will not work correctly for Objects that are logically equivalent but not the same in memory.
// Gathers all fields of this class, including those in superclasses, regardless of visibility
public static List<Field> getAllFields(Class<?> klass) {
List<Field> fields = new ArrayList<>();
for (Class<?> k = klass; k != null; k = k.getSuperclass()) {
fields.addAll(Arrays.asList(k.getDeclaredFields()));
}
return fields;
}
// Uses reflection and recursion to deep compare two objects.
// If the sub-fields and sub-arrays are not deeply equal this will return false.
// This will cause problems with data structures that may be logically equivalent
// but not have the same structure in memory, HashMaps and Sets come to mind
//
// Also might perform illegal reflective access which gets a warning from the JVM
// WARNING: Illegal reflective access ... to field java.util.LinkedList.size
// WARNING: Please consider reporting this to the maintainers ...
// WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
// WARNING: All illegal access operations will be denied in a future release
public static <T> boolean reflexiveEquals(T o1, T o2) {
return reflexiveEquals(o1, o2, new HashSet<>(), new HashSet<>());
}
private static <T> boolean reflexiveEquals(T o1, T o2, Set<Object> o1Refs, Set<Object> o2Refs) {
if (o1 == o2) {
// exact same object or both are null
return true;
}
if (o1 == null || o2 == null) {
// one is null but the other is not
System.err.println(o1 + " != " + o2);
return false;
}
Class<?> type = o1.getClass();
if (type != o2.getClass()) {
// not the exact same class therefore not equal
// you could treat this differently if you want
System.err.println(type + " != " + o2.getClass());
return false;
}
if (PRIMITIVE_WRAPPERS.contains(type)) {
// if it's a primitive wrapper then compare plainly
boolean result = Objects.equals(o1, o2);
if (!result) {
System.err.println("Objects.equals: " + o1 + " : " + o2);
}
return result;
}
// before descending, make sure there wont be an infinite loop
// if this object appeared in the reference chain before
// then it is currently being compared lower in the stack,
// return true to let it finish it's comparison
if (o1Refs.contains(o1) || o2Refs.contains(o2)) {
return true;
}
try {
// keep track of the objects that have been descended into
o1Refs.add(o1);
o2Refs.add(o2);
if (type.isArray()) {
// if its an array, compare all elements
try {
Object[] a1 = (Object[]) o1;
Object[] a2 = (Object[]) o2;
// only comparable field besides elements
if (a1.length != a2.length) {
System.err.println("Array length diff");
return false;
}
for (int i = 0; i < a1.length; i++) {
if (!reflexiveEquals(a1[i], a2[i], o1Refs, o2Refs)) {
return false;
}
}
return true;
} catch (Exception e) {
return false;
}
}
// otherwise its some other object so compare all fields
// moving up the super-classes as well
for (Class<?> k = type; k != null; k = k.getSuperclass()) {
for (Field f : k.getDeclaredFields()) {
try {
f.setAccessible(true);
if (!reflexiveEquals(f.get(o1), f.get(o2), o1Refs, o2Refs)) {
return false;
}
} catch (IllegalArgumentException | IllegalAccessException e) {
return false;
}
}
}
return true;
} finally {
// remove the references since their compare is complete
o1Refs.remove(o1);
o2Refs.remove(o2);
}
}
private static final Set<Class<?>> PRIMITIVE_WRAPPERS = getPrimitiveWrapperClasses();
private static final Set<Class<?>> getPrimitiveWrapperClasses() {
Set<Class<?>> set = new HashSet<>();
set.add(Boolean.class);
set.add(Character.class);
set.add(Byte.class);
set.add(Short.class);
set.add(Integer.class);
set.add(Long.class);
set.add(Float.class);
set.add(Double.class);
set.add(Void.class);
return set;
}
public static class AllInclusiveDetails {
private String rentersBusiness;
private ShortCustomer mainPropertyOwnerDetails;
private ShortCustomer mainTenantDetails;
private ShortCustomer[] arr;
private List<ShortCustomer> list;
}
public static class ShortCustomer {
private Long id;
private String fullName;
private String organizationNumber;
private LocalDate birthdate;
}
public static void main(String[] args) {
AllInclusiveDetails aids1 = new AllInclusiveDetails();
aids1.rentersBusiness = "Business";
aids1.mainTenantDetails = new ShortCustomer();
aids1.mainTenantDetails.id = 1L;
aids1.mainTenantDetails.fullName = "John Doe";
aids1.arr = new ShortCustomer[] {
aids1.mainTenantDetails,
aids1.mainPropertyOwnerDetails };
aids1.list = new LinkedList<>(Arrays.asList(aids1.arr));
AllInclusiveDetails aids2 = new AllInclusiveDetails();
aids2.rentersBusiness = "Business";
aids2.mainTenantDetails = new ShortCustomer();
aids2.mainTenantDetails.id = 1L;
aids2.mainTenantDetails.fullName = "John Doe";
aids2.arr = new ShortCustomer[] {
aids2.mainTenantDetails,
aids2.mainPropertyOwnerDetails };
aids2.list = new LinkedList<>(Arrays.asList(aids2.arr));
System.out.println(reflexiveEquals(aids1, aids2));
}
I have a method from which I need to return two values.I m confused as to how can I return two values.
public List<Class1> getCode(Long Code)
{
String Query1="Some Query";
List<Object[]> value = repos.getQuery(Query1);
List<Class1> counts = new ArrayList<>();
if (null != value)
{
Iterator<Object[]> rowItr = value.iterator();
while (rowItr.hasNext())
{
Class1 count = new Class1();
Object[] obj = rowItr.next();
if (null != obj)
{
if (null != obj[0])
{
count.setValuess1(obj[0].toString());
}
if (null != obj[1])
{
count.setValuess2(obj[1].toString());
}
}
counts.add(count);
return (List<Class1>) counts;
}
String Query2="SomeQuery" ;
List<Object[]> value2 = repos.getQuery(Query2);
List<Class2> count1s = new ArrayList<>();
if (null != value2)
{
Iterator<Object[]> rowItr1 = value2.iterator();
while (rowItr.hasNext())
{
Class2 countt = new Class2();
Object[] obj1 = rowItr1.next();
if (null != obj1)
{
if (null != obj1[0])
{
countt.setValuess1(obj1[0].toString());
}
if (null != obj1[1])
{
countt.setValuess2(Long.valueOf(obj1[1].toString()));
}
}
count1s.add(countt);
}
}
return (List<Class2>)count1s;
}
}
This is my Class1
public class1
{
private String valuess1;
private String valuess2;
private List<Class2>class2;
}
This is My Class2
public class Class2
{
private String valuess1;
private Long valuess2;
}
How can I return count1s and counts together .I have tried returning the value by the use of casting but it does not accept it.I have seen quiet a few solutions but none of them has worked for me.Any help would be appreciated.
You can return a Pair.
Pair<List<Class1>,List<Class2>> res = new Pair(counts, count1s);
return res;
Or you can create a class that represents the return values and return it.
public class Res {
public List<Class1> l1;
public List<Class2> l2;
public Res(List<Class1> l1, List<Class2> l2){
this.l1 = l1;
this.l2 = l2;
}
}
Whenever you want to return more than one values, you better return an array holding values you want, so when you call you can initialize the returned array and the loop through it. Hope that helps
I am trying to create a base abstract class for unit testing. It is very easy to do this in C# but couldn't in java. My idea is that I will have a TestFor class which is to be used as base for unit test. T represents the type under test. In this class I want to create the The object of type T with all its parameters of longest constructor MOCKED. That mean I have to reflect the class, get the longest constructor, pull out the parameters, create mock of this parameter and then create the object of type T. I have the following code but not working. Anyone who can try
public abstract class TestFor<T> {
protected Class<T> _class = null;
public HashMap<Class, Class<?>> _mocks = new HashMap<Class, Class<?>>();
protected T Target = null;
protected TestFor(Class<T> cls) {
_class = cls;
Constructor<T>[] allConstructors = (Constructor<T>[]) _class.getDeclaredConstructors();
Constructor<T> ctorWithLongestParameter = null;
int max = 0;
for (Constructor ctor : allConstructors) {
if (ctor.getParameterTypes().length > max) {
ctorWithLongestParameter = ctor;
max = ctor.getParameterTypes().length;
}
}
final List<Object> objects = new ArrayList<Object>();
int i = 0;
for (Class<?> p : ctorWithLongestParameter.getParameterTypes()) {
Class<?> mock = Mockito.mock(p.getClass()); //This does not work
_mocks.put(p.getClass(), mock);
objects.add(mock);
}
try {
Target = (T) ctorWithLongestParameter.newInstance(objects);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public <E> E the(Class clss) {
return (E) _mocks.get(clss);
}
}
There is a several bugs in your code - logic, types, generics... Try this instead:
public abstract class TestFor<T> {
protected Class<T> _class = null;
public Map<Class, Object> _mocks = new HashMap<>();
protected T Target = null;
protected TestFor(Class<T> cls) {
_class = cls;
List<Constructor> allConstructors = Arrays.asList(_class.getDeclaredConstructors());
Constructor ctorWithLongestParameter = Collections.max(allConstructors,
(o1, o2) -> Integer.compare(o1.getParameterCount(), o2.getParameterCount()));
List<Object> objects = new ArrayList<>();
int i = 0;
for (Class<?> type : ctorWithLongestParameter.getParameterTypes()) {
Object mock = _mocks.get(type);
if (mock == null) {
mock = Mockito.mock(type);
_mocks.put(type, mock);
}
objects.add(mock);
}
try {
Target = _class.cast(ctorWithLongestParameter.newInstance(objects.toArray(new Object[objects.size()])));
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
public <E> E the(Class<E> cls) {
return cls.cast(_mocks.get(cls));
}
public static void main(String[] args) {
TestFor<A> test = new TestFor<A>(A.class) {};
System.out.println(test.Target);
System.out.println(test.the(Object.class));
System.out.println(test.the(Number.class));
}
public static class A {
public A() {
System.out.println("Empty constructor");
}
public A(Object o) {
System.out.println("Constructor [o=" + o + ']');
}
public A(Object o, Number n) {
System.out.println("Constructor [o=" + o + ", n=" + n + ']');
}
}
}
This code works with Java 8, however after small modifications it will work on the elder versions.
If a Person without an Address is persisted and loaded later on, Person contains an Address with all fields set to null.
This (modified) example was taken from coderanch, where a similar problem was reported.
#Embeddable
public class Address {
private String street;
private String postalCode;
}
#Entity
public class Person {
private String name;
#Embedded
private Address home;
}
How can I circumvent this problem? Is is possible to instruct Hibernate not to instantiate an #Embedded object, if all fields are null?
Changing the getters for every #Embedded field seems cumbersome and error prone. Another cumbersome alternative would be the use of #PostLoad, but this is called just for #Entitys, not for #Embeddables.
well, I wrote something which helped in my case. I added it with #PostLoad to #Entitys with #Embeddables:
public class NullableEmbeddedCleanerImpl implements NullableEmbeddedCleaner {
private final static Logger LOGGER = LoggerFactory.getLogger(NullableEmbeddedCleaner.class);
Map<Class<?>, Predicate<Object>> classFilterForNullables = new HashMap<>();
Map<Class<?>, Predicate<Object>> collectionMap = new HashMap<>();
Set<Class<?>> primitiveArrayClasses = new HashSet<Class<?>>();
public NullableEmbeddedCleanerImpl() {
fillPredicates();
fillCollectionMap();
fillPrimitiveArrayClasses();
}
/**
* B C D F I J S Z
*/
private void fillPrimitiveArrayClasses() {
try {
primitiveArrayClasses.addAll(Arrays.asList(Class.forName("[B"), Class.forName("[B"), Class.forName("[C"),
Class.forName("[D"), Class.forName("[F"), Class.forName("[I"), Class.forName("[J"), Class.forName("[S"),
Class.forName("[Z")));
} catch (ClassNotFoundException e) {
LOGGER.error("Class not found", e);
}
}
#SuppressWarnings("unchecked")
private void fillCollectionMap() { // misses Lists, Maps, ...
collection(Set.class, s -> {
final Set<Object> toRemove = new HashSet<>();
for (Object i : s) {
try {
if (clean(i)) {
toRemove.add(i);
}
} catch (Exception e) {
LOGGER.warn("Error cleaning embeddable {} : {}", e, i);
}
}
s.removeAll(toRemove);
return s.isEmpty();
});
}
#Override
public final void embeddables(Object... arg) {
for (Object i : arg) {
try {
clean(i);
} catch (IllegalArgumentException | IllegalAccessException e) {
LOGGER.warn("Error cleaning embeddable {} : {}", e, i);
}
}
}
#Override
public final boolean clean(Object arg) throws IllegalArgumentException, IllegalAccessException {
if (arg == null) {
return true;
}
boolean cleanThis = true;
Vector<Field> fields = new Vector<>();
for (Class<?> clazz = arg.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
}
for (Field field : fields) {
if (!fieldLoop(field, arg)) {
cleanThis = false;
}
}
return cleanThis;
}
private boolean fieldLoop(Field field, Object arg) throws IllegalArgumentException, IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) { // skip static fields
return true;
}
field.setAccessible(true);
Object fieldValue = field.get(arg);
if (fieldValue == null) {
return true;
}
Class<?> fieldsClass = field.getType();
if (fieldsClass.isPrimitive() || fieldsClass.isEnum()) {
return false; // can not clean primitives nor enums
}
if (fieldsClass.isArray()) {
if (primitiveArrayClasses.contains(fieldsClass)) {
return false; // ignore primitive arrays
} else {
throw new UnsupportedOperationException("Do something useful here"); // object
// arrays
}
}
for (Class<?> clazz : collectionMap.keySet()) {
if (clazz.isAssignableFrom(fieldsClass)) {
boolean emptyCollection = collectionMap.get(clazz).test(fieldValue);
if (!emptyCollection) {
return false;
} else {
field.set(arg, null);
}
return true;
}
}
// test primitives. just classes, no interfaces >>
for (Class<?> fieldClass = fieldsClass; fieldClass != null; fieldClass = fieldClass.getSuperclass()) {
if (classFilterForNullables.containsKey(fieldClass)) {
Predicate<Object> handle = classFilterForNullables.get(fieldClass);
if (handle.test(fieldValue)) {
return true;
} else {
return false;
}
}
}
if (clean(field.get(arg))) { // decent to contained objects
field.set(arg, null);
} else {// non clean-able child exists
return false;
}
return true;
}
private void fillPredicates() {
nullableFilters(String.class, Boolean.class, Character.class, Byte.class, Short.class, Integer.class, Long.class,
Float.class, Double.class, Void.class, LocalDateTime.class, LocalDate.class, LocalTime.class,
OffsetDateTime.class, OffsetTime.class);
classFilterForNullables.put(NullableEmbeddedCleanerImpl.class, n -> true); // always
// filter
}
private void nullableFilters(Class<?>... cs) {
for (Class<?> c : cs) {
classFilterForNullables.put(c, o -> false);
}
}
#SuppressWarnings("unchecked")
final private <T> void collection(Class<T> c, Predicate<T> fun) {
collectionMap.put((Class<?>) c, ((Predicate<Object>) fun));
}
}
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...