3

I'm looking at various google-collections (or also Apache commons collections) classes, and I'd fancy some help in finding the most appropriate Map implementation for my use case. I'm not interested in home-grown maps.

Here are some requirements:

  • I have a Map<String, List<String>> data structure (A)
  • Most of the time, I want to expose a Map<String, String> read-only view (B).
  • (B) features:
    • Keys in (B) correspond to keys in (A)
    • Values in (B) correspond to the "last values in (A)", i.e. B.get(X) == A.get(X).get(A.get(X).size() - 1)
    • Values in (A) may not be empty lists

I know ArrayListMultimap, but it doesn't expose a view satisfying my requirements for (B).

Any pointers?

Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509

2 Answers2

12

I suspect you just want Maps.transformValues from Guava:

Map<String, String> lastValueView = Maps.transformValues(originalMap,
    new Function<List<String>, String>() {
        @Override
        public String apply(List<String> input) {
            return input.get(input.size() - 1);
        }
    });
Iulian Popescu
  • 2,595
  • 4
  • 23
  • 31
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
5

Extending on @JonSkeet's answer, I would suggest that you use Guava's ListMultimap as your (A) type. This type makes sure that you always have lists with at least one element as part of the map, and the .asMap() method returns a view that is a Map<String, List<String>> (even if the signature does not say so).

The Maps.transformValues method also returns a view, so changes in the source map are reflected in the target map. Be aware, however, that "The returned map is not thread-safe or serializable, even if the underlying map is.":

ListMultimap<String, String> source = ArrayListMultimap.create();
Map<String, String> target = Maps.transformValues(source.asMap(), new Function<Collection<String>, String>() {

  @Override
  public String apply(Collection<String> input) {
    return Iterables.getLast(input);
  }

});
nd.
  • 8,699
  • 2
  • 32
  • 42
  • Thanks for the info. However, if I know that I have lists in my `Map`, I prefer to access the last item directly, instead of iterating... – Lukas Eder May 02 '13 at 10:41
  • 2
    @lukas-eder Iterables.getLast is smart enough about its parameters to use iterable.get(iterable.size() - 1) for all iterable instanceof List (and iterable.last() for all iterable instanceof SortedSet) – nd. May 02 '13 at 11:15