2013-09-26 41 views
14

Java或Guava是否有返回列表中最常見元素的東西?Java - 獲取列表中最常見的元素

List<BigDecimal> listOfNumbers= new ArrayList<BigDecimal>(); 

[1,3,4,3,4,3,2,3,3,3,3,3]

回報3

+3

如果什麼有兩個最出現的元素? –

+0

好問題...... –

+0

你確定你需要BigDecimal嗎? –

回答

17

這是非常容易實現自己:

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 

如果要處理多於一個最常見元素的情況,可以掃描一次列表以確定最常出現的元素出現的次數,然後再次掃描列表,將這些元素置於一個集合中並返回。

+0

如果輸入列表爲空,則return語句將導致NullPointerException。即使你不希望永遠是空的,像這樣的東西會更安全:'return max == null? null:max.getKey();' – k2col

+0

這忽略了列表可能包含n個不同元素的情況。在這種情況下,沒有最常見的元素。如果(map.size()== list.size()){return null;} – gidim

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。

+0

該算法具有'O(N * log N)'複雜度,可以在'O(N)'中完成。 –

0

如果您願意使用谷歌番石榴,您可以使用它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其值下降。

14

可能與番石榴最簡單的辦法看起來像

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(); 
    } 
} 

這是一個完整的解決方案,而且比我看到討論的其它替代短。

4

番石榴提供了一個method,雖然它比路易的解決方案效率低,但會有所幫助。

BigDecimal mostCommon = 
    Multisets.copyHighestCountFirst(ImmutableMultiset.copyOf(listOfNumbers)) 
     .iterator().next(); 
1

這裏是路易的答案的擴展支持,其中有這樣的情形與相同的最高出現多個元素計:

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

這是一個純粹的的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()); 
+2

該算法在'O(N)'中有可行的'O(N^2)'複雜度... –

+1

@LukasEder true,反覆調用'Collections.frequency()'是不可行的,我沒有考慮複雜性寫答案。儘管編輯並增加了另一個解決方案,它具有'O(N)'複雜性。 –

+0

非常好的替代解決方案! –

11

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 

jOOλ是支持流mode()庫。下面的程序:

System.out.println(
    Seq.of(1, 3, 4, 3, 4, 3, 2, 3, 3, 3, 3, 3) 
     .mode() 
); 

產量:

Optional[3] 

爲了簡便起見,我省略使用BigDecimal。雖然解決方案將是相同的。

(聲明:我身後jOOλ公司工作)

+0

這是最乾淨的例子,很棒的工作 – Pumphouse

+0

t - > t lambda表達式可以用Functions.identity()替換。我已經發布了一個修改建議並進行了適當的更改。 –

+0

@MarcinKłopotek:謝謝,是的,爲什麼不 –

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; 
}