Hot questions for Grouping the Data in Guava

Top 10 Java Open Source / Guava / Grouping the Data

Grouping elements of a list into sublists (maybe by using guava)

Question: I want to group elements of a list. I'm currently doing it this way:

public static <E> List<List<E>> group(final List<E> list, final GroupFunction<E> groupFunction) {

    List<List<E>> result = Lists.newArrayList();

    for (final E element : list) {

        boolean groupFound = false;
        for (final List<E> group : result) {
            if (groupFunction.sameGroup(element, group.get(0))) {
                group.add(element);
                groupFound = true;
                break;
            }
        }
        if (! groupFound) {

            List<E> newGroup = Lists.newArrayList();
            newGroup.add(element);
            result.add(newGroup);
        }
    }

    return result;
}

public interface GroupFunction<E> {
    public boolean sameGroup(final E element1, final E element2);
}

Is there a better way to do this, preferably by using guava?

Answer: Sure it is possible, and even easier with Guava. Use Multimaps.index(Iterable, Function):

ImmutableListMultimap<E, E> indexed = Multimaps.index(list, groupFunction);

If you give concrete use case it would be easier to show it in action.

Example from docs:

List<String> badGuys =
   Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
Function<String, Integer> stringLengthFunction = ...;
Multimap<Integer, String> index =
   Multimaps.index(badGuys, stringLengthFunction);
System.out.println(index);

prints

{4=[Inky], 6=[Blinky], 5=[Pinky, Pinky, Clyde]}

In your case if GroupFunction is defined as:

GroupFunction<String> groupFunction = new GroupFunction<String>() {
  @Override public String sameGroup(final String s1, final String s2) {
    return s1.length().equals(s2.length());
  }
}

then it would translate to:

Function<String, Integer> stringLengthFunction = new Function<String, Integer>() {
  @Override public Integer apply(final String s) {
    return s.length();
  }
}

which is possible stringLengthFunction implementation used in Guava's example.

Finally, in Java 8, whole snippet could be even simpler, as lambas and method references are concise enough to be inlined:

ImmutableListMultimap<E, E> indexed = Multimaps.index(list, String::length);

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

Question: 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;
}

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