Here is the SQL version for the input and output :
with tab1 as (
select 1 as id from dual union all
select 1 as id from dual union all
long result = myList.stream()
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting()))
.entrySet()
.stream()
.filter(entry -> entry.getValue() == 1)
.map(Entry::getKey)
.count();
Well you collect all the elements to a Map<Integer, Long>
, where the key is the value itself, and value is how many times it is repeated. Later we stream the entry set from that resulting map and filter
only those entries that that have a count of 1
(meaning they are not repeated), later we map
to Entry::getKey
- to get that value from the List and count.
You may do it like so,
Map<Integer, Long> countByNumber = myList.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
List<Integer> uniqueNumbers = myList.stream()
.filter(n -> countByNumber.get(n) == 1)
.collect(Collectors.toList());
First create a map using the value and the number of occurences. Then iterate over the List
of numbers, get the occurences from the map
for each number. If the number of occurences is 1, then collect it into a separate container.
If you want to do it in one go, here it is.
List<Integer> uniqueNumbers = myList.stream()
.collect(Collectors.collectingAndThen(Collectors.groupingBy(Function.identity(),
Collectors.counting()),
m -> myList.stream().filter(n -> m.get(n) == 1).collect(Collectors.toList())));
For completeness, here's a Java 8 way which doesn't make use of streams:
Map<Integer, Long> frequencyMap = new LinkedHashMap<>(); // preserves order
myList.forEach(element -> frequencyMap.merge(element, 1L, Long::sum)); // group, counting
frequencyMap.values().removeIf(count -> count > 1); // remove repeated elements
Set<Integer> nonRepeated = frequencyMap.keySet(); // this is your result
This uses Map.merge to fill the map with the count of each element. Then, Collection.removeIf is used on the values of the map, in order to remove the entries whose value is greater than 1
.
I find this way much more compact and readable than using streams.
An alternative is to filter repeated elements out of the list but it's not very efficient so I would only use it if the list is small:
List<Integer> result = myList.stream()
.filter(i -> Collections.frequency(myList, i) == 1)
.collect(toList());
Another option for you to consider:
//Create a map where the key is the element you want to count and the value is the actual count
Map<Integer, Long> countByItem = myList.stream()
.collect(Collectors.groupingBy(
Function.identity(), //the function that will return the key
Collectors.counting())); //the function that will actually count the occurences
//Once you have that map, all you need to do is filter for the registers with count == 1
List<Integer> result = countByItem.entrySet().stream()
.filter(entry -> entry.getValue() == 1)
.map(Map.Entry::getKey)
.collect(Collectors.toList());