Split list into duplicate and non-duplicate lists Java 8

Viewed 636

I have a List<String> that may or not contain duplicated values:

In the case of duplicated "ABC" value (only ABC for this matter)
List myList = {"ABC", "EFG", "IJK", "ABC", "ABC"},

I want to split the list in two lists to finally get
List duplicatedValues = {"ABC"};
and
List nonDuplicatedValues = {"EFG", "IJK"};

And also if the list doesn't have more than one "ABC" it will return the same list

What I did so far :

void generateList(List<String> duplicatedValues, List<String> nonDuplicatedValues){

    List<String> myList=List.of("ABC","EFG","IJK","ABC","ABC");
    Optional<String> duplicatedValue = myList.stream().filter(isDuplicated -> Collections.frequency(myList, "ABC") > 1).findFirst();

    if (duplicatedValue.isPresent())
    {
        duplicatedValues.addAll(List.of(duplicatedValue.get()));
        nonDuplicatedValues.addAll(myList.stream().filter(string->string.equals("ABC")).collect(Collectors.toList()));
    }
    else
    {
        nonDuplicatedValues.addAll(myList);
    }
}

Is there a more efficient way to do that using only a stream of myList ?

4 Answers

You can do something like this:

myList.stream().forEach((x) -> ((Collections.frequency(myList, x) > 1) ? duplicatedValues : nonDuplicatedValues).add(x));

(The duplicatedValues should be a Set to prevent duplications)

Also it can be done by collecting to lists of duplicated and non-duplicated values:

Map<Boolean, List<String>> result = input.stream()
    .collect(Collectors.collectingAndThen(
        Collectors.groupingBy(s -> s, Collectors.counting()),
            m -> m.entrySet().stream()
                .collect(Collectors.groupingBy(e -> e.getValue() > 1, 
                    Collectors.mapping(e -> e.getKey(), Collectors.toList()))
                )
        ));
List<String> duplicates = result.get(true);
List<String> nonDuplicates = result.get(false);

It is possible to use a stream to create from your list a Map storing strings and their frequencies in your list; after you can iterate over the map to put elements in lists duplicatedValues and nonDuplicatedValues like below:

List<String> duplicatedValues = new ArrayList<String>();
List<String> nonDuplicatedValues = new ArrayList<String>();

List<String> myList=List.of("ABC","EFG","IJK","ABC","ABC");
        Map<String, Long> map = myList.stream()
            .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

map.forEach((k, v) -> { if (v > 1)  duplicatedValues.add(k); else nonDuplicatedValues.add(k); });

Here is one way to do it. It basically does a frequency count and divdes accordingly.

List<String> myList = new ArrayList<>(
        List.of("ABC", "EFG", "IJK", "ABC", "ABC", "RJL"));

Map<String,Long> freq = new HashMap<>();
for (String str : myList) {
    freq.compute(str, (k,v)->v == null ? 1 : v + 1);
}

Map<String,List<String>> dupsAndNonDups = new HashMap<>();
for (Entry<String,Long> e : freq.entrySet()) {
        dupsAndNonDups.computeIfAbsent(e.getValue() > 1 ? "dups" : "nondups",
          k-> new ArrayList<>()).add(e.getKey());
}

System.out.println("dups = " + dupsAndNonDups.get("dups"));

Prints

dups = [ABC]
nondups = [RJL, EFG, IJK]

Related