Below is my code (can be copy paste in https://www.compilejava.net/ with -ea as command line option).
I have an Object called Main. I have Main inside a List. If 2 properties (a and b) are equal to another Main object in the list, property strings should be concatenated. Furthermore, duplicates (when the 2 properties are equal) should than be removed (so the list can not contain 2 or more Mains in which both a and b are the same).
I tried it with a HashMap, hashCode, but I can not figure it out well. Note: I use OpenJDK-12 and can not use newer versions.
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class Main {
final int a;
final int b;
final List<String> strings;
Main(int a, int b, List<String> strings) {
this.a = a;
this.b = b;
this.strings = strings;
}
private static Main generateMain0() {
return new Main(0, 1, createListWithOneElement("merge me with main1"));
}
public static void main(String[] args) {
Main main0 = generateMain0();
Main main1 = new Main(0, 1, createListWithOneElement("merge me with main2"));
Main main2 = new Main(0, 2, createListWithOneElement("leave me alone"));
Main main3 = new Main(0, 2, createListWithOneElement("leave me alone also"));
List<Main> mains = new ArrayList<>();
mains.add(main0);
mains.add(main1);
mains.add(main2);
mains.add(main3);
// Do magic here to remove duplicate and concat property strings
// main1 should be removed, since property a and b were equal to main0 property a and b
assert mains.size() == 3;
Main main0Copy = generateMain0();
main0Copy.strings.add("merge me with main2");
// The first element should be main0. It should also contain
// the strings of main1 since property a and b were equal
assert mains.get(0).equals(main0Copy);
assert mains.get(1).equals(main2);
assert mains.get(2).equals(main3);
}
private static List<String> createListWithOneElement(String value) {
List<String> l = new ArrayList<>();
l.add(value);
return l;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Main main = (Main) o;
return a == main.a &&
b == main.b &&
strings.equals(main.strings);
}
#Override
public int hashCode() {
return Objects.hash(a, b, strings);
}
}
If, as you said in the comments, you can use a fully custom List, you can try the code below.
Internally, it uses a combination of a List and a Map to find out if a combination of a and b was already added to the "List". If yes, it adds all strings of the given Main to the existing Main. If not, it adds the given Main the the list.
package example;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MainList {
private final List<Main> mains;
private final Map<Key, Main> lookup;
public MainList() {
this.mains = new ArrayList<>();
this.lookup = new HashMap<>();
}
public Main get(int index) {
return this.mains.get(index);
}
public void add(Main main) {
final Key key = new Key(main.a, main.b);
Main existingMain = this.lookup.get(key);
if (existingMain == null) {
this.mains.add(main);
this.lookup.put(key, main);
} else {
existingMain.strings.addAll(main.strings);
}
}
public void remove(Main main) {
final Key key = new Key(main.a, main.b);
Main existingMain = this.lookup.get(key);
if (existingMain != null) {
if (existingMain.equals(main)) {
this.mains.remove(existingMain);
this.lookup.remove(key);
} else {
existingMain.strings.removeAll(main.strings);
}
}
}
public void remove(int index) {
final Main removedMain = this.mains.remove(index);
final Key key = new Key(removedMain.a, removedMain.b);
this.lookup.remove(key);
}
public int size() {
return this.mains.size();
}
private static class Key {
private final int a;
private final int b;
private Key(int a, int b) {
this.a = a;
this.b = b;
}
#Override
public boolean equals(Object object) {
if (this == object) {
return true;
} else if (object == null || getClass() != object.getClass()) {
return false;
}
Key key = (Key) object;
return this.a == key.a && this.b == key.b;
}
#Override
public int hashCode() {
return 31 * this.a + 31 * this.b;
}
}
}
Related
This question already has answers here:
How to find an object in an ArrayList by property
(8 answers)
Closed 4 years ago.
I am maintaining a sorted ArrayList of objects (by overwriting the add method as shown here) where each object has 2 attributes: a and b. How can I retrieve an object for which a equals 5?
I cannot use a map, because the value which I want to sort the list on must be able to accept duplicates (which is why this answer is not applicable here).
Code:
class TimeMap {
List<MyType> list = new ArrayList<KVT>() {
public boolean add(KVT mt) {
int index = Collections.binarySearch(this, mt, new SortByTime());
if (index < 0) index = ~index;
super.add(index, mt);
return true;
}
};
}
class KVT{//value-timestamp object
String value;
int timestamp;
public VT(String v, int t){
value=v;
timestamp=t;
}
}
class SortByTimestamp implements Comparator<KVT>{
public int compare(KVT a, KVT b){
return a.timestamp.compareTo(b.timestamp);
}
}
I have written a small example using java8 streams where you can get the object from the ArrayList by a property of the object.
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Test> list = Arrays.asList(new Test(1, 2), new Test(5, 6), new Test(3, 4));
Test test = list.stream().filter(obj -> obj.a == 5).findFirst().orElse(null);
System.out.println(test.a);
}
}
class Test {
int a;
int b;
Test(int a, int b) {
this.a = a;
this.b = b;
}
}
Hope this will give you an idea
Here is an mcve demonstrating retrieval by timestamp as well as some other enhancement:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class TimeMap {
private List<KVT> list;
TimeMap() {
list = new ArrayList<>() {
#Override
public boolean add(KVT mt) {
super.add(mt); //add
Collections.sort(this, new SortByTimestamp()); //resort after add
return true;
}
};
}
boolean add(KVT mt){return list.add(mt);}
KVT getByTimeStamp(int timestamp){
for(KVT mt : list){
if(timestamp == mt.timestamp)
return mt;
}
return null;
}
//returns a copy of list
List<KVT> getListCopy() { return new ArrayList<>(list) ;};
//test
public static void main(String[] args) {
TimeMap tm = new TimeMap();
tm.add(new KVT("A", 2));
tm.add(new KVT("B", -3));
tm.add(new KVT("C", 1));
System.out.println(tm.getListCopy());
System.out.println(tm.getByTimeStamp(1));
}
}
class KVT{
String value;
int timestamp;
public KVT(String v, int t){
value=v;
timestamp=t;
}
#Override
public String toString(){ return value+" ("+timestamp+")";}
//todo add getters
}
class SortByTimestamp implements Comparator<KVT>{
#Override
public int compare(KVT a, KVT b){
//compareTo can not be applied to primitives
return Integer.valueOf(a.timestamp).compareTo(b.timestamp);
}
}
I am trying to find how many time one string occurs in an ArrayList. I managed to find by using Collections.frequency(list,object);
public class Main {
public static void main(String[] args) {
ArrayList<Main> d = new ArrayList<Main>();
Main m = new Main();
m.setA("a");
d.add(m);
Main m11 = new Main();
m11.setA("a");
d.add(m11);
Main m111 = new Main();
m111.setA("a");
d.add(m111);
int c = Collections.frequency(d, m11);
System.out.println(c);
}
private String a,b,c;
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
#Override
public boolean equals(Object o) {
return a.equals(((Main)o).a);
}
}
In the above code I manage to find occurrences of a. But how can I find other stuff also, like if I want to find occurrences of b and calso? Is there a way I can do it? Can I have many equals function?
Implementation of frequency.
public static int frequency(Collection<?> c, Object o) {
int result = 0;
if (o == null) {
for (Object e : c)
if (e == null)
result++;
} else {
for (Object e : c)
if (o.equals(e))
result++;
}
return result;
}
So this works on equals method and you can't have more than one equals.
You have to manually iterate over the list and find the frequency for different properties.
ArrayList<Main> d = new ArrayList<Main>();
int counter = 0;
foreach( var item in d )
{
if( item = "The string you desire" )
{
counter += 1;
}
}
// This is the total count of the string you wanted in the collection
console.write(counter);
If you want to make this a generic function, it would just take 2 parameters, the collection, and the item you desire.
String a = "1";
String b;
...
String n = "100";
How can I check if none or all of the properties have been set?
I want to get "valid" if a..n all properties are set, and also "valid" if none if them are set. But "invalid" if only partially set.
How can this be solved? Of course I could write endless boolean statements like
(a != null && b != null & .. & n != null) || (a == null && b == null & .. & n == null)
But there must be a better way.
Having a sample class
public class SampleClass {
private String a, b, c, d, e, f;
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
public String getD() {
return d;
}
public void setD(String d) {
this.d = d;
}
public String getE() {
return e;
}
public void setE(String e) {
this.e = e;
}
public String getF() {
return f;
}
public void setF(String f) {
this.f = f;
}
}
you can get the Java Bean information using the java.beans.Introspector
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import org.junit.Test;
public class IntrospectorTest {
#Test
public void test() throws IntrospectionException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
SampleClass sampleClass = new SampleClass();
sampleClass.setA("value for a");
sampleClass.setB("value for b");
sampleClass.setC("value for c");
sampleClass.setD("value for d");
sampleClass.setE("value for e");
sampleClass.setF("value for f");
int withValue = 0;
PropertyDescriptor[] descriptors = Introspector.getBeanInfo(SampleClass.class, Object.class).getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : descriptors) {
Object value = new PropertyDescriptor(propertyDescriptor.getName(), SampleClass.class).getReadMethod().invoke(sampleClass);
if (value!=null) {
withValue++;
System.out.println(propertyDescriptor.getName() + ": " + value);
}
}
if (descriptors.length == withValue || withValue == 0) {
System.out.println("valid");
}else{
System.err.println("Invalid!!");
}
}
}
and voila!
Pay atention at this line
Introspector.getBeanInfo(SampleClass.class, Object.class).getPropertyDescriptors();
if you call the getBeanInfo method with your class as one and only parameter the Introspector will return all the Property Descriptors in the class hierarchy, so you can call the method with an optional stop class where the Introspector stops reading the Property Descriptors.
Hope this helps.
You can use map then iterate over it to check if any of the value is null and set status accordingly.
You can also try with this: Collections.frequency(map.values(), null) == map.size()
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...
I want to apply the inteserction ( using this method http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/Sets.html) to sets that contain objects not primitive. I wrote this code but I have that the intersection is empty..
Concept a = new Concept("Dog");
Concept b = new Concept("Tree");
Concept c= new Concept("Dog");
HashSet<Concept> set_1 = new HashSet<Concept>();
HashSet<Concept> set_2 = new HashSet<Concept>();
set_1.add(a);
set_1.add(b);
set_1.add(c);
SetView<Concept> inter = Sets.intersection(set_1,set_2);
System.out.println(inter.size()); ----> I HAVE ZERO !!!
The Concept class contains only a private member of type String and the method of get and set ..I don't have equals() and hashCode().
This works as expected (notice equals and hashCode on Concept)
package com.stackoverflow.so19634761;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
import java.util.Set;
public class ISect {
public static void main(final String[] args) {
final Concept a = new Concept("Dog");
final Concept b = new Concept("Tree");
final Concept c= new Concept("Dog");
final Set<Concept> set1 = Sets.newHashSet(a);
final Set<Concept> set2 = Sets.newHashSet(b, c);
final SetView<Concept> inter = Sets.intersection(set1, set2);
System.out.println(inter); // => [Concept [data=Dog]]
}
private static class Concept {
private final String data;
// below this point code was generated by eclipse.
public String getData() {
return data;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((data == null) ? 0 : data.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;
Concept other = (Concept) obj;
if (data == null) {
if (other.data != null)
return false;
} else if (!data.equals(other.data))
return false;
return true;
}
public Concept(String data) {
this.data = data;
}
#Override
public String toString() {
return "Concept [data=" + data + "]";
}
}
}
You are putting Concept inside Sets, not the Strings - Dog, Tree. U also need to override the hashcode and equals of the concept class for it to work
At first, You need to override equals and hashcode method on Concept class. You don't need third party library. Just use
set_1.retainAll(set2);
set_1.retainAll(set2) transforms set_1 into the intersection of set_1 and set_2. (The intersection of two sets is the set containing only the elements common to both sets.).