Looking for a best solution for the object recursion problem. Below is the example:
Class:
public class SomeObject {
private List<SomeObject> objects;
}
Data:
Input:
SomeObject has List of objects and every Object in list is of SomeObject Type and has list inside them. (Recursive in nature)
Requirement is to flatten them and put them in a single arraylist.
The flatten List should have all the SomeObject types.
Can anyone suggest what's the best way to handle this case. Thanks!
If you are not using Java 8, You might consider doing it this way.
import java.util.ArrayList;
import java.util.List;
public class TestClass {
static List<SomeObject> flatList = new ArrayList<SomeObject>();
public static void flatten(SomeObject object) {
if (object != null ){
if( object.getObjects() != null && !object.getObjects().isEmpty()) {
for (SomeObject o : object.getObjects()) {
flatten(o);
flatList.add(object);
}
}
}
}
public static void main(String[] args) {
SomeObject o1 = new SomeObject("1");
SomeObject o2 = new SomeObject("2");
SomeObject o3 = new SomeObject("3");
SomeObject o4 = new SomeObject("4");
o1.addObject(o2);
o2.addObject(o3);
o3.addObject(o4);
flatten(o1);
for (SomeObject obj : flatList){
System.out.println(obj.getObjectName());
}
}
}
class SomeObject {
String objectName = "";
public SomeObject(String name) {
this.objectName = name;
}
private List<SomeObject> objects = new ArrayList<SomeObject>();
public List<SomeObject> getObjects() {
return objects;
}
public void setObjects(List<SomeObject> objects) {
this.objects = objects;
}
public void addObject(SomeObject o){
objects.add(o);
}
public String getObjectName() {
return objectName;
}
}
To make recursion, the method for a given object would require to :
add itself : add to list
ask its children to do same : collect all children getAllChildren()
public class Foo {
private String s;
private List<Foo> fooList = new ArrayList<>();
public Foo(String a) {
s = a;
}
public static void main(String[] args) {
Foo a = new Foo("a");
Foo b = new Foo("b");
Foo c = new Foo("c");
Foo d = new Foo("d");
Foo e = new Foo("e");
a.fooList.add(b);
b.fooList.add(c);
c.fooList.add(e);
a.fooList.add(d);
List<Foo> list = a.getAllChildren();
System.out.println(list);
}
private List<Foo> getAllChildren() {
List<Foo> l = fooList.stream().flatMap(elt -> elt.getAllChildren().stream())
.collect(Collectors.toList());
l.add(this);
return l;
}
#Override
public String toString() { return s; }
}
Input Structure :
a-b-c-d
\ \
e f
Output List :
[d, f, c, b, e, a]
Related
I have a json string and my requirement is to covert into map, where key will be field of the json. below is my json
{
"A":[
{
"B":[
{
"C":[
{
"D1":"V1",
"D2":"X1",
"D3":Y1,
"D4":"Z1"
},
{
"D1":"V2",
"D2":"X2",
"D3":Y2,
"D4":"Z2"
}
]
}
]
}
]
}
Key should look like "A->B->C->D1" and corresponding value V1,V2.
Map signature should look like Map<String,List<String>>. Similar kind of question posted here but my problem is to create key out of json field.Let me know if more information is required. Thanks in advance.
I did something that answers the exact structure of yours where i changed the value of D3 to be also string:
class Wraper that is the whole object
public class Wraper {
public Wraper() {}
#JsonProperty("A") A[] a;
}
class A
public class A {
#JsonProperty("B") B[] b;
}
Class B
public class B {
#JsonProperty("C") C[] c;
}
Class C
public class C {
#JsonProperty("D1") String d1;
#JsonProperty("D2") String d2;
#JsonProperty("D3") String d3;
#JsonProperty("D4") String d4;
}
And finally where I tested:
static final String JSON_VAL="{\"A\":[{\"B\":[{\"C\":[{\"D1\":\"V1\",\"D2\":\"X1\",\"D3\":\"Y1\",\"D4\":\"Z1\"},{\"D1\":\"V2\",\"D2\":\"X2\",\"D3\":\"Y2\",\"D4\":\"Z2\"}]}]}]}";
final ObjectMapper mapper = new ObjectMapper();
final Wraper wraper = mapper.readValue(JSON_VAL, Wraper.class);
final Map<String,List<String>> map = new HashMap<>();
Arrays.stream(wraper.a).forEach(a -> {
Arrays.stream(a.b).forEach(b -> {
final List<String> d1 = new ArrayList<>();
final List<String> d2 = new ArrayList<>();
final List<String> d3 = new ArrayList<>();
final List<String> d4 = new ArrayList<>();
Arrays.stream(b.c).forEach(c -> {
d1.add(c.d1);
d2.add(c.d2);
d3.add(c.d3);
d4.add(c.d4);
});
map.put("A->B->C->D1", d1);
map.put("A->B->C->D2", d2);
map.put("A->B->C->D3", d3);
map.put("A->B->C->D4", d4);
});
});
Try Jackson using the correct Java class definitions (based on the JSON).
Here is some code:
public class topElement
{
private ElementA[] A;
public ElementA[] getA()
{
return A;
}
public void setA(
final ElementA[] newValue)
{
A = newValue;
}
}
public class ElementA
{
private ElementB[] B;
public ElementB[] getB()
{
return B;
}
public void setB(
final ElementB[] newValue)
{
B = newValue;
}
}
public class ElementB
{
private ElementC[] C;
public ElementC[] getC()
{
return C;
}
public void setC(
final ElementC[] newValue)
{
C = newValue;
}
}
public class ElementC
{
private Map blammyMap;
public Map getBlammyMap()
{
return blammyMap;
}
public void setBlammyMap(
final Map newValue)
{
blammyMap = newValue;
}
}
}
I've a Bean Class MyClass as
public class MyClass {
String myString;
int myCount;
public MyClass() {
}
public MyClass(String myString, int myCount) {
super();
this.myString = myString;
this.myCount = myCount;
}
public String getMyString() {
return myString;
}
public void setMyString(String myString) {
this.myString = myString;
}
public int getMyCount() {
return myCount;
}
public void setMyCount(int myCount) {
this.myCount = myCount;
}}
I have an ArrayList as
MyClass obj1 = new MyClass("1-ABC_2-PQR_1-PQR_1-DEF", 4);
MyClass obj2 = new MyClass("1-ABC_2-PQR_3-XYZ_1-PQR_1-DEF", 12);
MyClass obj3 = new MyClass("1-ABC_1-PQR_1-DEF", 3);
MyClass obj4 = new MyClass("3-ABC_2-PQR_1-DEF", 3);
ArrayList<MyClass> rawList = new ArrayList<MyClass>();
rawList.add(obj1); rawList.add(obj2); rawList.add(obj3); rawList.add(obj4);
For the whole ArrayList I am picking out the MyClass.myString iteratively and based on a selection criteria tailoring them and setting them back to their respective objects. After this, I need to create a New ArrayList in which if myString for one object matches that of the other then I need to add the MyClass.myCount for them both and delete of one of the objects.
For example, in the values I've taken, if after tailoring obj1.getMyString() matches obj3.getMyString() as 1-ABC_1-PQR_1-DEF then I need to add obj1.getMyCount() and obj3.getMyCount() and save only one of them in the new ArrayList.
I am doing this in the following way, but I am hoping to get a efficient and fail proof way to do this.
int j = 0;
if (rawList.size() > 0)
list.add(rawList.get(0));
for (int i = 1; i < rawList.size(); i++) {
if (rawList.get(i).getMyString()
.equals(list.get(j).getMyString())) {
list.get(j).setMyCount(
list.get(j).getMyCount()
+ rawList.get(i)
.getMyCount());
} else {
j++;
list.add(rawList.get(i));
}
}
I guess you could use a map:
Map<String,MyClass> map = new LinkedHashMap<>();
for (MyClass obj: rawList) {
MyClass other = map.get(obj.getMyString());
if (other != null) {
other.setMyCount(other.getMyCount() + obj.getMyCount());
} else {
map.put(obj.getMyString(), obj);
}
}
List<MyClass> list = new ArrayList<>(map.values());
public class ObjectToProxy
{
List<ObjectToProxy> potentiallyCircularReference;
}
public class SubClass
{
private ObjectToProxy aField;
Set<ObjectToProxy> aSetOfObjectsToProxy;
}
public class CrazyObject
{
Map<Integer, ObjectToProxy> proxiedObjects;
List<SubClass> manySubClasses;
}
public class ComplexObject
{
List<CrazyObject> crazyObjects;
private final ObjectToProxy storedAsAField;
}
I have a complex object graph. Lets say it looks a little like the one above (even though it is much deeper in the real system). I would like, after being given ComplexObject, to be able to traverse the object graph and replace all ObjectToProxys with a proxying object.
Is this doable?
The reason for this is that we have some pretty big nasty objects which we partially load on the servers side (legacy, you're my friend!). We have a semi-working solution that uses proxying on the client side to go through and loads the full object when needed.
edit I would like to replace every instance of ObjectProxy connected to a ComplexObject.
public static class ProxyObject extends ObjectToProxy
{
private final ObjectToProxy objectToProxy;
public ProxyObject(ObjectToProxy objectToProxy)
{
this.objectToProxy = objectToProxy;
}
#Override
public String toString()
{
return "ProxyObject";
}
}
public static class ObjectToProxy
{
List<ObjectToProxy> potentiallyCircularReference;
public ObjectToProxy()
{
potentiallyCircularReference = new ArrayList<>();
potentiallyCircularReference.add(this);
}
#Override
public String toString()
{
return "ObjectToProxy";
}
}
public static class SubClass
{
ObjectToProxy aField;
Set<ObjectToProxy> aSetOfObjectsToProxy;
}
public static class CrazyObject
{
Map<Integer, ObjectToProxy> proxiedObjects;
List<SubClass> manySubClasses;
public CrazyObject()
{
proxiedObjects = new HashMap<>();
proxiedObjects.put(1, new ObjectToProxy());
}
}
public static class ComplexObject
{
List<CrazyObject> crazyObjects;
final ObjectToProxy storedAsAField;
public ComplexObject()
{
this.storedAsAField = new ObjectToProxy();
crazyObjects = new ArrayList<>();
crazyObjects.add(new CrazyObject());
}
#Override
public String toString()
{
return "myField: " + storedAsAField.toString();
}
}
public static void main(String[] args) throws Exception
{
ComplexObject obj = new ComplexObject();
Set<Object> visitedObjects = Sets.newIdentityHashSet();
Queue<Object> objectsToVisit = new LinkedList<>();
visitedObjects.add(obj);
objectsToVisit.add(obj);
while (!objectsToVisit.isEmpty())
{
handleFields(objectsToVisit.poll(), visitedObjects, objectsToVisit);
}
System.out.println(obj.toString());
}
private static void handleFields(Object obj, Set<Object> visitedObjects, Queue<Object> objectsToVisit) throws Exception
{
List<Field> fields = getAllFields(obj);
for (Field field : fields)
{
field.setAccessible(true);
Object fieldValue = field.get(obj);
if (fieldValue != null && !visitedObjects.contains(fieldValue))
{
if (fieldValue instanceof Object[])
{
visitedObjects.add(fieldValue);
Object[] array = (Object[])fieldValue;
for (Object arrayObj : array)
{
if (arrayObj != null && !objectsToVisit.contains(arrayObj))
{
visitedObjects.add(arrayObj);
if (!DontLookAt.contains(arrayObj.getClass()))
objectsToVisit.add(arrayObj);
}
}
}
else
{
if (!DontLookAt.contains(fieldValue.getClass()))
objectsToVisit.add(fieldValue);
}
if (fieldValue.getClass().equals(ObjectToProxy.class))
{
field.set(obj, new ProxyObject((ObjectToProxy)fieldValue));
}
else if (fieldValue instanceof ObjectToProxy[])
{
ObjectToProxy[] array = (ObjectToProxy[])fieldValue;
for (int i = 0; i < array.length; i++)
{
if (array[i] != null)
array[i] = new ProxyObject(array[i]);
}
}
}
}
}
private static final Set<Class> DontLookAt = getDontLookAtSet();
private static Set<Class> getDontLookAtSet()
{
Set<Class> set = new HashSet<>();
set.add(Long.class);
set.add(Boolean.class);
set.add(Integer.class);
set.add(String.class);
set.add(Byte.class);
set.add(Double.class);
set.add(Float.class);
set.add(Class.class);
return set;
}
private static List<Field> getAllFields(Object obj) throws Exception
{
List<Field> output = new ArrayList<>();
Class klazz = obj.getClass();
while (!klazz.equals(Object.class))
{
Field[] fields = klazz.getDeclaredFields();
output.addAll(Arrays.asList(fields));
klazz = klazz.getSuperclass();
}
return output;
}
For anyone wondering, The above simulates and does what I'm after. I'm sure it isn't perfect, but it is good enough for my purposes.
I wondered how I could change ordering-direction only on some contitional. In my case Strings starting with 'BB' should be ordered in the other direction, everything else should be ordered as usual.
My Test-Class:
public class StringTest {
public static void main(String[] args) {
SomeClass someClass1= new SomeClass("AA");
SomeClass someClass2= new SomeClass("AB");
SomeClass someClass3= new SomeClass("CB4");
SomeClass someClass4= new SomeClass("BB7");
SomeClass someClass5= new SomeClass("BB9");
SomeClass someClass6= new SomeClass("BB3");
SomeClass someClass7= new SomeClass("CB3");
List<SomeClass> list = new ArrayList<SomeClass>();
list.add(someClass1);
list.add(someClass2);
list.add(someClass3);
list.add(someClass4);
list.add(someClass5);
list.add(someClass6);
list.add(someClass7);
Collections.sort(list);
for (SomeClass someClass : list) {
System.out.println(someClass.getSomeField());
}
}
}
My Comparator:
public class SomeClass implements Comparable<SomeClass>
{
private String someField;
public int compareTo(final SomeClass o)
{
int res = 0;
if (someField.startsWith("BB"))
{
res = o.someField.compareTo(someField);
}
else
{
res = someField.compareTo(o.someField);
}
return res;
}
}
My desired output:
AA
AB
BB9
BB7
BB3
CB3
CB4
The actual result so far:
AA
AB
CB3
BB9
BB7
BB3
CB4
Jonny
You need to make sure your Comparator applies the different sorting only when both elements start with "BB". Right now your Comparator applies the different sorting even if you compare "BB9" with "CB3" and therefore the latter is being sorted in front of BB9.
public class SomeClass implements Comparable<SomeClass>
{
private String someField;
public int compareTo(final SomeClass o)
{
int res = 0;
if (someField.startsWith("BB") && o.someField.startsWith("BB"))
{
res = o.someField.compareTo(someField);
}
else
{
res = someField.compareTo(o.someField);
}
return res;
}
}
if(someField.startsWith("BB") && o.someField.startsWith("BB")))
Try this change in your compareTo method which may solve your problem.
I've done some fancy wrapping to avoid unchecked warnings in the past, but after 90 mins of poring over http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html, I can't write the findMatch method below and make it work without #SuppressWarnings("unchecked"). The parameterized class isn't known at compile time.
public interface Matchable<T>
{
public boolean matches(T toMatch);
}
public class PlaceForMatching
{
public static Object findMatch(Object toMatch, Object[] toSearch)
{
if(!(toMatch instanceof Matchable)) return null;
Matchable matchObj = (Matchable)toMatch;
Class<?> matchClass = matchObj.getClass();
for(Object obj : toSearch)
{
/**
* Check here verifies that the search list object we're about
* to check is the same class as the toMatch object.
* This means Matchable will work without a ClassCastException.
**/
if(matchClass.isInstance(obj) && matchObj.matches(obj))
return obj;
}
//Didn't find it
return null;
}
}
Note the code works because in every case Matchable is implemented by T.
Apple implements Matchable<Apple>
Orange implements Matchable<Orange>
EDIT: Add some test code
public static void main(String[] args)
{
Object[] randomList = createAppleArray();
Object apple = new Apple("Red");
Object match = findMatch(apple, randomList);
}
private static Object[] createAppleArray()
{
return new Object[] { new Apple("Pink"), new Apple("Red"), new Apple("Green") };
}
public class Apple implements Matchable<Apple>
{
String color;
public Apple(String color)
{
this.color = color;
}
public boolean matches(Apple apple)
{
return color.equals(apple.color);
}
}
public static <T extends Matchable<T>> T findMatch(T toMatch, T[] toSearch) {
if (toMatch == null)
return null;
Matchable<T> matchObj = toMatch;
Class<?> matchClass = matchObj.getClass();
for (T obj : toSearch) {
if (matchClass.isInstance(obj) && matchObj.matches(obj))
return obj;
}
return null;
}