JavaまたはGuavaには、リスト内の最も一般的な要素を返すものがありますか?
List<BigDecimal> listOfNumbers= new ArrayList<BigDecimal>();
[1,3,4,3,4,3,2,3,3,3,3,3]
リターン3
これは自分で実装するのはかなり簡単です:
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ソリューションは次のようになります:
_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λの背後にある会社で働いています)
おそらくグアバを使用した最も簡単な解決策は次のようになります
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();
}
}
それは完全な解決策であり、私が議論した他の選択肢よりも短いです。
これは純粋な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());
Guavaは method を提供しますが、Louisのソリューションより効率的ではありません。
BigDecimal mostCommon =
Multisets.copyHighestCountFirst(ImmutableMultiset.copyOf(listOfNumbers))
.iterator().next();
これを行うための古典的な方法は、リストをソートしてから、それらを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を使用することができます。
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;
}
これは、同じ最大出現数の要素が複数ある場合をサポートするルイの回答の拡張です。
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;
}
コレクションで最も頻度の高いアイテムを検索します。
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);
}
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
を値の降順で並べ替えます。