8
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(), .... )
    });
Eran
  • 387,369
  • 54
  • 702
  • 768
joseluisbz
  • 1,491
  • 1
  • 36
  • 58

3 Answers3

10

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.

Eran
  • 387,369
  • 54
  • 702
  • 768
9

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.

Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
0

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]]
Roy Shahaf
  • 494
  • 5
  • 10