Java-получить наиболее общий элемент в списке
Есть ли у Java или Guava что-то, что вернет наиболее общий элемент в список?
List<BigDecimal> listOfNumbers= new ArrayList<BigDecimal>();
[1,3,4,3,4,3,2,3,3,3,3,3]
return 3
Ответы
Ответ 1
Это довольно легко реализовать:
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
Если вы хотите обрабатывать случаи, когда есть более один наиболее частый элемент, вы можете сканировать список один раз, чтобы определить, сколько раз чаще всего встречаются элементы (ы), а затем снова просматривать список, помещать эти элементы в установите и верните это.
Ответ 2
Возможно, самое простое решение с Guava выглядит как
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();
}
}
Это полное решение и короче, чем другие альтернативы, которые я вижу.
Ответ 3
В статистике это называется "режимом" . Решение Vanilla Java 8 выглядит так:
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);
Что дает:
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λ)
Ответ 4
Вот чистое решение 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 answer, написанный с использованием 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);
Обновление: Если наиболее частые элементы более одного, и вы хотите получить их все в коллекции, я предлагаю два метода:
Метод 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());
Метод 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());
Ответ 5
Guava предоставляет метод который поможет, хотя и менее эффективен, чем решение Луи.
BigDecimal mostCommon =
Multisets.copyHighestCountFirst(ImmutableMultiset.copyOf(listOfNumbers))
.iterator().next();
Ответ 6
Классический способ сделать это - сортировать список, а затем работать через них один за другим:
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;
}
Это немного больше пространства, чем использование хэша для подсчета очков, поскольку он сортирует массив на месте. Вы можете подбросить это в класс generics и заменить BigInteger на T или просто использовать Object вместо BigInteger.
Ответ 7
Вот расширение ответа Луи, которое поддерживает случай, когда имеется несколько элементов с таким же количеством количества ошибок:
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;
}
Ответ 8
Мы можем делать только с одной итерацией:
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;
}
Ответ 9
Если вы хотите использовать 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
по его значениям, по убыванию.