如何统计列表中元素的出现次数

我有一个ArrayList ,一个Java的集合类,如下所示:

 ArrayList<String> animals = new ArrayList<String>(); animals.add("bat"); animals.add("owl"); animals.add("bat"); animals.add("bat"); 

如您所见, animals ArrayList由3个bat元素和一个owl元素组成。 我想知道在Collection框架中是否有任何API返回bat出现次数,或者是否有另一种方法来确定出现次数。

我发现Google的Collection Multiset确实有一个API,它返回一个元素出现的总次数。 但是这只与JDK 1.5兼容。 我们的产品目前在JDK 1.6中,所以我不能使用它。

我很确定Collections中的静态频率方法在这里会派上用场:

 int occurrences = Collections.frequency(animals, "bat"); 

无论如何,我就是这么做的。 我很确定这是jdk 1.6的直线上升。

在Java 8中:

 Map<String, Long> counts = list.stream().collect(Collectors.groupingBy(e -> e, Collectors.counting())); 

这显示了为什么“ 有效的Java书”中描述的“按接口引用对象 ”很重要。

如果你编码实现,并使用ArrayList,比如在你的代码中有50个地方,当你find一个很好的“List”实现来计算项目时,你将不得不改变所有这50个地方,破坏你的代码(如果它只被你使用,没有什么大不了的,但是如果它被别人使用,你也会破坏它们的代码)

通过编程到接口,你可以让这50个位置不变,并将ArrayList中的实现replace为“CountItemsList”(例如)或其他类。

