2013-09-26 13 views
14

Hat Java oder Guava etwas, das das häufigste Element in einer Liste zurückgibt?Java-get am häufigsten verwendetes Element in einer Liste

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

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

Rückkehr 3

+3

Was passiert, wenn es zwei am meisten vorkommenden Elemente sind? –

+0

gute Frage ...... –

+0

Sind Sie sicher, dass Sie BigDecimal hier brauchen? –

Antwort

17

Das ist ziemlich einfach, sich selbst zu implementieren:

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 

Wenn Sie Fälle behandeln möchten, in denen es mehr als ein häufigstes Element gibt, können Sie die Liste einmal scannen, um zu ermitteln, wie oft die häufigsten Elemente auftreten, und dann die Liste erneut scannen und diese Elemente in ein Set einfügen und gib das zurück.

+0

Wenn die Eingabeliste leer ist, wird die Rückgabeanweisung eine NullPointerException verursachen.Selbst wenn Sie nicht erwarten, jemals leer zu sein, wäre so etwas sicherer: 'return max == null? null: max.getKey(); ' – k2col

+0

Das ignoriert die Fälle, in denen eine Liste n verschiedene Elemente enthalten könnte. In diesem Fall gibt es kein allgemeines Element. if (map.size() == list.size()) {return null;} – gidim

3

Der klassische Weg, dies zu tun ist, um die Liste zu sortieren und dann durch sie eins nach dem anderen arbeiten:

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

Dies ist ein platzsparender als einen Hash mit Zählungen Bit tally, da es das Array sortiert an Ort und Stelle. Sie können dies in eine Generikaklasse einfügen und BigInteger durch T ersetzen oder einfach Object anstelle von BigInteger verwenden.

+0

Dieser Algorithmus hat 'O (N * log N)' Komplexität für etwas, das in 'O (N)' –

0

Wenn Sie bereit sind, Google Guava zu verwenden, können Sie seine MultiSet Klassen verwenden:

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

Nun sortieren copies durch ihre Werte absteigend.

14

wahrscheinlich die einfachste Lösung mit Guava sieht aus wie

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

, dass eine vollständige Lösung ist, und kürzer als die anderen Alternativen, die ich diskutiert sehen.

4

Guava bietet eine method, die helfen wird, obwohl es weniger effizient als Louis Lösung ist.Hier

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

ist eine Erweiterung von Louis' Antwort, die den Fall unterstützen, wo es mehrere Elemente mit gleichen max Vorkommen zählen:

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

Hier ist eine reine Java 8 Lösung (Anmerkung: do verwenden Sie nicht dieses, siehe unten):

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

eine andere Lösung, indem Sie die Elemente auf einer Karte durch ihre Häufigkeit zu sammeln, dann den Eintrag wi finden th Maximalwert und dessen Schlüssel der Rückkehr (im Grunde die gleiche Lösung auf arshajii's answer, geschrieben mit 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); 

Update: Wenn die häufigsten Elemente sind mehr als ein, und Sie wollen, dass alle von ihnen bekommen in einer Sammlung schlage ich zwei Methoden:

Methode A: Nachdem die ursprüngliche Sammlung zu einer Karte mit Tasten als Elemente und Werte als die Anzahl der Vorkommen zu sammeln, um den Eintrag mit dem Maximalwert erhalten und Filtern der map-Einträge mit Wert gleich diesem Max-Wert (wenn) wir gefunden haben. Etwa wie folgt:

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

Methode B: Nachdem die ursprüngliche Sammlung zu einer Karte mit Tasten als Elemente und Werte als die Anzahl der Vorkommen zu sammeln, Umwandeln diese Karte in eine neue Karte mit Schlüssel als Anzahl von Vorkommnissen, Werte als eine Liste von Elementen mit dieser Anzahl von Vorkommen. Und dann finden Sie das maximale Element dieser Karte mit einem benutzerdefinierten Komparator, der die Schlüssel vergleicht und den Wert dieses Eintrags ermittelt. Wie folgt aus:

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

Dieser Algorithmus durchgeführt werden kann hat 'O (N^2)' Komplexität für etwas, das in 'O (N)' möglich ist ... –

+1

@LukasEder true, Aufruf 'Collections.frequency()' immer und immer wieder nicht, ich dachte nicht, Komplexität während die Antwort schreiben. Bearbeitet und fügte eine andere Lösung hinzu, die 'O (N) 'Komplexität hat. –

+0

Sehr schöne alternative Lösung! –

11

In statistics, this is called the "mode". Ein Vanille Java 8 Lösung sieht wie folgt aus:

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

Welche ergibt:

3=8 

jOOλ ist eine Bibliothek, die mode() auf Streams unterstützt. Das folgende Programm:

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

Ausbeuten:

Optional[3] 

Der Einfachheit halber weggelassen ich BigDecimal verwenden. Die Lösung wäre jedoch die gleiche.

(Disclaimer: Ich für das Unternehmen hinter jOOλ arbeiten)

+0

Dies ist das sauberste Beispiel, gute Arbeit – Pumphouse

+0

t -> t Lambda-Ausdruck kann mit Functions.identity() ersetzt werden. Ich habe bereits einen Bearbeitungsvorschlag mit entsprechenden Änderungen gepostet. –

+0

@ MarcinKłopotek: Danke, ja, warum nicht –

1

Wir in nur eine Iteration mit Leichtigkeit tun können:

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