2

I am printing items from an ArrayList with this code:

for(int i2 = 0; i2 < a.size(); i2++)
    {
        word2 = a.get(i2);
        for(int j2 = 0; j2 < a.size(); j2++)
        {
            if(word2.equals(a.get(j2)))
            {
                counter++;
            }
        }
        if(counter!=0)
        {
            System.out.println(word2 + " : " + counter);
        }
        counter = 0;
    } 

When I print I don't want to print out the duplicates. As it is now, it will print

Alphabet : 3
Alright : 3
Apple : 3
Alphabet : 3
Alright : 3
Apple : 3
Alphabet : 3
Alright : 3
Apple : 3

I only want it to print

Alphabet : 3
Alright : 3
Apple : 3

How do I make it not print the duplicates? I have to use ArrayList for the assignment

5
  • 3
    use a Set instead of a List Commented Mar 25, 2014 at 1:56
  • @JarrodRoberson A set would remove duplicates from the list, thus losing information about word counts. Commented Mar 25, 2014 at 2:05
  • You could take some help from here: stackoverflow.com/questions/22412035/… Commented Mar 25, 2014 at 2:06
  • The the appropriate collection is a Multimap Commented Mar 25, 2014 at 2:12
  • @JarrodRoberson A Multiset would be more appropriate (the Multimap would require some arbitrary value objects), but same idea. The words could be inserted into, e.g., Guava's Multiset, then count() will return the word count given a word. You should post an example as an answer, it would be another nice clean approach. Commented Mar 25, 2014 at 2:29

3 Answers 3

2

Another option, while performance is not the greatest (although it will be sufficient for your application, and has similar performance characteristics to your current code), is to create a temporary Set to hold a list of unique words, then use Collections.frequency() to count the occurrences in the original list, e.g. with your ArrayList<String> a:

Set<String> unique = new HashSet<String>(a);

for (String word : unique)
    System.out.println(word + " : " + Collections.frequency(a, word));

Or even just:

for (String word : new HashSet<String>(a))
    System.out.println(word + " : " + Collections.frequency(a, word));

The benefit here is short and clear code.

You can use a TreeSet if you want to print the words in alphabetical order, or a LinkedHashSet if you want to print them in the order of first occurrence.

As an aside, the above does not store the counts for later use, which your original code does not do either. However, if you wanted to do this, it's trivial to store the results in a map:

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

for (String word : new HashSet<String>(a))
    wordCounts.put(word, Collections.frequency(a, word));

// wordCounts now contains a map of strings -> counts.    
Sign up to request clarification or add additional context in comments.

6 Comments

Nice! I wasn't aware of this Collections.frequency method. How does it work? Are frequencies stored somewhere when objects are added? What is the time complexity of this method?
@BrianVanover I've added a documentation link. frequency() isn't optimized in any way, each call is O(n) on the size of the container; it simply iterates through the container and counts elements that equal the specified object as per equals(). It is similar to the OP's original implementation. This technique may not be suitable for huge word sets, but for smaller sets in simple applications, performance is generally acceptable.
The entire thing is O(mn), where m = number of unique words and n = total number of words; worst case O(n^2) when no words are repeated, best case O(n) when all words are the same.
Thanks. I'm confused about iterating through the container. Maybe I need more clarification on the underlying process for Sets. Won't duplicates not be added to the Set, so there would be no reference to the duplicates to count?
@BrianVanover Note that Collections.frequency(a, word) counts the frequency in the original ArrayList a, rather than in the set.
|
1

Use a TreeMap<String, Integer> to track word counts

SortedMap<String, Integer> wordFrequencyMap = new TreeMap<String, Integer>();

for (String str : a) {
  if (wordFrequencyMap.containsKey(str)) {
    int strFreq = Integer.intValue(wordFrequencyMap.get(str));
    strFreq++;
    wordFrequencyMap.put(str, new Integer(strFreq));
  }
  else {
    wordFrequencyMap.put(str, new Integer(1));
  }
}

for (String word : wordFrequencyMap.keySet()) {
  System.out.println(word + " : " + wordFrequencyMap.get(word));
}

This data structure will not allow duplicates and it will count the occurrence of each word with only having to traverse the list one time. Since you are using a TreeMap with String keys, it will print the keys in alphabetical order when iterating

1 Comment

+1 Adding to this: In more performance limited environments, one common optimization with this is to define a custom mutable counter class that wraps a primitive int, so that the count can be updated without creating a new Integer. There is also MutableInt in Apache Commons.
0

Another Java-8 stream alternative:

This creates a map at collect step: the key is the word (because Function.identity() returns every word) and the value is the frequency (because Collectors.counting() returns every word frequency). And the forEach step just prints every entry "<word>: <word-frequency>"

a.stream().collect(Collectors.groupingBy(
               Function.identity(),
               Collectors.counting()))
          .forEach((word, frequency) -> System.out.println(word+": "+frequency));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.