stream creating List of List (nested List) using forEach, Java 8

邮差的信 提交于 2019-12-22 05:08:11

问题


class EntityCompositeId {
    private Long firstId;
    private Long secondId;
    // getter & setter...
}

class EntityComposite {
    private EntityCompositeId id;
    private String first;
    private String second;
    // getter & setter...
}

List<EntityComposite> listEntityComposite = ....
Supose this content

1, 1, "firstA", "secondBirdOne"
1, 2, "firstA", "secondBirdTwo"
1, 3, "firstA", "secondBirdThree"

2, 1, "firstB", "secondCatOne"
2, 2, "firstB", "secondCatTwo"
2, 3, "firstB", "secondCatThree"

3, 1, "firstC", "secondDogOne"
3, 2, "firstC", "secondDogTwo"
3, 3, "firstC", "secondDogThree"

Map<Long, List<String>> listOfLists = new HashMap<>();

Now using stream I want to fill like:

 1 -> {"secondBirdOne", "secondBirdTwo", "secondBirdThree"}
 2 -> {"secondCatOne", "secondCatTwo", "secondCatThree"}
 3 -> {"secondDogOne", "secondDogTwo", "secondDogThree"}

My UNFINISHED (that's the question) code is:

listEntityComposite.stream()forEach(entityComposite {
        // How create a list according entityComposite.getId.getFirstId()?
        listOfLists.put(entityComposite.getId.getFirstId(), .... )
    });

回答1:


There are several different approaches in which you can accomplish the task at hand.

forEach + computeIfAbsent

 Map<Long, List<String>> map = new HashMap<>();
 listEntityComposite.forEach(e -> map.computeIfAbsent(e.getId().getFirstId(), 
                k -> new ArrayList<>()).add(e.getSecond()));
  • enumerates over the elements in listEntityComposite via forEach
  • for each element utilises computeIfAbsent to compute the key (i.e. firstId) and value (i.e. List<String>)

groupingBy + mapping

another approach would be to apply a groupingBy with a mapping downstream collector:

Map<Long, List<String>> resultSet = listEntityComposite.stream()
                .collect(groupingBy(e -> e.getId().getFirstId(),
                        mapping(EntityComposite::getSecond, toList())));
  • groups the source elements by the classification function e.getId().getFirstId() and then applies a mapping downstream collector to further refine our query.

forEach + merge

listEntityComposite.forEach(e -> map.merge(e.getId().getFirstId(),
                new ArrayList<>(singletonList(e.getSecond())),
                (l, r) -> {l.addAll(r); return l;}));
  • enumerates over the elements in listEntityComposite via forEach

  • for each element utilises merge to compute the key (i.e. firstId) and value (i.e. List<String>)

toMap

listEntityComposite.stream()
                   .collect(toMap(e -> e.getId().getFirstId(), 
                             v ->  new ArrayList<>(singletonList(v.getSecond())),
                             (l, r) -> {l.addAll(r); return l;}));
  • applies a keyMapper function e -> e.getId().getFirstId() to extract the map keys.
  • applies a valueMapper function v -> new ArrayList<>(singletonList(v.getSecond())) to extract the map values.
  • applies a merge function (l, r) -> {l.addAll(r); return l;} to resolve key collisions.

To conclude, the forEach + computeIfAbsent approach and the groupingBy + mapping approach are the two you should favour in this specific case as they're the more idiomatic.




回答2:


collect is a more suitable terminal operation for generating an output Map than forEach.

You can use collect() with Collectors.groupingBy:

Map<Long, List<String>> listOfLists =
    listEntityComposite.stream()
                       .collect(Collectors.groupingBy(e -> e.getId().getFirstId(),
                                                      Collectors.mapping(EntityComposite::getSecond,
                                                                         Collectors.toList());

Collectors.groupingBy with a single argument (just e -> e.getId().getFirstId()) would generate a Map<Long,List<EntityComposite>>.

Chaining to it Collectors.mapping() maps each EntityComposite instance to the corresponding getSecond() String, as required.




回答3:


I would suggest looking into Guava's MultiMap which makes your use-case easier to deal with (providing lots of optimizations and extra functionality you may want to obtain later down the road)

Edit: An example of why Multimap makes more sense than, say, using the computeIfAbsent approach: 1. each key has a list of a certain size, what if you'll want to get the "total" size in the future? you would have to create some logic to achieve this with good performance (or use a method that takes O(keys)) 2. at the moment you're only putting things into the map, but what happens if you want to remove things from the map in the future? You will need to write some boilerplate code (that is easy to get wrong) to make sure that removing values doesn't cause a memory leak

There are other things to gain from using a multimap but these are just two easy to explain benefits.

Edit 2: An example using your input:

import java.util.Arrays;
import java.util.List;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;

public class Example {

    public static class EntityCompositeId {
        @Override
        public String toString() {
            return "EntityCompositeId [firstId=" + firstId + ", secondId=" + secondId + "]";
        }

        public EntityCompositeId(Long firstId, Long secondId) {
            super();
            this.firstId = firstId;
            this.secondId = secondId;
        }

        private Long firstId;

        public Long getFirstId() {
            return firstId;
        }

        private Long secondId;
    }

    public static class EntityComposite {
        @Override
        public String toString() {
            return "EntityComposite [id=" + id + ", first=" + first + ", second=" + second + "]";
        }

        public EntityComposite(EntityCompositeId id, String first, String second) {
            super();
            this.id = id;
            this.first = first;
            this.second = second;
        }

        private EntityCompositeId id;

        public EntityCompositeId getId() {
            return id;
        }

        private String first;
        private String second;
    }

    public static void main(String[] args) {
        List<EntityComposite> listEntityComposite = Arrays.asList(
                new EntityComposite(new EntityCompositeId(1l, 1l), "firstA", "secondBirdOne"),
                new EntityComposite(new EntityCompositeId(1l, 2l), "firstA", "secondBirdTwo"),
                new EntityComposite(new EntityCompositeId(1l, 3l), "firstA", "secondBirdThree"),
                new EntityComposite(new EntityCompositeId(2l, 1l), "firstB", "secondCatOne"),
                new EntityComposite(new EntityCompositeId(2l, 2l), "firstB", "secondCatTwo"),
                new EntityComposite(new EntityCompositeId(2l, 3l), "firstB", "secondCatThree"),
                new EntityComposite(new EntityCompositeId(3l, 1l), "firstC", "secondDogOne"),
                new EntityComposite(new EntityCompositeId(3l, 2l), "firstC", "secondDogTwo"),
                new EntityComposite(new EntityCompositeId(3l, 3l), "firstC", "secondDogThree"));
        ListMultimap<Long, EntityComposite> map = ArrayListMultimap.create();
        listEntityComposite.forEach(entityComposite -> map.put(entityComposite.getId().getFirstId(), entityComposite));
        map.keySet().forEach(key -> System.out.println(map.get(key)));
    }
}

Yields the following output:

[EntityComposite [id=EntityCompositeId [firstId=1, secondId=1], first=firstA, second=secondBirdOne], EntityComposite [id=EntityCompositeId [firstId=1, secondId=2], first=firstA, second=secondBirdTwo], EntityComposite [id=EntityCompositeId [firstId=1, secondId=3], first=firstA, second=secondBirdThree]]
[EntityComposite [id=EntityCompositeId [firstId=2, secondId=1], first=firstB, second=secondCatOne], EntityComposite [id=EntityCompositeId [firstId=2, secondId=2], first=firstB, second=secondCatTwo], EntityComposite [id=EntityCompositeId [firstId=2, secondId=3], first=firstB, second=secondCatThree]]
[EntityComposite [id=EntityCompositeId [firstId=3, secondId=1], first=firstC, second=secondDogOne], EntityComposite [id=EntityCompositeId [firstId=3, secondId=2], first=firstC, second=secondDogTwo], EntityComposite [id=EntityCompositeId [firstId=3, secondId=3], first=firstC, second=secondDogThree]]


来源:https://stackoverflow.com/questions/53786325/stream-creating-list-of-list-nested-list-using-foreach-java-8

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!