Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to convert HashMultiset<String> to Map<String,Integer>

Is there some trick to convert HashMultiset<String> to Map<String,Integer>, except from iterating all the entries in the Set?
Update: The Integer should represent the count of String in the multiset.

like image 922
oshai Avatar asked Jan 13 '14 14:01

oshai


4 Answers

You can use Maps.asMap. With lambda expression (Java 8) it will be a one-liner:

Maps.asMap(multiset.elementSet(), elem -> multiset.count(elem));

In Java 7 and below:

final Multiset<String> multiset = HashMultiset.create();
Map<String, Integer> freqMap = Maps.asMap(multiset.elementSet(), 
    new Function<String, Integer>() {
        @Override
        public Integer apply(String elem) {
            return multiset.count(elem);
        }
    });
like image 112
Lukasz Wiktor Avatar answered Oct 16 '22 20:10

Lukasz Wiktor


Updated to java 8, here is what I found as the best answer (based on other answers):

public static <E> Map<E, Integer> convert(Multiset<E> multiset) {
    return multiset.entrySet().stream().collect(
        Collectors.toMap(x->x.getElement(),x->x.getCount()));
}

or:

public static <E> Map<E, Integer> convert(Multiset<E> multiset) {
    return multiset.entrySet().stream().collect(
        Collectors.toMap(Entry::getElement,Entry::getCount));
}
like image 23
oshai Avatar answered Oct 16 '22 18:10

oshai


With Eclipse Collections you can use the method toMapOfItemToCount on a Bag (aka Multiset), which will return a Map with a key of the same type in the Bag and an Integer count.

Note: I am a committer for Eclipse collections.

like image 21
Donald Raab Avatar answered Oct 16 '22 18:10

Donald Raab


If you really want to avoid looping through the entries of the Multiset, you can create a view of it as a Map:

public class MultisetMapView<E> implements Map<E, Integer> {
  private Multiset<E> delegate;

  public MultisetMapView(Multiset<E> delegate) {
    this.delegate = delegate;
  }

  public int size() {
    return delegate.size();
  }

  public boolean isEmpty() {
    return delegate.isEmpty();
  }

  public boolean containsKey(Object key) {
    return delegate.contains(key);
  }

  public boolean containsValue(Object value) {
    throw new UnsupportedOperationException();
  }

  public Integer get(Object key) {
    return delegate.count(key);
  }

  public Integer put(E key, Integer value) {
    return delegate.setCount(key, value);
  }

  public Integer remove(Object key) {
    int count = delegate.count(key);
    delegate.remove(key);
    return count;
  }

  public void putAll(Map<? extends E, ? extends Integer> m) {
    for (Entry<? extends E, ? extends Integer> entry : m.entrySet()) {
      delegate.setCount(entry.getKey(), entry.getValue());
    }
  }

  public void clear() {
    delegate.clear();
  }

  public Set<E> keySet() {
    return delegate.elementSet();
  }

  public Collection<Integer> values() {
    throw new UnsupportedOperationException();
  }

  public Set<java.util.Map.Entry<E, Integer>> entrySet() {
    Set<java.util.Map.Entry<E, Integer>> entrySet = Sets.newHashSet();
    for (E e : delegate) {
      delegate.count(e);
      entrySet.add(Maps.immutableEntry(e, delegate.count(e)));
    }

    return entrySet;
  }

}

In my implementation, I declined to implement the containsValue and values methods, as these are not useful in the context. If desired, these could be implemented by looping through the entries and inspecting the count of the elements encountered.

And again, you can see this working in this JUnit case:

  @Test
  public void testConvert() {
    HashMultiset<String> hashMultiset = HashMultiset.create();

    hashMultiset.add("a");
    hashMultiset.add("a");
    hashMultiset.add("a");
    hashMultiset.add("b");
    hashMultiset.add("c");

    Map<String, Integer> map = new MultisetMapView<String>(hashMultiset);
    assertEquals((Integer) 3, map.get("a"));
    assertEquals((Integer) 1, map.get("b"));
    assertEquals((Integer) 1, map.get("c"));
  }
like image 45
Ray Avatar answered Oct 16 '22 18:10

Ray