Java-获取列表中最常见的元素

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19031213/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 13:33:51  来源:igfitidea点击:

Java-get most common element in a list

javaguava

提问by Doc Holiday

Does Java or Guava have something that will return most common element in a list?

Java 或 Guava 是否有返回列表中最常见元素的东西?

List<BigDecimal> listOfNumbers=  new ArrayList<BigDecimal>(); 

[1,3,4,3,4,3,2,3,3,3,3,3]

[1,3,4,3,4,3,2,3,3,3,3,3]

return 3

返回 3

采纳答案by arshajii

This is fairly easy to implement yourself:

这很容易自己实现:

public static <T> T mostCommon(List<T> list) {
    Map<T, Integer> map = new HashMap<>();

    for (T t : list) {
        Integer val = map.get(t);
        map.put(t, val == null ? 1 : val + 1);
    }

    Entry<T, Integer> max = null;

    for (Entry<T, Integer> e : map.entrySet()) {
        if (max == null || e.getValue() > max.getValue())
            max = e;
    }

    return max.getKey();
}


List<Integer> list = Arrays.asList(1,3,4,3,4,3,2,3,3,3,3,3);
System.out.println(mostCommon(list));
3

If you want to handle cases where there's more then one most frequent element, you can scan the list once to determine how many times the most frequent element(s) occur, and then scan the list again, put those elements in a set and return that.

如果您想处理不止一个最频繁元素的情况,您可以扫描列表一次以确定最频繁的元素出现的次数,然后再次扫描列表,将这些元素放入一个集合中并返回那。

回答by Alcanzar

The classic way to do this is to sort the list and then work through them one by one:

执行此操作的经典方法是对列表进行排序,然后一一处理它们:

public static BigInteger findMostCommon(List<BigInteger> list) {
    Collections.sort(list);
    BigInteger mostCommon = null;
    BigInteger last = null;
    int mostCount = 0;
    int lastCount = 0;
    for (BigInteger x : list) {
        if (x.equals(last)) {
            lastCount++;
        } else if (lastCount > mostCount) {
            mostCount = lastCount;
            mostCommon = last;
        }
        last = x;
    }
    return mostCommon;
}

This is a bit more space efficient than using a hash to tally counts since it sorts the array in place. You could toss this into a generics class and replace BigInteger with T, or just use Object in place of BigInteger.

这比使用散列来统计计数更节省空间,因为它对数组进行了排序。您可以将其放入泛型类并用 T 替换 BigInteger,或者仅使用 Object 代替 BigInteger。

回答by Eric Jablow

If you are willing to use Google Guava, you can use its MultiSetclasses:

如果你愿意使用 Google Guava,你可以使用它的MultiSet类:

MultiSet<BigNumber> numbers = HashMultiSet.create();
numberSet.addAll(list);
Set<MultiSet.Entry<BigNumber>> pairs = numbers.emtrySet();
Set<MultiSet.Entry<BigNumber>> copies = new HashSet<MultiSet.Entry<BigNumber>>(pairs);

Now, sort copiesby its values descending.

现在,copies按其值降序排序。

回答by Louis Wasserman

Probably the simplest solution with Guava looks like

可能最简单的番石榴解决方案看起来像

Multiset<BigDecimal> multiset = HashMultiset.create(listOfNumbers);
BigDecimal maxElement = null;
int maxCount = 0;
for (Multiset.Entry<BigDecimal> entry : multiset.entrySet()) {
  if (entry.getCount() > maxCount) {
    maxElement = entry.getElement();
    maxCount = entry.getCount();
  }
}

That's a complete solution, and shorter than the other alternatives I see discussed.

这是一个完整的解决方案,比我看到讨论的其他替代方案更短。

回答by Jared Levy

Guava provides a methodthat will help, though it's less efficient than Louis's solution.

Guava 提供了一种有用的方法,尽管它不如 Louis 的解决方案有效。

BigDecimal mostCommon = 
    Multisets.copyHighestCountFirst(ImmutableMultiset.copyOf(listOfNumbers))
        .iterator().next();

回答by a.b.d

Here is an extension of Louis' answer that support the case where there is multiple elements with same max occurrence count:

这是路易斯回答的扩展,支持存在多个具有相同最大出现次数的元素的情况:

private <T> List<T> getMostFrequentElements(List<T> list) {
    Multiset<T> multiset = HashMultiset.create(list);

    List<T> mostFrequents = new ArrayList<>();
    int maxCount = 0;

    for (Multiset.Entry<T> entry : multiset.entrySet()) {
        if (entry.getCount() > maxCount) {
            maxCount = entry.getCount();
            mostFrequents.clear();
            mostFrequents.add(entry.getElement());
        } else if (entry.getCount() == maxCount) {
            mostFrequents.add(entry.getElement());
        }
    }

    return mostFrequents;
}

回答by Utku ?zdemir

Here is a pure Java 8solution (note: do not use this one, see below):

这是一个纯Java 8解决方案(注意:不要使用这个,见下文):

