Est-ce que Java ou Guava ont quelque chose qui retournera l'élément le plus courant dans une liste?
List<BigDecimal> listOfNumbers= new ArrayList<BigDecimal>();
[1,3,4,3,4,3,2,3,3,3,3,3]
retour 3
C'est assez facile à mettre en œuvre vous-même:
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
Si vous souhaitez gérer les cas dans lesquels il y a plus d'un élément le plus fréquent, vous pouvez parcourir la liste une fois pour déterminer le nombre d'occurrences du ou des éléments les plus fréquents, puis parcourir à nouveau la liste, placer ces éléments dans un ensemble et les renvoyer. cette.
Probablement la solution la plus simple avec Guava ressemble à
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();
}
}
C'est une solution complète et plus courte que les autres alternatives que je vois discutées.
En statistique, cela s'appelle le "mode" . Une solution Vanilla Java 8 ressemble à ceci:
Stream.of(1, 3, 4, 3, 4, 3, 2, 3, 3, 3, 3, 3)
.collect(Collectors.groupingBy(Functions.identity(), Collectors.counting()))
.entrySet()
.stream()
.max(Comparator.comparing(Entry::getValue))
.ifPresent(System.out::println);
Quels rendements:
3=8
jOOλ est une bibliothèque qui prend en charge mode()
sur les flux. Le programme suivant:
System.out.println(
Seq.of(1, 3, 4, 3, 4, 3, 2, 3, 3, 3, 3, 3)
.mode()
);
Rendements:
Optional[3]
Par souci de simplicité, j'ai omis d'utiliser BigDecimal
. La solution serait la même, cependant.
(disclaimer: je travaille pour la société derrière jOOλ)
Voici une solution pure Java 8 (note: n'utilisez pas celle-ci, voir ci-dessous):
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);
Une autre solution consiste à collecter les éléments d’une carte par leur fréquence, puis à rechercher l’entrée avec la valeur max et à la restituer (la même solution sur la réponse de arshajii , écrite en 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);
Mise à jour: Si les éléments les plus fréquents sont plusieurs, et que vous voulez tous les avoir dans une collection, je propose deux méthodes:
Méthode A: Après avoir collecté la collection d'origine sur une carte avec des clés en tant qu'éléments et des valeurs en nombre d'occurrences, obtenir l'entrée avec la valeur maximale et filtrer les entrées de la carte avec une valeur égale à cette valeur maximale (si ) nous avons trouvé. Quelque chose comme ça:
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());
Méthode B: Après avoir collecté la collection d'origine sur une carte avec des clés en tant qu'éléments et des valeurs en nombre d'occurrences, la transformation de cette carte en une nouvelle carte avec des clés en nombre d'occurrences, des valeurs en tant que liste d'éléments ce nombre d'occurrences. Et ensuite, trouver l'élément max de cette carte avec un comparateur personnalisé qui compare les clés et obtenir la valeur de cette entrée. Comme ça:
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 fournit une méthode qui vous aidera, même si elle est moins efficace que la solution de Louis.
BigDecimal mostCommon =
Multisets.copyHighestCountFirst(ImmutableMultiset.copyOf(listOfNumbers))
.iterator().next();
La méthode classique consiste à trier la liste, puis à la parcourir une par une:
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;
}
C’est un peu moins encombrant que d’utiliser un hachage pour compter, car cela trie le tableau en place. Vous pouvez lancer ceci dans une classe de génériques et remplacer BigInteger par T ou simplement utiliser Object à la place de BigInteger.
Nous pouvons faire en une seule itération avec facilité:
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;
}
Voici une extension de la réponse de Louis qui prend en charge le cas où plusieurs éléments ont le même nombre d'occurrences maximum:
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;
}
Trouver l'élément le plus fréquent dans la 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);
}
Si vous souhaitez utiliser Google Guava, vous pouvez utiliser ses classes 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);
Maintenant, triez copies
par ses valeurs décroissantes.