Java または Guava には、リスト内の最も一般的な要素を返すものがありますか?
List<BigDecimal> listOfNumbers= new ArrayList<BigDecimal>();
[1,3,4,3,4,3,2,3,3,3,3,3]
リターン 3
統計では、これは「モード」と呼ばれます。バニラ 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(Map.Entry.comparingByValue())
.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λの背後にある会社で働いています)
これは自分で実装するのはかなり簡単です:
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 回スキャンして、最も頻繁に発生する要素が何回発生するかを判断し、リストを再度スキャンして、それらの要素をセットに入れて返すことができます。それ。
これは純粋な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);
要素を頻度でマップに収集し、最大値を持つエントリを見つけてそのキーを返す別のソリューション (基本的に、 Java 8 を使用して記述された、 arshajii の 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);
更新:最も頻繁に使用される要素が複数あり、それらすべてをコレクションに入れたい場合は、次の 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:元のコレクションを要素としてキー、出現回数として値を持つマップに収集した後、このマップを、キーを出現回数、値をこの出現回数の要素のリストとして持つ新しいマップに変換します。次に、キーを比較するカスタム コンパレータを使用してこのマップの最大要素を見つけ、このエントリの値を取得します。このような:
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());
Guava は役立つ方法を提供していますが、Louis のソリューションよりも効率的ではありません。
BigDecimal mostCommon =
Multisets.copyHighestCountFirst(ImmutableMultiset.copyOf(listOfNumbers))
.iterator().next();
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
次に、その値で降順に並べ替えます。