0

I need to split one list to multiple list based on some property.

 List<Custom1> nums = new ArrayList<>();
        List<Custom2> cats = new ArrayList<>();
        List<Custom3> forms = new ArrayList<>();

        list.stream().forEach(custom -> {
            if (custom.getType().equalsIgnoreCase("N")) {
                Custom1 attr = new Custom1();
                attr.setAttrCd(custom.getCode());
                attr.setValue(Float.parseFloat(custom.getValue()));

                nums.add(attr);
            } else if (custom.getType().equalsIgnoreCase("C")) {
                Custom2 attr = new Custom2();
                attr.setAttrCd(custom.getCode());
                attr.setValue(Integer.parseInt(custom.getValue()));

                cats.add(attr);
            } else if (custom.getType().equalsIgnoreCase("F")) {
                Custom3 attr = new Custom3();
                attr.setAttrCd(custom.getCode());
                attr.setValue(custom.getValue());
                forms.add(attr);
            }
        });

The same code can be made better with stream filter.

list.stream().filter(...).collect(toList())

but i need to iterate 3 times in my case. Is there any other better way?

user1578872
  • 7,808
  • 29
  • 108
  • 206

2 Answers2

2

You should try to use Collectors.partitioningBy() and collect required lists as values in the map. You can see more info, e.g. here - https://stackoverflow.com/a/30110890/10479804

update: as Nikolai noted, partitioningBy() can be used only for splitting in two subsets based on the Predicate function.

@user1578872, you should think about correct inheritance of your classes: if all classes in stream have the field type and other common fields I suppose you can extract some main abstract class. So you need to refactor your classes and take your type information from class field to class type.

Nevertheless, let's assume that you have:

public abstract class Custom {

    private String type;
    // other common fields
    // constructors etc.
}

and three sub-classes that extends it: Custom1, Custom2 and Custom3. You can take the type information to class name, e.g. Num, Cat and Form. So with the next code you can easily divide whole stream on Sets depending on object classes:

Map<Class, Set<Custom>> collect = list.stream()
        .collect(Collectors.groupingBy(Custom::getClass, toSet()));

Quite simple solution at all!

If input objects have other fields that are used for creation of new different objects it will a problem. I think you shouldn't use Stream API if you deal with set of objects that have too many features. In this case or you'd better process all objects in one for-loop using switch construction. Also you should remember that handling exceptions in streams is not so easy and not so readable solution at all.

If you still want to use Streams yo will have basically put all your code in lambda expression that have to be passed to Collectors.mapping.

Andrey
  • 433
  • 1
  • 5
  • 19
1

there is nothing wrong with list.stream().filter(...).collect(toList()) 3 times. it's complexity still O(n) and your code much cleaner.

Ferry
  • 344
  • 3
  • 10