Java或Guava是否有返回列表中最常見元素的東西?Java - 獲取列表中最常見的元素
List<BigDecimal> listOfNumbers= new ArrayList<BigDecimal>();
[1,3,4,3,4,3,2,3,3,3,3,3]
回報3
Java或Guava是否有返回列表中最常見元素的東西?Java - 獲取列表中最常見的元素
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
如果要處理多於一個最常見元素的情況,可以掃描一次列表以確定最常出現的元素出現的次數,然後再次掃描列表,將這些元素置於一個集合中並返回。
經典的方式做,這是對列表進行排序,然後通過逐一工作:
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;
}
這是多一點空間比使用哈希因爲它排序陣列相符計數效率到位。你可以把它扔到一個泛型類中,用T替換BigInteger,或者只用Object代替BigInteger。
該算法具有'O(N * log N)'複雜度,可以在'O(N)'中完成。 –
如果您願意使用谷歌番石榴,您可以使用它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
其值下降。
可能與番石榴最簡單的辦法看起來像
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();
}
}
這是一個完整的解決方案,而且比我看到討論的其它替代短。
番石榴提供了一個method,雖然它比路易的解決方案效率低,但會有所幫助。
BigDecimal mostCommon =
Multisets.copyHighestCountFirst(ImmutableMultiset.copyOf(listOfNumbers))
.iterator().next();
這裏是路易的答案的擴展支持,其中有這樣的情形與相同的最高出現多個元素計:
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;
}
這是一個純粹的的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's 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());
該算法在'O(N)'中有可行的'O(N^2)'複雜度... –
@LukasEder true,反覆調用'Collections.frequency()'是不可行的,我沒有考慮複雜性寫答案。儘管編輯並增加了另一個解決方案,它具有'O(N)'複雜性。 –
非常好的替代解決方案! –
In statistics, this is called the "mode"。香草的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
System.out.println(
Seq.of(1, 3, 4, 3, 4, 3, 2, 3, 3, 3, 3, 3)
.mode()
);
產量:
Optional[3]
爲了簡便起見,我省略使用BigDecimal
。雖然解決方案將是相同的。
(聲明:我身後jOOλ公司工作)
這是最乾淨的例子,很棒的工作 – Pumphouse
t - > t lambda表達式可以用Functions.identity()替換。我已經發布了一個修改建議並進行了適當的更改。 –
@MarcinKłopotek:謝謝,是的,爲什麼不 –
我們只能用一種輕鬆的迭代做:
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;
}
如果什麼有兩個最出現的元素? –
好問題...... –
你確定你需要BigDecimal嗎? –