【问题标题】:how to convert HashMultiset<String> to Map<String,Integer>如何将 HashMultiset<String> 转换为 Map<String,Integer>
【发布时间】:2014-01-13 14:45:30
【问题描述】:

除了迭代Set 中的所有条目之外,是否有一些技巧可以将HashMultiset&lt;String&gt; 转换为Map&lt;String,Integer&gt;?
更新:Integer 应该表示多重集中 String 的计数。

【问题讨论】:

  • HashMultiset 是一个集合。您对地图的期望究竟是什么结果? map 中的键和值应该是什么?
  • @Jesper HashMultiset 不是一个集合(在 JDK 的集合中)。
  • 我认为没有直接的方法可以做到这一点。你也许可以写下你自己的方法,我猜这几乎不会是 5-6 行代码。
  • @Jesper 他可能想要将唯一字符串映射到它们的映射。
  • @oshai:当然:Multiset.entrySet() 可以做到这一点。

标签: java collections guava


【解决方案1】:

您可以使用Maps.asMap。使用 lambda 表达式 (Java 8) 它将是一个单行:

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

在 Java 7 及以下版本中:

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

【讨论】:

    【解决方案2】:

    更新到 java 8,这是我发现的最佳答案(基于其他答案):

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

    或:

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

    【讨论】:

      【解决方案3】:

      使用Eclipse Collections,您可以在Bag(又名Multiset)上使用toMapOfItemToCount 方法,这将返回一个Map,并在Bag 和Integer 计数中使用相同类型的键.

      注意:我是 Eclipse 集合的提交者。

      【讨论】:

        【解决方案4】:

        您可以简单地遍历条目并将元素和计数放入地图。

        public class MultisetToMap {
          public static <E> Map<E, Integer> convert(Multiset<E> multiset) {
            Map<E, Integer> map = Maps.newHashMap();
            for (E e : multiset) {
              multiset.count(e);
              map.put(e, multiset.count(e));
            }
        
            return map;
          }
        }
        

        下面是(通过)JUnit 测试。

         @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 = MultisetToMap.convert(hashMultiset);
            assertEquals((Integer) 3, map.get("a"));
            assertEquals((Integer) 1, map.get("b"));
            assertEquals((Integer) 1, map.get("c"));
          }
        

        【讨论】:

          【解决方案5】:

          如果你真的想避免循环遍历 Multiset 的条目,你可以创建一个 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;
            }
          
          }
          

          在我的实现中,我拒绝实现containsValue 和values 方法,因为它们在上下文中没有用。如果需要,可以通过遍历条目并检查遇到的元素的count 来实现这些。

          再一次,您可以在这个 JUnit 案例中看到这一点:

            @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"));
            }
          

          【讨论】:

            【解决方案6】:

            这是可能的,但只能使用反射,而且看起来很不安全。

                HashMultiset<String> hashMultiset = HashMultiset.create();
            
                hashMultiset.add("a");
                hashMultiset.add("a");
                hashMultiset.add("a");
                hashMultiset.add("b");
                hashMultiset.add("c");
            
                System.out.println(hashMultiset);
            
                Method method = hashMultiset.getClass().getSuperclass().getDeclaredMethod("backingMap");
                method.setAccessible(true);
            
                Map<String, Integer> map = (Map<String, Integer>) method.invoke(hashMultiset);
            
                System.out.println(map);
            

            结果:

            [b, c, a x 3]
            {b=1, c=1, a=3}
            

            【讨论】:

            • 不,这行不通。你会得到一个Map&lt;String, Count&gt;,其中Count 是一个本地类;您实际上不能将这些值视为Integer 值。
            • ... javac 实际上会在编译上述内容时向您发出警告。
            猜你喜欢
            • 1970-01-01
            • 2019-12-06
            • 1970-01-01
            • 1970-01-01
            • 2022-09-30
            • 1970-01-01
            • 2014-09-24
            • 2023-03-26
            • 1970-01-01
            相关资源
            最近更新 更多