2

I found a way to transform List<Object> to a Map<Integer, Object> but I need

List<Object> to Map<Integer, List<Object>

For example I have class

class Movie {
   public int rank;
   public String desc;

   public Movie(int rank, String desc) {
        this.rank = rank;
        this.desc = desc;
   }
}

And list:

    List<Movie> movies = new ArrayList<Movie>();
    movies.add(new Movie(1, "The Movie 0"));
    movies.add(new Movie(2, "The Movie 1"));
    movies.add(new Movie(2, "The Movie 2"));

What I would like to have is a Map of List for each rank..

List<Movie> -> Map<Integer (rank), List<Movie>>

with Guava I could do simple transformation like that

Map<Integer,Movie> mappedMovies = Maps.uniqueIndex(movies, new Function <Movie,Integer> () {
    public Integer apply(Movie from) {
        return from.getRank(); 
}});

But not to Map<Integer, List<Movie>>

I could find only on post regarding it

Java: how to transform from List<T> to Map<f1(T), List(f2(T))> without iterating

However one approach is using Java 8 and second approach for some reason returns ListMultimap<Integer, String>

Community
  • 1
  • 1
antohoho
  • 950
  • 2
  • 17
  • 37
  • 1
    `ListMultimap` is essentially a `Map>` that manages the creation of lists for you. See http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/ListMultimap.html#asMap() for information about how to transform it to a normal Map – Kiskae Feb 23 '16 at 04:15

2 Answers2

5

The second approach you mentioned is what you want. As Kiskae commented, a "ListMultimap<K,V> is essentially a Map<K,List<V>> that manages the creation of lists for you."

As such, you can use Multimaps.index(Iterable, Function) and Multimaps.asMap(ListMultimap) to get a Map<F<T>, List<T>>:

ImmutableListMultimap<Integer, Movie> index = Multimaps.index(movies, new Function<Movie, Integer>() {
    @Override
    public Integer apply(Movie from) {
        return from.getRank();
    }
});
Map<Integer, List<Movie>> map = Multimaps.asMap(index);

Although you can use Multimaps.asMap(ListMultimap) it may be easier to use the Multimap directly. See the Multimap section of NewCollectionTypesExplained · google/guava Wiki for more details.

Community
  • 1
  • 1
mfulton26
  • 29,956
  • 6
  • 64
  • 88
  • Yep, thanks! Appears to work nicely, step I missed was "ListMultimap is essentially a Map>". What about performance, what would be more efficient, this way or just traditional iterator (like Stefan Haustein mentioned in first answer, iterate over the list and add values to a map http://stackoverflow.com/questions/35568044/transform-listt-to-map-with-list-of-elements-mapft-listt-java-7/35579317#35579317 ) ? – antohoho Feb 23 '16 at 16:14
  • @antohoho It depends on how your going to use it but I would say that the object-oriented benefits of using a `Multimap` far outweighs the negligible performance impacts (which may favor one solution for high mutability and favor a different solution for read-only usage, etc.). – mfulton26 Feb 23 '16 at 16:42
  • I wouldn't do this on Android because of the code size implications, in particular high static costs of the two anonymous classes). I also don't see how code that's longer than the corresponding traditional loop is supposed to be "cleaner" or more readable, but I guess that's a matter of taste in the end. – Stefan Haustein Feb 23 '16 at 17:24
  • 1
    Note: the `Maps.transformValues` step is unnecessary. `ListMultimap.asMap()` is guaranteed to return a `Map>`, it just can't actually use that return type. So you can either just cast the result or (better) use `Multimaps.asMap(ListMultimap)` which returns `Map>` so you don't have to do any casting. – ColinD Feb 23 '16 at 17:59
  • 1
    Also note: You don't _have_ to use `Multimaps.index`. If you feel it's preferable you can just create a `ListMultimap` and then just iterate through your list calling `multimap.put(movie.rank, movie)` to get the same effect. – ColinD Feb 23 '16 at 18:08
  • Thanks @ColinD. I wasn't aware of `Multimaps.asMap(ListMultimap)`. I've updated the code sample to use it instead. – mfulton26 Feb 23 '16 at 21:15
0

Simplest way seeems to have your own "add" utility:

static void add(Map<Integer,List<Movie>> map, Movie movie) {
  List<Movie> list = map.get(movie.rank);
  if (list == null) {
    list = new ArrayList<Movie>();
    map.put(movie.rank, list);
  }
  list.add(movie);
}
Stefan Haustein
  • 18,427
  • 3
  • 36
  • 51