简体   繁体   中英

Most frequent element stream

How to find most frequent element, but when there are few most frequent element return null.

I would like to find code equivalent of:

public static void main(String[] args) {
    System.out.println("Should return A -> " + mostFrequent(Arrays.asList("A", "A", "B")));
    System.out.println("Should null as element in list have same frequency -> "
            + mostFrequent(Arrays.asList("A", "B")));
}

private static String mostFrequent(List<String> elements) {
    Map<String, Long> ordered = new TreeMap<>();
    for (String e : elements) {
        if (!ordered.containsKey(e)) {
            ordered.put(e, 0L);
        }
        Long tmp = ordered.get(e);
        ordered.put(e, ++tmp);
    }

    String mostFrequent = null;
    long i = 0;
    Iterator<Map.Entry<String, Long>> it = ordered.entrySet().iterator();
    while (it.hasNext() && i < 2) {
        Map.Entry<String, Long> pair = it.next();
        if (i == 0) {
            mostFrequent = pair.getKey();
        } else {
            if (ordered.get(mostFrequent) == ordered.get(pair.getKey())) {
                return null;
            }
        }
        i++;
    }

    return mostFrequent;
}

However stream version does not handle most frequent elements with the same frequency.

private static String mostFrequentStream(List<String> elements) {
    return elements.stream()
            .reduce(BinaryOperator.maxBy(
                    Comparator.comparingInt(o -> Collections.frequency(elements, o))))
            .orElse(null);
}

How to modify stream above to achieve it?

using groupingBy:

String mostFrequentStream(List<String> elements) {
    Map<String, Long> temp = elements.stream()
            .collect(Collectors.groupingBy(a -> a, Collectors.counting()));


    return new HashSet<>(temp.values()).size() < temp.size() ? 
          null : temp.entrySet()
                     .stream()
                     .max(Map.Entry.comparingByValue())
                     .map(Map.Entry::getKey).get();

}

I managed to build a concatenated Stream but it got long:

private static String mostFrequentStream3(List<String> elements) {
    return elements.stream() // part 1
            .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
            .entrySet().stream() // part 2
            .collect(Collectors.groupingBy(Entry::getValue))
            .entrySet().stream() // part 3
            .max(Entry.comparingByKey())
            .map(Entry::getValue)
            .filter(v -> v.size() == 1)
            .map(v -> v.get(0).getKey())
            .orElse(null);
}

To "find most frequent element, but when there are few most frequent element return null"
Part 1 counts the frequency of every element.
Part 2 groups entries by frequency.
Part 3 looks up the entry with the highest frequency. If this entry does only have one element ("there are few most frequent"), then it's the one and only maximum. Otherwise null is returned.

I would never use stream for this to avoid hurting readability and performance at the same time. For the sake of fun -

private static String mostFrequentStream(List<String> elements) {
    Map<String, Long> frequencyMap = elements.stream().collect(groupingBy(Function.identity(), counting()));

    return frequencyMap.entrySet().stream()
            .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
            .limit(2).reduce((i, e) -> i.getValue().equals(e.getValue()) ? new AbstractMap.SimpleEntry<>(null, 0L) : i).get().getKey();
}

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