下面是关于如何写这个的一个非常基本的例子。 这只是一个例子,一个生产准备清单将会复杂得多。

 import java.util.*; public class CountItemsList<E> extends ArrayList<E> { // This is private. It is not visible from outside. private Map<E,Integer> count = new HashMap<E,Integer>(); // There are several entry points to this class // this is just to show one of them. public boolean add( E element ) { if( !count.containsKey( element ) ){ count.put( element, 1 ); } else { count.put( element, count.get( element ) + 1 ); } return super.add( element ); } // This method belongs to CountItemList interface ( or class ) // to used you have to cast. public int getCount( E element ) { if( ! count.containsKey( element ) ) { return 0; } return count.get( element ); } public static void main( String [] args ) { List<String> animals = new CountItemsList<String>(); animals.add("bat"); animals.add("owl"); animals.add("bat"); animals.add("bat"); System.out.println( (( CountItemsList<String> )animals).getCount( "bat" )); } } 

面向对象的原则在这里应用:inheritance,多态,抽象,封装。

对不起,没有简单的方法调用可以做到这一点。 所有你需要做的是创build一个地图,并计数频率。

 HashMap<String,int> frequencymap = new HashMap<String,int>(); foreach(String a in animals) { if(frequencymap.containsKey(a)) { frequencymap.put(a, frequencymap.get(a)+1); } else{ frequencymap.put(a, 1); } } 

实际上,Collections类有一个静态方法: frequency (Collection c,Object o),它返回你正在search的元素的出现次数,顺便说一下,这对你来说是完美的:

 ArrayList<String> animals = new ArrayList<String>(); animals.add("bat"); animals.add("owl"); animals.add("bat"); animals.add("bat"); System.out.println("Freq of bat: "+Collections.frequency(animals, "bat")); 

我想知道,为什么你不能在JDK 1.6中使用Google的Collection API。 这样说吗? 我认为你可以,不应该有任何兼容性问题,因为它是为较低版本而构build的。 如果这个版本是1.6版本,而且你运行的是1.5版本的话,那么情况就不一样了。

我在哪里错了?

Java中没有本地方法可以为你做。 但是,您可以使用Apache Commons-Collections的IterableUtils#countMatches()为您完成此操作。

一个稍微有效的方法可能是

 Map<String, AtomicInteger> instances = new HashMap<String, AtomicInteger>(); void add(String name) { AtomicInteger value = instances.get(name); if (value == null) instances.put(name, new AtomicInteger(1)); else value.incrementAndGet(); } 

你想要的是一个袋子 – 这就像一个集合,但也计数出现的次数。 不幸的是java集合框架 – 伟大的,因为他们没有一个袋子impl。 为此,必须使用Apache Common Collection 链接文本

使用stream的替代Java 8解决scheme:

 long count = animals.stream().filter(animal -> "bat".equals(animal)).count(); 

要直接从列表中获取对象的出现:

 int noOfOccurs = Collections.frequency(animals, "bat"); 

为了获得列表中Object对象集合的出现,重写Object类中的equals方法,如下所示:

 @Override public boolean equals(Object o){ Animals e; if(!(o instanceof Animals)){ return false; }else{ e=(Animals)o; if(this.type==e.type()){ return true; } } return false; } Animals(int type){ this.type = type; } 

调用Collections.frequency为:

 int noOfOccurs = Collections.frequency(animals, new Animals(1)); 

Java 8 – 另一种方法

 String searched = "bat"; long n = IntStream.range(0, animals.size()) .filter(i -> searched.equals(animals.get(i))) .count(); 

将arrays列表的元素放在哈希映射中来计算频率。

如果您使用Eclipse集合 ,您可以使用一个BagMutableBag可以通过调用toBag()RichIterable的任何实现中返回。

 MutableList<String> animals = Lists.mutable.with("bat", "owl", "bat", "bat"); MutableBag<String> bag = animals.toBag(); Assert.assertEquals(3, bag.occurrencesOf("bat")); Assert.assertEquals(1, bag.occurrencesOf("owl")); 

EC中的HashBag实现由MutableObjectIntMap支持。

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

 List<String> list = Arrays.asList("as", "asda", "asd", "urff", "dfkjds", "hfad", "asd", "qadasd", "as", "asda", "asd", "urff", "dfkjds", "hfad", "asd", "qadasd" + "as", "asda", "asd", "urff", "dfkjds", "hfad", "asd", "qadasd", "as", "asda", "asd", "urff", "dfkjds", "hfad", "asd", "qadasd"); 

方法1:

 Set<String> set = new LinkedHashSet<>(); set.addAll(list); for (String s : set) { System.out.println(s + " : " + Collections.frequency(list, s)); } 

方法2:

 int count = 1; Map<String, Integer> map = new HashMap<>(); Set<String> set1 = new LinkedHashSet<>(); for (String s : list) { if (!set1.add(s)) { count = map.get(s) + 1; } map.put(s, count); count = 1; } System.out.println(map); 

所以按照老式的方式来做,然后推出自己的产品:

 Map<String, Integer> instances = new HashMap<String, Integer>(); void add(String name) { Integer value = instances.get(name); if (value == null) { value = new Integer(0); instances.put(name, value); } instances.put(name, value++); } 

如果您是我的ForEach DSL用户 ,则可以使用Count查询完成。

 Count<String> query = Count.from(list); for (Count<Foo> each: query) each.yield = "bat".equals(each.element); int number = query.result(); 

我不想让这种情况更加困难,并使用两个迭代器,我有一个与姓氏 – >名字的HashMap。 而我的方法应该删除具有美名的项目。

 public static void removeTheFirstNameDuplicates(HashMap<String, String> map) { Iterator<Map.Entry<String, String>> iter = map.entrySet().iterator(); Iterator<Map.Entry<String, String>> iter2 = map.entrySet().iterator(); while(iter.hasNext()) { Map.Entry<String, String> pair = iter.next(); String name = pair.getValue(); int i = 0; while(iter2.hasNext()) { Map.Entry<String, String> nextPair = iter2.next(); if (nextPair.getValue().equals(name)) i++; } if (i > 1) iter.remove(); } } 
 List<String> lst = new ArrayList<String>(); lst.add("Ram"); lst.add("Ram"); lst.add("Shiv"); lst.add("Boss"); Map<String, Integer> mp = new HashMap<String, Integer>(); for (String string : lst) { if(mp.keySet().contains(string)) { mp.put(string, mp.get(string)+1); }else { mp.put(string, 1); } } System.out.println("=mp="+mp); 

输出:

 =mp= {Ram=2, Boss=1, Shiv=1} 

使用Java 8function在数组中查找string值的简单方法

 public void checkDuplicateOccurance() { List<String> duplicateList = new ArrayList<String>(); duplicateList.add("Cat"); duplicateList.add("Dog"); duplicateList.add("Cat"); duplicateList.add("cow"); duplicateList.add("Cow"); duplicateList.add("Goat"); Map<String, Long> couterMap = duplicateList.stream().collect(Collectors.groupingBy(e -> e.toString(),Collectors.counting())); System.out.println(couterMap); } 

输出:{Cat = 2,Goat = 1,Cow = 1,cow = 1,Dog = 1}

你可以注意到“牛”和牛不被认为是相同的string,如果你需要它在相同的计数,使用.toLowerCase()。 请在下面find相同的代码段。

 Map<String, Long> couterMap = duplicateList.stream().collect(Collectors.groupingBy(e -> e.toString().toLowerCase(),Collectors.counting())); 

输出:{猫= 2,牛= 2,山羊= 1,狗= 1}