2010-12-03 4 views
31

Betrachten Sie dieses Beispiel, das einige Gerätetyp-Statistiken ausgibt. („Device“ ist eine Enumeration mit dozenish Werten.)Einfachste Möglichkeit, ein Multiset in der Reihenfolge der Elementhäufigkeit zu durchlaufen?

Multiset<DeviceType> histogram = getDeviceStats(); 
for (DeviceType type : histogram.elementSet()) { 
    System.out.println(type + ": " + histogram.count(type)); 
} 

Was ist die einfachste, elegante Weise, die verschiedene Elemente in der Reihenfolge ihrer Häufigkeit (häufigste Art zuerst) zu drucken?

Mit einem kurzen Blick auf die Multiset Schnittstelle, gibt es keine fertige Methode hierfür, und keiner von Multiset Implementierungen des Guava (HashMultiset, TreeMultiset usw.) scheinen automatisch Elemente zu halten frequenz bestellt entweder.

+6

https://code.google.com/p/guava-libraries/issues/detail?id=356 – Bozho

Antwort

37

ich gerade um diese Funktion zu Guava hinzugefügt, siehe here für die Javadoc.

bearbeiten: Verwendungsbeispiel von Multisets.copyHighestCountFirst() gemäß der ursprünglichen Frage:

Multiset<DeviceType> histogram = getDeviceStats(); 
for (DeviceType type : Multisets.copyHighestCountFirst(histogram).elementSet()) { 
    System.out.println(type + ": " + histogram.count(type)); 
} 
+0

Wow, danke! Also wird dies offenbar in Guava Release 11 enthalten sein? – Jonik

+1

Ja. (Ich war der Guava Praktikant in diesem Sommer.) Es könnte jedoch umbenannt werden; siehe http://code.google.com/p/guava-libraries/issues/detail?id=356. –

+0

Ich habe mir die Freiheit genommen, ein Codebeispiel hinzuzufügen. Auch dies wird jetzt als akzeptiert markiert. Danke nochmal für die Implementierung des Features! – Jonik

7

Hier ist eine Methode, die eine List von Einträgen zurückgibt, sortiert nach Häufigkeit (UPDATE: verwendet, um eine Fahne auf-/absteigend zu wechseln und verwendet Guava Lieblingsspielzeug: die Enum Singleton Pattern, wie gefunden in Effective Java Punkt 3):

private enum EntryComp implements Comparator<Multiset.Entry<?>>{ 
    DESCENDING{ 
     @Override 
     public int compare(final Entry<?> a, final Entry<?> b){ 
      return Ints.compare(b.getCount(), a.getCount()); 
     } 
    }, 
    ASCENDING{ 
     @Override 
     public int compare(final Entry<?> a, final Entry<?> b){ 
      return Ints.compare(a.getCount(), b.getCount()); 
     } 
    }, 
} 

public static <E> List<Entry<E>> getEntriesSortedByFrequency(
    final Multiset<E> ms, final boolean ascending){ 
    final List<Entry<E>> entryList = Lists.newArrayList(ms.entrySet()); 
    Collections.sort(entryList, ascending 
     ? EntryComp.ASCENDING 
     : EntryComp.DESCENDING); 
    return entryList; 
} 

Code Test:

final Multiset<String> ms = 
    HashMultiset.create(Arrays.asList(
     "One", 
     "Two", "Two", 
     "Three", "Three", "Three", 
     "Four", "Four", "Four", "Four" 
    )); 

System.out.println("ascending:"); 
for(final Entry<String> entry : getEntriesSortedByFrequency(ms, true)){ 
    System.out.println(MessageFormat.format("{0} ({1})", 
     entry.getElement(), entry.getCount())); 
} 

System.out.println("descending:"); 
for(final Entry<String> entry : getEntriesSortedByFrequency(ms, false)){ 
    System.out.println(MessageFormat.format("{0} ({1})", 
     entry.getElement(), entry.getCount())); 
} 

Ausgang:

aufsteigend:
Ein (1)
Zwei (2)
Drei (3)
Vier (4)
absteigend:
Vier (4)
Drei (3)
Zwei (2)
Ein (1)

+0

Dies ist ziemlich nett, möglicherweise der einfachste Weg für jetzt, während Multiset selbst es nicht zur Verfügung stellt! – Jonik

+2

(Guava ist so cool; ich hatte zum Beispiel die 'Ints' Klasse vorher nicht bemerkt.Mit Blick auf die API-Dokumentation war "Files" auch für mich neu - eine Util-Sammlung, ähnlich wie bei Commons IO, außer dass generische Unterstützung & irgendwie * sauberer * insgesamt.) – Jonik

3

An Implementation ForwardingMultiSet mit:

(EntryComp von seanizer'sanswer)

enum EntryComp implements Comparator<Multiset.Entry<?>> { 
    DESCENDING { 
     @Override 
     public int compare(final Entry<?> a, final Entry<?> b) { 
      return Ints.compare(b.getCount(), a.getCount()); 
     } 
    }, 
    ASCENDING { 
     @Override 
     public int compare(final Entry<?> a, final Entry<?> b) { 
      return Ints.compare(a.getCount(), b.getCount()); 
     } 
    }, 
} 

public class FreqSortMultiSet<E> extends ForwardingMultiset<E> { 
    Multiset<E> delegate; 
    EntryComp comp; 

    public FreqSortMultiSet(Multiset<E> delegate, boolean ascending) { 
     this.delegate = delegate; 
     if (ascending) 
      this.comp = EntryComp.ASCENDING; 
     else 
      this.comp = EntryComp.DESCENDING; 
    } 

    @Override 
    protected Multiset<E> delegate() { 
     return delegate; 
    } 

    @Override 
    public Set<Entry<E>> entrySet() { 
     TreeSet<Entry<E>> sortedEntrySet = new TreeSet<Entry<E>>(comp); 
     sortedEntrySet.addAll(delegate.entrySet()); 
     return sortedEntrySet; 
    } 

    @Override 
    public Set<E> elementSet() { 
     Set<E> sortedEntrySet = new LinkedHashSet<E>(); 
     for (Entry<E> en : entrySet()) 
      sortedEntrySet.add(en.getElement()); 
     return sortedEntrySet; 
    } 

    public static <E> FreqSortMultiSet<E> create(boolean ascending) { 
     return new FreqSortMultiSet<E>(HashMultiset.<E> create(), ascending); 
    } 

    /* 
    * For Testing 
    * public static void main(String[] args) { 
     Multiset<String> s = FreqSortMultiSet.create(false); 
     s.add("Hello"); 
     s.add("Hello"); 
     s.setCount("World", 3); 
     s.setCount("Bye", 5); 
     System.out.println(s.entrySet()); 
    }*/ 

} 
+0

+1. Elegant, in gewisser Weise, um eine Multiset-Implementierung zu haben, die die gesamte Sortierlogik einschließt und abstrahiert. Client-Code, der dies verwendet, würde einfach bleiben (keine Änderungen erforderlich, um Beispiel in meiner Frage zu codieren, solange FreqSortMultiSet verwendet wurde). Nachteil, natürlich muss etwas mehr Code geschrieben und gepflegt werden als in [SPFloyds Lösung] (http://stackoverflow.com/questions/4345633/simplet-way-to-iterate-through-a-multiset-in) -die-Reihenfolge-der-Element-Frequenz/4346245 # 4346245) ... – Jonik