Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a Commons Collections MultiValueMap with a custom value collection type

The 4.0 release of the Apache Commons Collections library has added generics support. I am having trouble converting my code to take advantage of it:

I would like a MultiValueMap which takes a String as a key, and a collection of Strings as the value. But:

  1. The keys should retain insertion ordering (so I create the multi-valued map by decorating a LinkedHashMap)
  2. The values should be unique for each key and retain insertion ordering (so I want the values Collection type to be a LinkedHashSet).

The closest I can get is:

MultiValueMap<String, String> orderedMap = MultiValueMap.multiValueMap(
    new LinkedHashMap<String, Collection<String>>(), 
    LinkedHashSet.class
);

But that produces the error:

The method multiValueMap(Map<K,? super C>, Class<C>) in the type MultiValueMap is not applicable for the arguments (LinkedHashMap<String,Collection<String>>, Class<LinkedHashSet>)

So now I am in generics hell. Any suggestions would be most welcome.

Prior to version 4.0, I accomplished that with the following:

MultiValueMap orderedMap = MultiValueMap.decorate(
    new LinkedHashMap<>(), 
    LinkedHashSet.class
);

Simple! I provide the LinkedHashMap to decorate with MultiValueMap behaviour, and specify the type of collection (LinkedHashSet) to use as the values. But that requires casting when I call put() and get() and so I'd like to be able to use the new generic version provided by 4.0.

like image 964
Ryan Bennetts Avatar asked Mar 25 '14 04:03

Ryan Bennetts


People also ask

What is the use of MultiValueMap in Java?

A MultiValueMap decorates another map, allowing it to have more than one value for a key. A MultiMap is a Map with slightly different semantics. Putting a value into the map will add the value to a Collection at that key. Getting a value will return a Collection, holding all the values put to that key.

What is the difference between MAP and MultiValueMap?

A map cannot contain duplicate keys; each key can map to at most one value. So in a MultivaluedMap you can insert 0, 1, 2, 3 or more objects related to the same key. In a Map you can insert exactly 1 object related to a key.

What is the use of Commons collections jar?

Commons Collections was introduced as a series of utilities that augment the Java Collections API. Commons Collections contains functors such as Predicate and Closure , utilities for filtering and selecting elements in a collection, and some new collections: Bag and Buffer .

What is Apache Commons Collections library?

The Apache Commons Collections are the components of the Apache Commons which are derived from Java API and provides component architecture for the Java language. Commons-Collections seek to build upon the JDK classes by providing new interfaces, implementations and utilities.


2 Answers

I consulted the Apache Commons Collections mailing list, where it was explained to me that the interface for MultiValueMap is known to be lacking, but will be revamped in version 4.1 (see here for the JIRA issue and associated discussion).

So in the future we may have a better solution, but in the meantime, as Rohit Jain mentioned in his answer, we're just going to have to suppress some warnings. However, since the key aspect of type safety is for the MultiValueMap (not the custom collection type), the simplest way to achieve this is:

@SuppressWarnings({ "rawtypes", "unchecked" })
MultiValueMap<String, String> orderedMap = 
    MapUtils.multiValueMap(new LinkedHashMap(), LinkedHashSet.class);

Note the use of the MapUtils factory method, rather than the more direct MultiValueMap which I had used in my original question.

like image 73
Ryan Bennetts Avatar answered Nov 13 '22 14:11

Ryan Bennetts


The problem is you need to pass a Class<LinkedHashSet<String>> as second argument. That you can't create directly. You need to do some cast.

This will work fine:

@SuppressWarnings("unchecked")
MultiValueMap<String, String> orderedMap = 
           MultiValueMap.multiValueMap(
                new LinkedHashMap<String, Collection<String>>(), 
                (Class<LinkedHashSet<String>>)(Class<?>)LinkedHashSet.class
           );
like image 40
Rohit Jain Avatar answered Nov 13 '22 16:11

Rohit Jain