Simplest way to iterate through a Multiset in the order of element frequency?

前端 未结 4 674
时光说笑
时光说笑 2020-11-29 08:01

Consider this example which prints out some device type stats. (\"DeviceType\" is an enum with a dozenish values.)

Multiset histogram = get         


        
相关标签:
4条回答
  • 2020-11-29 08:40

    Since it is not yet implemented, I guess you can create a Map with key=type and value=count. Then sort that map - see here

    0 讨论(0)
  • 2020-11-29 08:42

    Here's a method that returns a List of entries, sorted by frequency (UPDATE: used a flag to toggle ascending / descending order and used Guava's favorite toy: the Enum Singleton Pattern, as found in Effective Java, Item 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;
    }
    

    Test code:

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

    Output:

    ascending:
    One (1)
    Two (2)
    Three (3)
    Four (4)
    descending:
    Four (4)
    Three (3)
    Two (2)
    One (1)

    0 讨论(0)
  • 2020-11-29 08:54

    I just added this feature to Guava, see here for the Javadoc.

    Edit: usage example of Multisets.copyHighestCountFirst() as per the original question:

    Multiset<DeviceType> histogram = getDeviceStats();
    for (DeviceType type : Multisets.copyHighestCountFirst(histogram).elementSet()) {
        System.out.println(type + ": " + histogram.count(type));
    }
    
    0 讨论(0)
  • 2020-11-29 08:58

    An Implementation using ForwardingMultiSet :

    (EntryComp from seanizer's answer)

    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 讨论(0)
提交回复
热议问题