Search code examples
javafunctional-programmingguavaapache-commonsfunctor

Can you use a functor/functional programming to group a list in Java 7 (and count its elements per group)?


Can you group List<TypeEnum> types = new ArrayList(Arrays.asList(TypeEnum.A, TypeEnum.B, TypeEnum.A)); into a Map<TypeEnum, Integer> countPerType;, using functors (e.g. Google's Guava, Apache's Commons Functor) pre Java 8?

I'm trying to get my head around functional programming, but am unsure if that sort of thing would actually be possible (as I'm not just mapping a collections value, but trying to aggregate)?

In imperative style, I would have done something like this:

public Map<TypeEnum, Integer> countByGroup(List<TypeEnum> types) {
    Map<TypeEnum, Integer> countedTypes = new HashMap<>();
    for(TypeEnum type : types) {
        if(countedTypes.containsKey(type)) {
            countedTypes.put(type, countedTypes.get(type) + 1);
        } else {
            countedTypes.put(type, 1);
        }
    }
    return countedTypes;
}


EDIT: Relying on side effects seems somewhat inappropriate - or is that how it's done...?

Procedure<TypeEnum> count = new Procedure<TypeEnum>() {
public Map<TypeEnum, Integer> countPerType = null;

    @Override
    public void run(TypeEnum type) {
        if(countPerType.containsKey(type)) {
            countPerType.put(type, countPerType.get(type) + 1);
        } else {
            countPerType.put(type, 1);
        }
    }

    public Procedure<TypeEnum> init(Map<TypeEnum, Integer> countPerType) {
        this.countPerType = countPerType;
        return this;
    }
}.init(countPerType); // kudos http://stackoverflow.com/a/12206542/2018047

Solution

  • Olivier Grégoire commented the correct answer:

    Using Guava, you want a simple Multiset, and more specifically its EnumMultiset implementation. Multiset is a data structure made to keep track of counted elements.

    Given your List<TypeEnum> types you can create an EnumMultiset using create:

    Multiset<TypeEnum> multiset = EnumMultiset.create(types);
    

    And you can query for the count of an element in the Multiset using count:

    multiset.count(TypeEnum.A); // 2
    multiset.count(TypeEnum.B); // 1