web-dev-qa-db-ja.com

Java-リスト内の最も一般的な要素を取得する

JavaまたはGuavaには、リスト内の最も一般的な要素を返すものがありますか?

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

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

リターン3

18
Doc Holiday

これは自分で実装するのはかなり簡単です:

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 

最も頻度の高い要素が複数ある場合を処理する場合は、リストを1回スキャンして、最も頻度の高い要素が発生する回数を確認し、リストをもう一度スキャンして、それらの要素をセットに入れて返すことができます。それ。

22
arshajii

統計では、これは「モード」と呼ばれます 。バニラ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);
_

収量:

_3=8
_

jOOλ は、ストリームで mode() をサポートするライブラリです。次のプログラム:

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

収量:

_Optional[3]
_

簡単にするため、BigDecimalの使用を省略しました。ただし、解決策は同じです。

(免責事項:私はjOOλの背後にある会社で働いています)

18
Lukas Eder

おそらくグアバを使用した最も簡単な解決策は次のようになります

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();
  }
}

それは完全な解決策であり、私が議論した他の選択肢よりも短いです。

15
Louis Wasserman

これは純粋な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);

頻度で要素をマップに収集し、最大値のエントリを見つけてそのキーを返す別の解決策(基本的に arshajiiの同じ解決策 、Java 8):

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);

更新:最も頻度の高い要素が複数ある場合、コレクション内のすべての要素を取得するには、2つの方法を提案します。

方法A:元のコレクションを要素としてのキーと出現回数としての値を持つマップに収集した後、最大値を持つエントリを取得してフィルタリングします見つかったこの最大値(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());

方法B:元のコレクションを、要素としてのキーと、出現回数としての値を持つマップに収集した後、このマップをキーを持つ新しいマップに変換します発生数として、この発生数を持つ要素のリストとしての値。次に、キーを比較するカスタムコンパレータを使用してこのマップのmax要素を見つけ、このエントリの値を取得します。このような:

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());
10
Utku Özdemir

Guavaは method を提供しますが、Louisのソリューションより効率的ではありません。

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

これを行うための古典的な方法は、リストをソートしてから、それらを1つずつ処理することです。

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;
}

これは、配列を所定の場所に並べ替えるため、ハッシュを使用してカウントを数えるよりも少しスペース効率的です。これを総称クラスに投げてBigIntegerをTに置き換えるか、BigIntegerの代わりにObjectを使用することができます。

3
Alcanzar

1回の反復で簡単に実行できます:

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;
}
1
Sandeep Sharda

これは、同じ最大出現数の要素が複数ある場合をサポートするルイの回答の拡張です。

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;
}
1
a.b.d

コレクションで最も頻度の高いアイテムを検索します。

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);
}
0
nejckorasa

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);

次に、copiesを値の降順で並べ替えます。

0
Eric Jablow