If You create MultiValueMap via MultiValueMapAdapter, and value list is empty, call of method getFirst fail on IndexOutOfBounds exception:

private static class MultiValueMapAdapter<K, V> implements MultiValueMap<K, V>, Serializable {

        private final Map<K, List<V>> map;

        public MultiValueMapAdapter(Map<K, List<V>> map) {
            Assert.notNull(map, "'map' must not be null");
            this.map = map;
        }

        @Override
        @Nullable
        public V getFirst(K key) {
            List<V> values = this.map.get(key);
            return (values != null ? values.get(0) : null);
        }

Good solution is:

return (values != null && !values.isEmpty() ? values.get(0) : null);

Comment From: jhoeller

The common scenario is that such a list either contains one or more values or isn't set at all (in case of no value associated with a key). That said, point taken, we should align this with the existing LinkedMultiValueMap implementation where such a defensive emptiness check is present already.

Comment From: vlavasa

The common scenario is that such a list either contains one or more values or isn't set at all (in case of no value associated with a key). That said, point taken, we should align this with the existing LinkedMultiValueMap implementation where such a defensive emptiness check is present already.

OK, but the adapter is used in method: org.springframework.util.CollectionUtils#toMultiValueMap

and there is accepted any Map> map

Comment From: jhoeller

Good point, it can be constructed with any target map content, including empty list values there. Also, an addAll call with an empty list argument will also lead to such empty list state, just like in LinkedMultiValueMap. I was just pointing out why it is uncommon to encounter empty lists there, otherwise it would be surprising for this bug to remain unnoticed so many years.

Thanks for raising this, in any case! I've marked it for a fix in the upcoming 5.2.7 and 5.1.16 releases.

Comment From: midumitrescu

@jhoeller could I give it a go?

I have also prepared unit tests.

Comment From: jhoeller

@midumitrescu I got a local commit for 5.2.x staged along with a few others, using the same code that we have in LinkedMultiValueMap (with an && !values.isEmpty() check); I'll forward-merge this to master ASAP. So thanks for volunteering but I'm afraid it's effectively done already :-)

If you got unit tests for MultiValueMapAdapter, I'd appreciate if you could subsequently submit them as a separate PR. We got quite a few tests for LinkedMultiValueMap but no isolated ones for CollectionUtils.toMultiValueMap, as far as I can see, so there's definitely a gap to close there.

Comment From: jhoeller

@midumitrescu I see that your commit uses CollectionUtils.firstElement, another interesting solution. I nevertheless prefer alignment with the LinkedMultiValueMap implementation without any further method delegation involved, as in my locally staged commit.

To be clear about the unit tests, we don't have any at the moment. We'd benefit from complete unit tests for CollectionUtils.toMultiValueMap, to the extent that LinkedMultiValueMapTests covers the typical interaction patterns already. A contribution would be very welcome there!

Comment From: midumitrescu

@jhoeller Sorry, I did not see your message in time. I'll close my PR.

To not forget,

toSingleValueMap

also calls

list.get(0)

Producing basically the same ArrayIndexOutOfBoundsException.

By not wanting to duplicate the behavior I came to

Collections.firstElement()

Just let me know when to add the unit tests. The thing is, I created only those necessary for fixing this bug.

However, I can gladly also complete them with some meaningful ones.

Comment From: jhoeller

That's a good catch indeed, my 5.2.x commit has this aligned with the LinkedMultiValueMap implementation as well where we are performing the same !values.isEmpty() check in toSingleValueMap. It's a matter of implementation style and therefore always debatable; we tend to keep our low-level utilities as decoupled from our own convenience methods as possible. From that perspective, LinkedMultiValueMap should not depend on CollectionUtils (which is meant to be working on top of collection implementations), and the same goes for the internal MultiValueMapAdapter... even if the latter is nested within CollectionUtils itself.

As for the unit tests, LinkedMultiValueMapTests is a good starting point. MultiValueMapAdapter should receive the same coverage there. So if you got some cycles left for volunteering, a PR for that part would be much appreciated :-) Otherwise we can also cover the unit tests at a later point.

Comment From: midumitrescu

@jhoeller I think I needed a bit of time to understand what you mean by aligning implementations.

Are you meaning

    public static <K, V> MultiValueMap<K, V> toMultiValueMap(Map<K, List<V>> map) {
        return new LinkedMultiValueMap<>(map);
    }

Or is the MultiValueMapAdapter really needed?

Comment From: jhoeller

At this point it's just about coding the same methods the same way in both implementations. You got a point though, except for LinkedMultiValueMap always using an independent internal LinkedHashMap (and not a given external Map as-is), the implementations are almost the same. I'll see what I can do to unify them, maybe extracting a common base class... although we'd only do that in our recent branches, not in the backports.

Comment From: midumitrescu

Understood. I would be interested in giving a hand there, if needed.

If not, I will attend the unit tests now.

Comment From: jhoeller

I ended up moving the existing MultiValueMapAdapter class to top level (but package-visible), extending it in LinkedMultiValueMap and therefore reusing most of the implementation (and putting it into a single place for maintenance purposes).