2

I have a code as following :

List<UserDTO> result = new ArrayList<>();
UserDTO userDTO;
for (User user : users) {
    for (Individual individual : individuals) {
        if (individual.getKey().equals(user.getIndividualId())) {
            userDTO = new UserDTO();
            userDTO.setUserId(user.getUserId());
            userDTO.setFirstName(individual.getFirstName());
            userDTO.setLastName(individual.getLastName());
            result.add(utilisateurDTO);
            break;
        }
    }
}

How can I write this using Java 8 streams ?

3
  • Wrong algorithm. This is O(n^2) and highly inefficient. Put one of the sets (either users or individuals) into a HashMap and then iterate over the other, looking up the corresponding entry in the map. Also, you are expected to make an attempt, we are not going to write the code for you. Try it and ask a specific question when you run into an issue. Please visit the help center, take the tour end read How to Ask Commented Jul 27, 2018 at 16:30
  • What have you tried? You should start by creating a Map<IndividualKey, Individual> to go from O(n^2) to O(n) (and simplify the code). Commented Jul 27, 2018 at 16:30
  • Arrays or ArrayLists? Your title says "arrays" but your code has result as an ArrayList. Also, any attempt you can include in your questions can help us determine where you're stuck. Commented Jul 27, 2018 at 16:30

3 Answers 3

2

Store the keys and values into a map:

Map<T, Individual> individualsMap =
                individuals.stream()
                        .collect(Collectors.toMap(Individual::getKey,
                                Function.identity(),
                                (l, r) -> l));

where T is whatever type Individual::getKey is.

Java-8 solution:

List<UserDTO> resultSet = 
       users.stream()
            .map(user -> Optional.ofNullable(individualsMap.get(user.getIndividualId()))
                    .map(i -> new AbstractMap.SimpleEntry<>(i, user.getUserId())).orElse(null))
            .filter(Objects::nonNull)
            .map(e -> new UserDTO(e.getValue(), e.getKey().getFirstName(), e.getKey().getLastName()))
            .collect(Collectors.toList());

java-9 solution:

List<UserDTO> resultSet = 
        users.stream()
             .flatMap(user -> Optional.ofNullable(individualsMap.get(user.getIndividualId()))
                        .map(i -> Map.entry(i, user.getUserId())).stream())
             .map(e -> new UserDTO(e.getValue(), e.getKey().getFirstName(), e.getKey().getLastName()))
             .collect(Collectors.toList());

This assumes you have a constructor as such:

public UserDTO(T userId, String firstName, String lastName) { ... }

where T should be substituted with whatever type userId is.

Sign up to request clarification or add additional context in comments.

8 Comments

In the java 9 solution code, something questions me. You use Optional.ofNullable() but then you don't filter non empty optional. Could you explain that part please ?
Sure. note that I am invoking stream() upon the optional and that means if the optional is actually empty then an empty stream is returned which then means flatMap will do the remaining work; as if you flatten a stream of say 1 element with another stream of no elements I.e. empty stream the result is a stream of 1 element. In other words you can think of flatMap as discarding the empty Streams which are returned when the optional is actually empty and simply concatenation the Streams with one elements each. @davidxxx
I understand much better. The Optional.stream() introduced in Java 9 (that you have just made me discovered) is the key ! It is indeed very fine because the Java 8 way I used (probably improvable) is very cumbersome : we have to handle explicitly the empty elements of the Optional. Thanks for the explanation.
Thanks for this solution, but I get this error : Syntax error on token(s), misplaced construct(s) for the Java 8 solution and when I add a ) I get this error : Cannot infer type argument(s) for <R> map(Function<? super T,? extends R>)
Thanks the first suggestion fixed my problem.
|
2

Not tested but you could try something like that :

List<UserDTO> userDtos = 
            users.stream()
                 .map(u -> individuals.stream()
                                       .filter(indiv -> indiv.getKey()
                                                             .equals(u.getIndividualId()))
                                       .findFirst()                                                        
                                       .map(indiv -> new UserDTO(u.getUserId(), indiv.getFirstName(), indiv.getLastName()))
                                       .orElse(null))
                 .filter(Objects::nonNull)                                                         
                 .collect(Collectors.toList());

findFirst() will allow short-circuiting the inner iteration as soon as a matching between an individual and a user is detected.
I introduced an arg constructor in UserDTO() to ease its initialization.

Comments

1

As already suggested in comment by @JB Nizet, to not go with O(n^2) I would like to solve the problem in two steps like this :

Map<Long, Individual> individualsMap =
        individual.stream()
                .collect(Collectors.toMap(Individual::getKey, Function.identity()));

List<UserDTO> result = users.stream()
        .filter(user -> individualsMap.containsKey(user.getKey()))
        .map(user -> {
            UserDTO userDTO = new UserDTO();
            Individual indivd = individualsMap.get(user.getKey());
            userDTO.setUserId(user.getUserId());
            userDTO.setFirstName(indivd.getFirstName());
            userDTO.setLastName(indivd.getLastName());
            return userDTO;
        }).collect(Collectors.toList());

4 Comments

This individualsMap.get(user.getKey()) can return null I guess.. Maybe add .filter(user -> individualsMap.contains(user.getUserId()))?
@user7 and now? I add a filter before I make map
Perfect. Just an opinion. The OP breaks after a first match. If more than one Individual can have the same key, then you might need to add a merge function to pick the first one. ((individual1, individual2) -> individual1). But I don't think it is the case and the break is to stop the unnecessary looping.
Thanks for your help, but I'm getting this compilation error Cannot infer type argument(s) for <R> map(Function<? super T,? extends R>)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.