tags:

views:

137

answers:

8

I have a list. The list can contain multiple items of the same enum type.

Lets say i have an enum : TOY which has values: BALL, DOLL, PLAYSTATION. I want to know how many PLAYSTATION items are in a list with the type TOY. (ie, List<Toy> toys)

What is the best possible solution for this? I don't want to keep iterating through the list everytime.

+8  A: 

You can use Apache commons-collections' HashBag. It has a getCount(Object) method which will suit you.

Bozho
+1 - HashBag keeps counts in a map, so it's a quick lookup each time you call getCount. Much better than iterating through a list.
Bill the Lizard
This sounds like the answer I need. A quick look up, and not everytime iterating (like the java collections answer I posted earlier).
Stefan Hendriks
+1  A: 

Why don't you create a decorator for the type of list you're using which stores a list of counts for each enum type have been added/removed internally. That way you could use it as a normal list but also add some extra functionality for querying how many of which type are currently contained.

All you'd need to do would be to override the add/remove/addAll etc methods and increment your counters before passing it on to the real list type. The best part about it would be that you could decorate any list type with your new wrapper.

Benj
A: 

Extend java.util.List method and override all mutator methods, i.e. the ones that are used for add or delete elements and also ones used to clear the list. Add a reference to a private java.util.Map which will hold the number of items per type. Add accessor methods which will return current number of elements per type.

Boris Pavlović
+1  A: 

At the very least, a utility method like:

public int count(List<Toy> haystack, Toy needle) {
    int result;
    for (Toy t : haystack) {
        if (t == needle) {
           result++;
        }
    }
    return result;
}

Would let you concisely refer to the number of PLAYSTATIONs from elsewhere in the code. Alternatively if you knew the list was unlikely to change, building a Map<Toy, Integer> would let you build up the counts for all items once.

MHarris
Yeah that is the brute-force method, but it would require a look for every time i wanted to know the count() of that type of Toy.
Stefan Hendriks
+2  A: 

java.util.Collections has a method called frequency(Collection c, Object type).

Usage in my question:

int amountOfPlayStations = Collections.frequency(toys, TOY.PLAYSTATION);
Stefan Hendriks
Meh. Looks like this implementation basically does a for loop everytime. So this is *not* very friendly CPU-wise.
Stefan Hendriks
this method just iterates over each element of the collection and counts the occurences
Bozho
A: 

The HashBag (by Bozho) seems to be your best bet. But a bit more general would be Googles Collections 2 with an appropriate Predicate:

List<Toy> toys;
List<Toy> playstations = Collections2.filter( toys, new Predicate() {
  boolean apply(TOY toy){
    return toy == TOY.PLAYSTATION;
  }
});
extraneon
A: 

Besides all those solutions (I have a weakness for the Collections.Frequency call), i would recommend you to take a look at google collections, and particularly to [Collections2.transform][2], which could give you a live view on items.

[2]: http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/Collections2.html#transform%28java.util.Collection, com.google.common.base.Function)

Riduidel
+1  A: 

If you don't want to have to iterate over the entire collection each time, another alternative would be to write a ForwardingList implementation. The main benefits of this over the HashBag suggestion are:

  • it supports generics
  • it implements the List interface, so you can pass it to any method that expects a List

There is a downside to this approach however, in that you have to write a bit of plumbing code to get it up and running.

Below is a quick example of how you could do it. Note that if you do this you should override all methods that add/delete from the list, otherwise you may end up in an inconsistent state:

import com.google.common.collect.ForwardingList;


public class CountingList<E> extends ForwardingList<E> {

 private List<E> backingList = new LinkedList<E>();
 private Map<E, Integer> countMap = new HashMap<E, Integer>();

 @Override
 protected List<E> delegate() {
  return backingList;
 }

 @Override
 public boolean add(E element) {
  backingList.add(element);
  if(countMap.containsKey(element)) {
   countMap.put(element, countMap.get(element) + 1);
  } else {
   countMap.put(element, 1);
  }
  return true;
 }

 public int getCount(E element) {
  Integer count = countMap.get(element);
  return count != null ? count.intValue() : 0;
 }

}
Jared Russell
Very nice solution actually. Thanks!
Stefan Hendriks