List<Integer> theList = Arrays.asList(1, 3, 4, 3, 4, 3, 2, 3, 3, 3, 3, 3);
Integer maxOccurredElement = theList.stream()
        .reduce(BinaryOperator.maxBy((o1, o2) -> Collections.frequency(theList, o1) -
                        Collections.frequency(theList, o2))).orElse(null);
System.out.println(maxOccurredElement);

Another solution, by collecting the elements to a map by their frequency, then finding the entry with max value and returning its key (basically the same solution on arshajii's answer, written using Java 8):

另一种解决方案,通过按频率将元素收集到映射,然后找到具有最大值的条目并返回其键(与使用 Java 8 编写的arshajii's answer基本相同的解决方案):

Integer maxVal = theList.stream()
                .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
                .entrySet().stream().max((o1, o2) -> o1.getValue().compareTo(o2.getValue()))
                .map(Map.Entry::getKey).orElse(null);

Update:If the most frequent elements are more than one, and you want to get all of them in a collection, I propose two methods:

更新:如果出现频率最高的元素不止一个,并且您想将所有元素都放在一个集合中,我提出两种方法:

Method A:After collecting the original collection to a map with keys as elements and values as their number of occurrences, getting the entry with the maximum value and filtering the map entries with value equal to this max value (if) we found. Something like this:

方法A:将原始集合收集到一个以key为元素,value为出现次数的map后,得到最大值的条目,过滤掉我们找到的值等于这个最大值的map条目(if)。像这样的东西:

Map<Integer, Long> elementCountMap = theList.stream()
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
List<Integer> result = elementCountMap.values().stream()
        .max(Long::compareTo).map(maxValue -> elementCountMap.entrySet().stream()
            .filter(entry -> maxValue.equals(entry.getValue())).map(Map.Entry::getKey).collect(Collectors.toList()))
        .orElse(Collections.emptyList());

Method B:After collecting the original collection to a map with keys as elements and values as their number of occurrences, transforming this map into a new map with keys as number of occurences, values as a list of elements with this number of occurences. And then finding the max element of this map with a custom comparator which compares the keys, and getting the value of this entry. Like this:

方法B:将原始集合收集到一个以key为元素,value为出现次数的map后,将该map转换为一个新的map,以key为出现次数,value为一个具有该出现次数的元素列表。然后使用自定义比较器查找此映射的最大元素,该比较器比较键,并获取此条目的值。像这样:

List<Integer> result = theList.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
    .entrySet().stream()
    .collect(Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toList())))
    .entrySet().stream().max((o1, o2) -> o1.getKey().compareTo(o2.getKey())).map(Map.Entry::getValue)
    .orElse(Collections.emptyList());

回答by Lukas Eder

In statistics, this is called the "mode". A vanilla Java 8 solution looks like this:

在统计学中,这被称为“模式”。一个普通的 Java 8 解决方案如下所示:

Stream.of(1, 3, 4, 3, 4, 3, 2, 3, 3, 3, 3, 3)
      .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
      .entrySet()
      .stream()
      .max(Comparator.comparing(Entry::getValue))
      .ifPresent(System.out::println);

Which yields:

其中产生:

3=8

jOOλis a library that supports mode()on streams. The following program:

jOOλ是一个支持mode()流的库。以下程序:

System.out.println(
    Seq.of(1, 3, 4, 3, 4, 3, 2, 3, 3, 3, 3, 3)
       .mode()
);

Yields:

产量:

Optional[3]

For simplicity's sake, I omitted using BigDecimal. The solution would be the same, though.

为简单起见,我省略了使用BigDecimal. 不过,解决方案是一样的。

(disclaimer: I work for the company behind jOOλ)

(免责声明:我为 jOOλ 背后的公司工作)

回答by Sandeep Sharda

We can do in only one iteration with ease:

我们可以轻松地在一次迭代中完成:

public static Integer mostFrequent(List<Integer> list) {

    if (list == null || list.isEmpty())
        return null;

    Map<Integer, Integer> counterMap = new HashMap<Integer, Integer>();
    Integer maxValue = 0;
    Integer mostFrequentValue = null;

    for(Integer valueAsKey : list) {
        Integer counter = counterMap.get(valueAsKey);
        counterMap.put(valueAsKey, counter == null ? 1 : counter + 1);
        counter = counterMap.get(valueAsKey);
        if (counter > maxValue) {
            maxValue = counter;
            mostFrequentValue = valueAsKey;
        }
    }
    return mostFrequentValue;
}

回答by nejckorasa

Find most frequent item in collection:

查找集合中出现频率最高的项目:

private <V> V findMostFrequentItem(final Collection<V> items)
{
  return items.stream()
      .filter(Objects::nonNull)
      .collect(Collectors.groupingBy(Functions.identity(), Collectors.counting())).entrySet().stream()
      .max(Comparator.comparing(Entry::getValue))
      .map(Entry::getKey)
      .orElse(null);
}