Skip to content

Commit bec89db

Browse files
committed
Consistent MultiValueMap behavior for empty list values
This commit extracts MultiValueMapAdapter from CollectionUtils and reuses its implementation as base class of LinkedMultiValueMap. Closes gh-25140
1 parent 7682575 commit bec89db

File tree

4 files changed

+202
-299
lines changed

4 files changed

+202
-299
lines changed

spring-core/src/main/java/org/springframework/util/CollectionUtils.java

Lines changed: 14 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,15 +16,13 @@
1616

1717
package org.springframework.util;
1818

19-
import java.io.Serializable;
2019
import java.util.ArrayList;
2120
import java.util.Arrays;
2221
import java.util.Collection;
2322
import java.util.Collections;
2423
import java.util.Enumeration;
2524
import java.util.Iterator;
2625
import java.util.LinkedHashMap;
27-
import java.util.LinkedList;
2826
import java.util.List;
2927
import java.util.Map;
3028
import java.util.Properties;
@@ -412,25 +410,28 @@ public static <E> Iterator<E> toIterator(@Nullable Enumeration<E> enumeration) {
412410

413411
/**
414412
* Adapt a {@code Map<K, List<V>>} to an {@code MultiValueMap<K, V>}.
415-
* @param map the original map
416-
* @return the multi-value map
413+
* @param targetMap the original map
414+
* @return the adapted multi-value map (wrapping the original map)
417415
* @since 3.1
418416
*/
419-
public static <K, V> MultiValueMap<K, V> toMultiValueMap(Map<K, List<V>> map) {
420-
return new MultiValueMapAdapter<>(map);
417+
public static <K, V> MultiValueMap<K, V> toMultiValueMap(Map<K, List<V>> targetMap) {
418+
Assert.notNull(targetMap, "'targetMap' must not be null");
419+
return new MultiValueMapAdapter<>(targetMap);
421420
}
422421

423422
/**
424423
* Return an unmodifiable view of the specified multi-value map.
425-
* @param map the map for which an unmodifiable view is to be returned.
426-
* @return an unmodifiable view of the specified multi-value map.
424+
* @param targetMap the map for which an unmodifiable view is to be returned.
425+
* @return an unmodifiable view of the specified multi-value map
427426
* @since 3.1
428427
*/
429428
@SuppressWarnings("unchecked")
430-
public static <K, V> MultiValueMap<K, V> unmodifiableMultiValueMap(MultiValueMap<? extends K, ? extends V> map) {
431-
Assert.notNull(map, "'map' must not be null");
432-
Map<K, List<V>> result = new LinkedHashMap<>(map.size());
433-
map.forEach((key, value) -> {
429+
public static <K, V> MultiValueMap<K, V> unmodifiableMultiValueMap(
430+
MultiValueMap<? extends K, ? extends V> targetMap) {
431+
432+
Assert.notNull(targetMap, "'targetMap' must not be null");
433+
Map<K, List<V>> result = new LinkedHashMap<>(targetMap.size());
434+
targetMap.forEach((key, value) -> {
434435
List<? extends V> values = Collections.unmodifiableList(value);
435436
result.put(key, (List<V>) values);
436437
});
@@ -467,141 +468,4 @@ public void remove() throws UnsupportedOperationException {
467468
}
468469

469470

470-
/**
471-
* Adapts a Map to the MultiValueMap contract.
472-
*/
473-
@SuppressWarnings("serial")
474-
private static class MultiValueMapAdapter<K, V> implements MultiValueMap<K, V>, Serializable {
475-
476-
private final Map<K, List<V>> map;
477-
478-
public MultiValueMapAdapter(Map<K, List<V>> map) {
479-
Assert.notNull(map, "'map' must not be null");
480-
this.map = map;
481-
}
482-
483-
@Override
484-
@Nullable
485-
public V getFirst(K key) {
486-
List<V> values = this.map.get(key);
487-
return (values != null ? values.get(0) : null);
488-
}
489-
490-
@Override
491-
public void add(K key, @Nullable V value) {
492-
List<V> values = this.map.computeIfAbsent(key, k -> new LinkedList<>());
493-
values.add(value);
494-
}
495-
496-
@Override
497-
public void addAll(K key, List<? extends V> values) {
498-
List<V> currentValues = this.map.computeIfAbsent(key, k -> new LinkedList<>());
499-
currentValues.addAll(values);
500-
}
501-
502-
@Override
503-
public void addAll(MultiValueMap<K, V> values) {
504-
for (Entry<K, List<V>> entry : values.entrySet()) {
505-
addAll(entry.getKey(), entry.getValue());
506-
}
507-
}
508-
509-
@Override
510-
public void set(K key, @Nullable V value) {
511-
List<V> values = new LinkedList<>();
512-
values.add(value);
513-
this.map.put(key, values);
514-
}
515-
516-
@Override
517-
public void setAll(Map<K, V> values) {
518-
values.forEach(this::set);
519-
}
520-
521-
@Override
522-
public Map<K, V> toSingleValueMap() {
523-
LinkedHashMap<K, V> singleValueMap = new LinkedHashMap<>(this.map.size());
524-
this.map.forEach((key, value) -> singleValueMap.put(key, value.get(0)));
525-
return singleValueMap;
526-
}
527-
528-
@Override
529-
public int size() {
530-
return this.map.size();
531-
}
532-
533-
@Override
534-
public boolean isEmpty() {
535-
return this.map.isEmpty();
536-
}
537-
538-
@Override
539-
public boolean containsKey(Object key) {
540-
return this.map.containsKey(key);
541-
}
542-
543-
@Override
544-
public boolean containsValue(Object value) {
545-
return this.map.containsValue(value);
546-
}
547-
548-
@Override
549-
public List<V> get(Object key) {
550-
return this.map.get(key);
551-
}
552-
553-
@Override
554-
public List<V> put(K key, List<V> value) {
555-
return this.map.put(key, value);
556-
}
557-
558-
@Override
559-
public List<V> remove(Object key) {
560-
return this.map.remove(key);
561-
}
562-
563-
@Override
564-
public void putAll(Map<? extends K, ? extends List<V>> map) {
565-
this.map.putAll(map);
566-
}
567-
568-
@Override
569-
public void clear() {
570-
this.map.clear();
571-
}
572-
573-
@Override
574-
public Set<K> keySet() {
575-
return this.map.keySet();
576-
}
577-
578-
@Override
579-
public Collection<List<V>> values() {
580-
return this.map.values();
581-
}
582-
583-
@Override
584-
public Set<Entry<K, List<V>>> entrySet() {
585-
return this.map.entrySet();
586-
}
587-
588-
@Override
589-
public boolean equals(@Nullable Object other) {
590-
if (this == other) {
591-
return true;
592-
}
593-
return this.map.equals(other);
594-
}
595-
596-
@Override
597-
public int hashCode() {
598-
return this.map.hashCode();
599-
}
600-
601-
@Override
602-
public String toString() {
603-
return this.map.toString();
604-
}
605-
}
606-
607471
}

spring-core/src/main/java/org/springframework/util/LinkedCaseInsensitiveMap.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -273,8 +273,8 @@ public LinkedCaseInsensitiveMap<V> clone() {
273273
}
274274

275275
@Override
276-
public boolean equals(@Nullable Object obj) {
277-
return this.targetMap.equals(obj);
276+
public boolean equals(@Nullable Object other) {
277+
return (this == other || this.targetMap.equals(other));
278278
}
279279

280280
@Override

0 commit comments

Comments
 (0)