简体   繁体   中英

How do I get a list of items from a subset of a list, based on the items in another list in Java?

I've got a List<String> which represents the ID's (can be duplicate), of items from another list, which is a List<Cheat> , where each Cheat has a String ID and a List<Integer> RNG . Both have accessor methods in Cheat.

I need to convert this list of ID's, into a list of RNG's for each Cheat that I have been supplied with the ID for.

For example, I could have 3 Cheats:

1:{ID:1, RNG:{1,2,3}}
2:{ID:2, RNG{1,2}}
3:{ID:3, RNG:{1}}

And a List of ID's of:

{3,1,1,2}.

I would need to end up with a final list of {1,1,2,3,1,2,3,1,2}, which is the RNG's of Cheat 3, then the RNG's of cheat 1, then the RNG's of cheat 1 again, then finally the RNG's of cheat 2.

If anyone could help me out it would be appreciated. Thank you.

I've tried and failed with:

ImmutableList<Integer> sequenceRngs = cheatIds.stream()
                                              .map(s -> cheats.stream()
                                                                .filter(cheat -> cheat.getId().equals(s))
                                                                .findFirst()
                                                                .map(cheat -> cheat.getRng()))
                                              .flatMap(cheat -> cheat.getRng())
                                              .collect(ListUtils.toImmutableList());

One possible solution:

import java.util.List;
import java.util.stream.Collectors;

class Scratch {

    static class Cheat {
        int id;
        List<Integer> rng;

        public Cheat(int id, List<Integer> rng) {
            this.id = id;
            this.rng = rng;
        }
    }

    public static void main(String[] args) {

        List<Cheat> allCheats = List.of(
                new Cheat(1, List.of(1,2,3)),
                new Cheat(2, List.of(1,2)),
                new Cheat(3, List.of(1))
        );

        List<Integer> result = List.of(3, 1, 1, 2).stream()
                .flatMap(id -> allCheats.stream()
                        .filter(cheat -> cheat.id == id)
                        .findFirst().orElseThrow().rng.stream())
                .collect(Collectors.toList());

        System.out.println(result);

    }
}

The key is to use flatMap to get the result in a single - not nested - Collection in the end.

The lambda that you pass to flatMap should return a Stream , not a List . And you should handle the case where there's no such element in the stream - even if you are sure there is. Something like this should do:

final ImmutableList<String> sequenceRngs = cheatIds.stream().flatMap(id ->
    cheats.stream().filter(cheat -> id.equals(cheat.getId()))
        .findAny().orElseThrow(IllegalStateException::new)
        .getRng().stream())
    .collect(ListUtils.toImmutableList());

Also, I would propose to convert the list of cheats to a map - that would simplify the code and reduce the complexity of searching from O(n) to O(1).

You can attain that with the following steps:

  1. Create a map of cheatId to RNG id s associated:

     Map<Integer, List<Integer>> map = cheats.stream() .collect(Collectors.toMap(Cheat::getId, cheat -> cheat.getRng().stream().map(RNG::getId).collect(Collectors.toList())));
  2. Iterate over the cheatIds provided as input and get the corresponding RNG ids from the map to collect as output:

     List<Integer> output = cheatIds.stream() .flatMap(ch -> map.get(ch).stream()) .collect(Collectors.toList());

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