简体   繁体   中英

Java Clone Object inside a list N times

I have this object

public class Menu implements Cloneable{
String id;
String repetable;
List <Menu> children;

//Getters and Setters

public Menu clone() {
    Menu obj = null;
    try {
      obj = (Menu)super.clone();
    }catch (CloneNotSupportedException e) {
      throw new RuntimeException();
    }
    return obj;
  }
}

As you can see this object have a list inside of the same item so it is a recursive list.

The logic I am implementing recieve as an input param the object Menu as a list, inside there can be N children.

What I need to achieve is clone the object if the field "repetable" is different from NULL or Empty. I need to iterate the whole list, process each object one by one apply the filter (by field repetable) and then clone the object and append it to the list in other words.

The twist here is that I need to clone the Item N times, based on a int value given during the execution.

Here is what I got so far.

private List<Menu> cloneElementsInListNTimes(List<Menu> sourceList) {

    List <Menu> srList = sourceList;
    List<Menu> repetableList = new ArrayList<>();

    srList.stream()
          .peek(p -> p.setChildren(cloneElementsInListNTimes(p.getChildren())))
          .map(obj -> {
            int objectRepetitions = 2;
            return cloneObject(obj, objectRepetitions);
          })
          .filter(p -> StringUtils.isNotEmpty(p.getRepetable()))
          .forEachOrdered(repetableList::add);

    sourceList.addAll(repetableList);
    return sourceList;
  }

Here is the cloneObject method.

private ConfigurationMenuRs cloneObject(Menu obj, int repetableTimes) {
    for (int j = 0; j < repetableTimes; j++) {
      obj.clone();
    }
    return obj;
  }

List Data example:

Input:

[
    {
        "id": "1",
        "repetable": "",
        "children": [
            {
                "id": "1.1",
                "repetable": "Yes",
                "children": []
            },
            {
                "id": "1.2",
                "repetable": "",
                "children": []
            }
        ]
    },
    {
        "id": "2",
        "repetable": "Yes",
        "children": []
    },
    {
        "id": "3",
        "children": []
    }
]

Output:

[
    {
        "id": "1",
        "repetable": "",
        "children": [
            {
                "id": "1.1",
                "repetable": "Yes",
                "children": []
            },
            {
                "id": "1.1",
                "repetable": "Yes",
                "children": []
            },
            {
                "id": "1.1",
                "repetable": "Yes",
                "children": []
            },
            {
                "id": "1.2",
                "repetable": "",
                "children": []
            }
        ]
    },
    {
        "id": "2",
        "repetable": "Yes",
        "children": []
    },
    {
        "id": "2",
        "repetable": "Yes",
        "children": []
    },
    {
        "id": "2",
        "repetable": "Yes",
        "children": []
    },
    {
        "id": "3",
        "children": []
    }
]

As can see the object with id 1.1 and 2 where cloned two times. That is because of the value 2 set on the method cloneObject.

I know I am missing the exact logic to clone a object N times and how to add it or append it to the current list, but I cant figure out how I can achieve this.

EDIT

Using a for to iterate over the principal array (input) I manage to get the response I am looking for.

private List<Menu> cloneElementsInListNTimes(List<Menu> sourceList,
      HashMap<String, String> contextData) {

    List <Menu> srList = sourceList;
    List<Menu> repetableList = new ArrayList<>();

    for (int i = 0; i < sourceList.size(); i++) {
      Menu m = srList.get(i);
      if(StringUtils.isNotEmpty(m.getRepeatableBy())){
        m.setChildren(cloneElementsInListNTimes(m.getChildren(), contextData));
        List<Menu> cloned = cloneObject(m, 2);
        repetableList.addAll(cloned);
      }
    }

    sourceList.addAll(repetableList);
    return sourceList;
  }

private List<Menu> cloneObject(Menu item, int i) {
    List<Menu> clonedList = new ArrayList<>();
    for (int j = 0; j < i; j++) {
      item.clone();
      clonedList.add(item);
    }
    return clonedList;
  }

What I would want to know now is that if there is a way to optimize this, maybe using java 8 streams.

Following is a working example:

import java.util.ArrayList;
import java.util.List;

public class Main {

    public static void main(String[] args) {

        // Create the menus...
        // First menu with its 2 children
        Menu menu11 = new Menu("1.1", true, List.of());
        Menu menu12 = new Menu("1.2", false, List.of());
        Menu menu1 = new Menu("1", false, List.of(menu11, menu12));

        // Second and third menu, no children.
        Menu menu2 = new Menu("2", true, List.of());
        Menu menu3 = new Menu("3", false, List.of());

        List<Menu> sourceList = List.of(menu1, menu2, menu3);
        List<Menu> clonedElements = cloneElementsInListNTimes(sourceList, 2);

        // Print result
        System.out.println("[");
        for (Menu m : clonedElements) {
            m.printMenu(0);
        }
        System.out.println("]");
    }


    private static List<Menu> cloneElementsInListNTimes(List<Menu> sourceList, int N) {
        List<Menu> result = new ArrayList<>();
        for (Menu menu : sourceList) {
            List<Menu> children = menu.getChildren();
            List<Menu> clonedChildren = cloneElementsInListNTimes(children, N);
            menu.setChildren(clonedChildren);
            result.add(menu);
            if (menu.getRepeatable())
                result.addAll(cloneObject(menu, N));
        }
        return result;
    }

    
    private static List<Menu> cloneObject(Menu obj, int N) {
        List<Menu> cloned = new ArrayList<>();
        for (int j = 0; j < N; ++j) {
            cloned.add(obj.clone());
        }
        return cloned;
    }
}

Be aware of the fact that cloning in Java is a shallow copy of the object to clone, so cloneElementsInListNTimes will naturally also change sourceList ( Actually, the children of menus inside that list, because the method is setting new children for a menu, so we're referencing the same object ).

Using Streams:

private static List<Menu> cloneElements(List<Menu> sourceList, int N) {
        List<Menu> result = new ArrayList<>();
        sourceList.stream()
                .peek(menu -> menu.setChildren(cloneElements(menu.getChildren(), N)))
                .forEach(menu -> {
                    result.add(menu);
                    if (menu.getRepeatable())
                        result.addAll(cloneObject(menu, N));
                });
        return result;
    }

The following alternative omits the if in the streams solution above, but clones first all the objects and then filters them out if they are not repeatable:

List<Menu> result = new ArrayList<>();
sourceList.stream()
          .peek(menu -> {
                    menu.setChildren(cloneElements(menu.getChildren(), N));
                    result.add(menu);
                }
           )
           .map(menu -> cloneObject(menu, N).stream().filter(menuInList -> menuInList.getRepeatable()))
           .forEach(menuStream -> result.addAll(menuStream.toList()));
        return result;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM