计算java集合中出现次数的优雅方法

给定一组可能重复的对象,我想最终得到每个对象的出现次数。 我通过初始化一个空Map ,然后迭代Collection并将对象映射到它的计数(每次map已经包含对象时递增计数)来实现。

  public Map countOccurrences(Collection list){ Map occurrenceMap = new HashMap(); for(Object obj: list){ Integer numOccurrence = occurrenceMap.get(obj); if(numOccurrence == null){ //first count occurrenceMap.put(obj, 1); } else{ occurrenceMap.put(obj, numOccurrence++); } } return occurrenceMap; } 

对于计算出现次数的简单逻辑,这看起来过于冗长。 有更优雅/更短的方式吗? 我对一种完全不同的算法或java语言特定function持开放态度,允许更短的代码。

查看Guava的Multiset 。 几乎就是你要找的东西。

不幸的是它没有addAll(Iterable iterable)函数,但是在你的集合中调用add(E e)的简单循环很容易。

编辑

我的错误,它确实有一个addAll方法 – 因为它必须,因为它实现了Collection。

现在让我们尝试一些Java 8代码:

 static public Map toMap(List lst){ return lst.stream() .collect(HashMap::new, (map,str) ->{ if(!map.containsKey(str)){ map.put(str,1); }else{ map.put(str,map.get(str)+1); } }, HashMap::putAll); } static public Map toMap(List lst){ return lst.stream().collect(Collectors.groupingBy(s -> s, Collectors.counting())); } 

我觉得这段代码比较优雅

查看本文如何计算List中元素的出现次数 。 要计算int occurrences = Collections.frequency(list, obj);可以使用int occurrences = Collections.frequency(list, obj);

我知道这是一个老问题,但我在Java 8中找到了一种更优雅的方式来计算这些投票,希望你喜欢它。

 Map map = a.getSomeStringList() .stream() .collect(Collectors.groupingBy( Function.identity(), Collectors.counting()) ); 

任何错误,只需评论。

这里有一篇关于java中的计数器的好文章: http : //www.programcreek.com/2013/10/efficient-counter-in-java/它更注重效率而不是优雅。

获胜者是这样的:

 HashMap intCounter = new HashMap(); for (int i = 0; i < NUM_ITERATIONS; i++) { for (String a : sArr) { int[] valueWrapper = intCounter.get(a); if (valueWrapper == null) { intCounter.put(a, new int[] { 1 }); } else { valueWrapper[0]++; } } } 

这不是Java的冗长;)你可以使用TObjectIntHashMap

 public  TObjectIntHashMap countOccurrences(Iterable list){ TObjectIntHashMap counts = new TObjectIntHashMap(); for(T obj: list) counts.adjustOrPut(obj, 1, 1); return counts; } 

作为对@NimChimpsky讨论的回应,这里有一个替代方案,也是更快 – 我试图certificate – 计数方法使用排序集合。 根据元素的数量和“sortFactor”(参见代码),速度差异会有所不同,但对于运行环境(而不是调试)中的大量对象,我的方法相对于默认方法的速度提高了20-30%。 这是两种方法的简单测试类。

 public class EltCountTest { final static int N_ELTS = 10000; static final class SampleCountedObject implements Comparable { int value = 0; public SampleCountedObject(int value) { super(); this.value = value; } @Override public int compareTo(SampleCountedObject o) { return (value == o.value)? 0:(value > o.value)?1:-1; // just *a* sort } @Override public int hashCode() { return value; } @Override public boolean equals(Object obj) { if (obj instanceof SampleCountedObject) { return value == ((SampleCountedObject)obj).value; } return false; } @Override public String toString() { return "SampleCountedObject("+value+")"; } } /** * * @param args */ public static void main(String[] args) { int tries = 10000; int sortFactor = 10; Map map1 = null; Map map2 = null; ArrayList objList = new ArrayList(N_ELTS); for (int i =0, max=N_ELTS/sortFactor; i method1(ArrayList objList) { Map occurenceMap = new HashMap(); for(SampleCountedObject obj: objList){ Integer numOccurrence = occurenceMap.get(obj); if(numOccurrence == null){ occurenceMap.put(obj, 1); } else { occurenceMap.put(obj, ++numOccurrence); } } return occurenceMap; } private static Map metod2(ArrayList objList) { Map occurenceMap = new HashMap(); int count = 0; Collections.sort(objList); SampleCountedObject prevObj = objList.get(0); for(SampleCountedObject obj: objList){ if (!obj.equals(prevObj)) { occurenceMap.put(prevObj, count); count = 1; } else { count ++; } prevObj = obj; } occurenceMap.put(prevObj, count); return occurenceMap; } } 

请注意,我还validation结果是否相同,并在打印测试结果后执行此操作。

我发现有趣的是,在Debug运行中,我的方法比原始方法慢得多(10-20%,再次 – 取决于集合中的元素数量)。

请参考以下解决方案来计算集合中的每个元素。

对于整数值:

 List list = new ArrayList(); list.add(3); list.add(2); list.add(5); list.add(1); list.add(8); list.add(0); list.add(2); list.add(32); list.add(72); list.add(0); list.add(13); list.add(32); list.add(73); list.add(22); list.add(73); list.add(73); list.add(21); list.add(73); HashSet set = new HashSet<>(); for (int j = 0; j < list.size(); j++) { set.add(list.get(j)); } Iterator itr = set.iterator(); while(itr.hasNext()){ int a = itr.next(); System.out.println(a+ " : "+Collections.frequency(list, a)); } 

输出:

 0 : 2 32 : 2 1 : 1 2 : 2 3 : 1 5 : 1 21 : 1 22 : 1 8 : 1 72 : 1 73 : 4 13 : 1 

对于字符串值:

 List stringList = new ArrayList<>(); stringList.add("ABC"); stringList.add("GHI"); stringList.add("ABC"); stringList.add("DEF"); stringList.add("ABC"); stringList.add("GHI"); HashSet setString = new HashSet<>(); for (int j = 0; j < stringList.size(); j++) { setString.add(stringList.get(j)); } Iterator itrString = setString.iterator(); while(itrString.hasNext()){ String a = itrString.next(); System.out.println(a+ " ::: "+Collections.frequency(stringList, a)); } 

输出:

 ABC ::: 3 DEF ::: 1 GHI ::: 2 

commons-collections有一个方法: CollectionUtils.getCardinalityMap正是这样做的。

Java是一种冗长的语言,我不认为有更简单的方法可以实现,除非使用第三方库或等待Java 8的Lambda Expression。