10

I have two lists of dogs - dogsList1, dogsList2
I want to create a single list of all dogs with unique name field.
(meaning, if I encounter a second dog with the same name I do not add it to the result list)

This is the best I could do in java, but it collects unique names, not Dogs:
(Dog contains other fields than name)

// collect all dogs names from first list
List<String> dogNames1 = dogsList1.stream()
     .map(x -> x.getName()).collect(Collectors.toList()); 

dogList2.stream()
     .filter(x->!dogNames1.contains(x.getName()))
     .forEach( x->
            dogsList1.add(x);
     );

Can it be improved ? Any other better solution or optimized approach?

Pshemo
  • 122,468
  • 25
  • 185
  • 269
ran632
  • 1,099
  • 1
  • 12
  • 14

2 Answers2

17

You can use merge multiple stream and drop duplicates.

For the first dog of a given name, you can do

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class A {

    public static void main(String[] args) {
        List<Dog> dogList1 = Arrays.asList(new Dog("a", 1), new Dog("b", 2), new Dog("f", 3));
        List<Dog> dogList2 = Arrays.asList(new Dog("b", 4), new Dog("c", 5), new Dog("f", 6));
        List<Dog> dogList3 = Arrays.asList(new Dog("b", 7), new Dog("d", 8), new Dog("e", 9));
        List<Dog> dogs = new ArrayList<>(
                Stream.of(dogList1, dogList2, dogList3)
                        .flatMap(List::stream)
                        .collect(Collectors.toMap(Dog::getName,
                                d -> d,
                                (Dog x, Dog y) -> x == null ? y : x))
                        .values());
        dogs.forEach(System.out::println);
    }
}

class Dog {
    String name;
    int id;

    public Dog(String name, int id) {
        this.name = name;
        this.id = id;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", id=" + id +
                '}';
    }
}

prints

Dog{name='a', id=1}
Dog{name='b', id=2}
Dog{name='c', id=5}
Dog{name='d', id=8}
Dog{name='e', id=9}
Dog{name='f', id=3}

In each case, you can see the first instance of a name is retained.

For unique names

Set<String> names = Stream.of(dogList1, dogList2, dogList3)
                          .flatMap(List::stream)
                          .map(Dog::getName)
                          .collect(Collectors.toSet());
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 1
    If the OP does indeed want what you've shown in both your examples then this is a duplicate. However, if it's not then your implementation is still not quite correct because if you look at the OP's current code they are not removing all duplicates by a property rather he is preventing adding duplicates to the accumulator, so dogNames1 could contain an arbitrary amount of duplicates but the key point here is _**do not add an object from the second list if it's already present in the first list**_ which your code doesn't seem to take into account. – Ousmane D. Jan 08 '18 at 16:08
  • @Aominè I take your point that duplicates in the first list appear to be allowed, however I don't understand what you mean by the bold statement. Can you give an example? – Peter Lawrey Jan 08 '18 at 16:13
  • @Aominè it might be a duplicate but it's not clear to me that it is either. – Peter Lawrey Jan 08 '18 at 16:14
  • 1
    this might in fact not be a duplicate if and only if the OP states that duplicates in the first list are allowed(which I believe is what he wants). My bold statement might not be clear but it simply means _duplicates in the first list are allowed_ which your examples doesn’t seem to take into account. – Ousmane D. Jan 08 '18 at 16:21
7

That could be an option

  List<String> dogNames = Stream.concat(dogsList1.stream(),dogsList2.stream())
                          .map(x -> x.getName())
                          .distinct()
                          .collect(Collectors.toList());
Frablamo
  • 81
  • 3