18

I have an ArrayList of words with duplicate entries.

I want to count and save occurrences for each word in a data structure.

How can I do it?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Francesco Nigro
  • 479
  • 2
  • 8
  • 20
  • 1
    Sort it, iterate over it. Or create a HashMap iterate over the arraylist and increase the count by one each time you see the string. – Voo Mar 06 '11 at 15:00

4 Answers4

59

If you don't have a huge list of strings the shortest way to implement it is by using Collections.frequency method, like this:

List<String> list = new ArrayList<String>();
list.add("aaa");
list.add("bbb");
list.add("aaa");

Set<String> unique = new HashSet<String>(list);
for (String key : unique) {
    System.out.println(key + ": " + Collections.frequency(list, key));
}

Output:

aaa: 2
bbb: 1
lukastymo
  • 26,145
  • 14
  • 53
  • 66
13

There are lots of possibilities. A fast to implement solution could be to use a Map<String, Integer> where the String is each individual word and Integer the count of each.

Traverse the list and increase the corresponding value in the map for it. In case there is no entry yet, add one with the value 1.

wordList = ....;

Map<String, Integer> wordCount = new HashMap<String, Integer>();

for(String word: wordList) {
  Integer count = wordCount.get(word);          
  wordCount.put(word, (count==null) ? 1 : count+1);
}
Kosi2801
  • 22,222
  • 13
  • 38
  • 45
  • `Integer` is immutable, you **nned** to put it back in : wordCount.put(word, wordCount.get(word)++) - ok, I just saw you fixed it already :) – Yanick Rochon Mar 06 '11 at 15:17
  • Already fixed it, but thanks for the hint ;) – Kosi2801 Mar 06 '11 at 15:18
  • I like a two-pass approach better - in the first pass, simply put zero into the map; in the second, add one to the value. This avoids the sometimes confusing conditional logic, at (possibly) some slight performance cost. – Carl Manaster Mar 06 '11 at 15:19
  • IMO the "?" operator is something people should be aware of because it's widely used. But you're right, if it would have gotten more complicated than that, better use either a two-pass solution or use a proper if/else, depends on the requirements. – Kosi2801 Mar 06 '11 at 15:21
  • 1
    if Java had a null coalescing operator (??) this look even better – Yanick Rochon Mar 06 '11 at 15:40
1

Here's a test-driven class that will do what you want. First the test:

import junit.framework.TestCase;

public class CounterTest extends TestCase {
    private Counter<String> counter;

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        counter = new Counter<String>();
    }

    public void testInitialCountIsZero() throws Exception {
        assertEquals(0, counter.get("a"));
    }

    public void testCount() throws Exception {
        counter.count("a");
        assertEquals(1, counter.get("a"));
    }
}

Now the class:

import java.util.HashMap;

public class Counter<T> {
    private final HashMap<T, Integer> map = new HashMap<T, Integer>();

    public int get(T key) {
        final Integer n = map.get(key);
        return n == null ? 0 : n;
    }

    public void count(T key) {
        map.put(key, get(key) + 1);
    }
}

To solve your specific problem, you would create a counter, and iterate over your list, counting each element.

Counter<String> counter = new Counter<String>();
for (String string: myList)
    counter.count(string);
Carl Manaster
  • 39,912
  • 17
  • 102
  • 155
0

Or if you are too lazy to do it yourself (or a good industrial programmer :p), use Multiset from google guava.

Enno Shioji
  • 26,542
  • 13
  • 70
  • 109