Skip to content

Commit 7f87eb5

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 b291639 commit 7f87eb5

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;
@@ -379,25 +377,28 @@ public static <E> Iterator<E> toIterator(@Nullable Enumeration<E> enumeration) {
379377

380378
/**
381379
* Adapt a {@code Map<K, List<V>>} to an {@code MultiValueMap<K, V>}.
382-
* @param map the original map
383-
* @return the multi-value map
380+
* @param targetMap the original map
381+
* @return the adapted multi-value map (wrapping the original map)
384382
* @since 3.1
385383
*/
386-
public static <K, V> MultiValueMap<K, V> toMultiValueMap(Map<K, List<V>> map) {
387-
return new MultiValueMapAdapter<>(map);
384+
public static <K, V> MultiValueMap<K, V> toMultiValueMap(Map<K, List<V>> targetMap) {
385+
Assert.notNull(targetMap, "'targetMap' must not be null");
386+
return new MultiValueMapAdapter<>(targetMap);
388387
}
389388

390389
/**
391390
* Return an unmodifiable view of the specified multi-value map.
392-
* @param map the map for which an unmodifiable view is to be returned.
393-
* @return an unmodifiable view of the specified multi-value map.
391+
* @param targetMap the map for which an unmodifiable view is to be returned.
392+
* @return an unmodifiable view of the specified multi-value map
394393
* @since 3.1
395394
*/
396395
@SuppressWarnings("unchecked")
397-
public static <K, V> MultiValueMap<K, V> unmodifiableMultiValueMap(MultiValueMap<? extends K, ? extends V> map) {
398-
Assert.notNull(map, "'map' must not be null");
399-
Map<K, List<V>> result = new LinkedHashMap<>(map.size());
400-
map.forEach((key, value) -> {
396+
public static <K, V> MultiValueMap<K, V> unmodifiableMultiValueMap(
397+
MultiValueMap<? extends K, ? extends V> targetMap) {
398+
399+
Assert.notNull(targetMap, "'targetMap' must not be null");
400+
Map<K, List<V>> result = new LinkedHashMap<>(targetMap.size());
401+
targetMap.forEach((key, value) -> {
401402
List<? extends V> values = Collections.unmodifiableList(value);
402403
result.put(key, (List<V>) values);
403404
});
@@ -434,141 +435,4 @@ public void remove() throws UnsupportedOperationException {
434435
}
435436

436437

437-
/**
438-
* Adapts a Map to the MultiValueMap contract.
439-
*/
440-
@SuppressWarnings("serial")
441-
private static class MultiValueMapAdapter<K, V> implements MultiValueMap<K, V>, Serializable {
442-
443-
private final Map<K, List<V>> map;
444-
445-
public MultiValueMapAdapter(Map<K, List<V>> map) {
446-
Assert.notNull(map, "'map' must not be null");
447-
this.map = map;
448-
}
449-
450-
@Override
451-
@Nullable
452-
public V getFirst(K key) {
453-
List<V> values = this.map.get(key);
454-
return (values != null ? values.get(0) : null);
455-
}
456-
457-
@Override
458-
public void add(K key, @Nullable V value) {
459-
List<V> values = this.map.computeIfAbsent(key, k -> new LinkedList<>());
460-
values.add(value);
461-
}
462-
463-
@Override
464-
public void addAll(K key, List<? extends V> values) {
465-
List<V> currentValues = this.map.computeIfAbsent(key, k -> new LinkedList<>());
466-
currentValues.addAll(values);
467-
}
468-
469-
@Override
470-
public void addAll(MultiValueMap<K, V> values) {
471-
for (Entry<K, List<V>> entry : values.entrySet()) {
472-
addAll(entry.getKey(), entry.getValue());
473-
}
474-
}
475-
476-
@Override
477-
public void set(K key, @Nullable V value) {
478-
List<V> values = new LinkedList<>();
479-
values.add(value);
480-
this.map.put(key, values);
481-
}
482-
483-
@Override
484-
public void setAll(Map<K, V> values) {
485-
values.forEach(this::set);
486-
}
487-
488-
@Override
489-
public Map<K, V> toSingleValueMap() {
490-
LinkedHashMap<K, V> singleValueMap = new LinkedHashMap<>(this.map.size());
491-
this.map.forEach((key, value) -> singleValueMap.put(key, value.get(0)));
492-
return singleValueMap;
493-
}
494-
495-
@Override
496-
public int size() {
497-
return this.map.size();
498-
}
499-
500-
@Override
501-
public boolean isEmpty() {
502-
return this.map.isEmpty();
503-
}
504-
505-
@Override
506-
public boolean containsKey(Object key) {
507-
return this.map.containsKey(key);
508-
}
509-
510-
@Override
511-
public boolean containsValue(Object value) {
512-
return this.map.containsValue(value);
513-
}
514-
515-
@Override
516-
public List<V> get(Object key) {
517-
return this.map.get(key);
518-
}
519-
520-
@Override
521-
public List<V> put(K key, List<V> value) {
522-
return this.map.put(key, value);
523-
}
524-
525-
@Override
526-
public List<V> remove(Object key) {
527-
return this.map.remove(key);
528-
}
529-
530-
@Override
531-
public void putAll(Map<? extends K, ? extends List<V>> map) {
532-
this.map.putAll(map);
533-
}
534-
535-
@Override
536-
public void clear() {
537-
this.map.clear();
538-
}
539-
540-
@Override
541-
public Set<K> keySet() {
542-
return this.map.keySet();
543-
}
544-
545-
@Override
546-
public Collection<List<V>> values() {
547-
return this.map.values();
548-
}
549-
550-
@Override
551-
public Set<Entry<K, List<V>>> entrySet() {
552-
return this.map.entrySet();
553-
}
554-
555-
@Override
556-
public boolean equals(Object other) {
557-
if (this == other) {
558-
return true;
559-
}
560-
return this.map.equals(other);
561-
}
562-
563-
@Override
564-
public int hashCode() {
565-
return this.map.hashCode();
566-
}
567-
568-
@Override
569-
public String toString() {
570-
return this.map.toString();
571-
}
572-
}
573-
574438
}

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-2018 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.
@@ -220,8 +220,8 @@ public LinkedCaseInsensitiveMap<V> clone() {
220220
}
221221

222222
@Override
223-
public boolean equals(Object obj) {
224-
return this.targetMap.equals(obj);
223+
public boolean equals(@Nullable Object other) {
224+
return (this == other || this.targetMap.equals(other));
225225
}
226226

227227
@Override

0 commit comments

Comments
 (0)