From d5e637a33c87c8945f046ab2e4a25cff3c3d4a1d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 26 Oct 2020 11:16:33 +0100 Subject: [PATCH] Restore independent LinkedMultiValueMap implementation (without base class) Closes gh-25960 (cherry picked from commit 82835b99ec9c9425564480334e2078abca7c7509) --- .../util/LinkedMultiValueMap.java | 147 +++++++++++++++++- .../util/MultiValueMapAdapter.java | 5 + 2 files changed, 148 insertions(+), 4 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/LinkedMultiValueMap.java b/spring-core/src/main/java/org/springframework/util/LinkedMultiValueMap.java index 00ef6b4d6bd6..327e3f336b03 100644 --- a/spring-core/src/main/java/org/springframework/util/LinkedMultiValueMap.java +++ b/spring-core/src/main/java/org/springframework/util/LinkedMultiValueMap.java @@ -17,10 +17,14 @@ package org.springframework.util; import java.io.Serializable; +import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; + +import org.springframework.lang.Nullable; /** * Simple implementation of {@link MultiValueMap} that wraps a {@link LinkedHashMap}, @@ -35,16 +39,18 @@ * @param the key type * @param the value element type */ -public class LinkedMultiValueMap extends MultiValueMapAdapter implements Serializable, Cloneable { +public class LinkedMultiValueMap implements MultiValueMap, Serializable, Cloneable { private static final long serialVersionUID = 3801124242820219131L; + private final Map> targetMap; + /** * Create a new LinkedMultiValueMap that wraps a {@link LinkedHashMap}. */ public LinkedMultiValueMap() { - super(new LinkedHashMap<>()); + this.targetMap = new LinkedHashMap<>(); } /** @@ -53,7 +59,7 @@ public LinkedMultiValueMap() { * @param initialCapacity the initial capacity */ public LinkedMultiValueMap(int initialCapacity) { - super(new LinkedHashMap<>(initialCapacity)); + this.targetMap = new LinkedHashMap<>(initialCapacity); } /** @@ -65,7 +71,140 @@ public LinkedMultiValueMap(int initialCapacity) { * @see #deepCopy() */ public LinkedMultiValueMap(Map> otherMap) { - super(new LinkedHashMap<>(otherMap)); + this.targetMap = new LinkedHashMap<>(otherMap); + } + + + // MultiValueMap implementation + + @Override + @Nullable + public V getFirst(K key) { + List values = this.targetMap.get(key); + return (values != null && !values.isEmpty() ? values.get(0) : null); + } + + @Override + public void add(K key, @Nullable V value) { + List values = this.targetMap.computeIfAbsent(key, k -> new LinkedList<>()); + values.add(value); + } + + @Override + public void addAll(K key, List values) { + List currentValues = this.targetMap.computeIfAbsent(key, k -> new LinkedList<>()); + currentValues.addAll(values); + } + + @Override + public void addAll(MultiValueMap values) { + for (Entry> entry : values.entrySet()) { + addAll(entry.getKey(), entry.getValue()); + } + } + + @Override + public void set(K key, @Nullable V value) { + List values = new LinkedList<>(); + values.add(value); + this.targetMap.put(key, values); + } + + @Override + public void setAll(Map values) { + values.forEach(this::set); + } + + @Override + public Map toSingleValueMap() { + Map singleValueMap = new LinkedHashMap<>(this.targetMap.size()); + this.targetMap.forEach((key, values) -> { + if (values != null && !values.isEmpty()) { + singleValueMap.put(key, values.get(0)); + } + }); + return singleValueMap; + } + + + // Map implementation + + @Override + public int size() { + return this.targetMap.size(); + } + + @Override + public boolean isEmpty() { + return this.targetMap.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return this.targetMap.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return this.targetMap.containsValue(value); + } + + @Override + @Nullable + public List get(Object key) { + return this.targetMap.get(key); + } + + @Override + @Nullable + public List put(K key, List value) { + return this.targetMap.put(key, value); + } + + @Override + @Nullable + public List remove(Object key) { + return this.targetMap.remove(key); + } + + @Override + public void putAll(Map> map) { + this.targetMap.putAll(map); + } + + @Override + public void clear() { + this.targetMap.clear(); + } + + @Override + public Set keySet() { + return this.targetMap.keySet(); + } + + @Override + public Collection> values() { + return this.targetMap.values(); + } + + @Override + public Set>> entrySet() { + return this.targetMap.entrySet(); + } + + @Override + public boolean equals(@Nullable Object other) { + return (this == other || this.targetMap.equals(other)); + } + + @Override + public int hashCode() { + return this.targetMap.hashCode(); + } + + @Override + public String toString() { + return this.targetMap.toString(); } diff --git a/spring-core/src/main/java/org/springframework/util/MultiValueMapAdapter.java b/spring-core/src/main/java/org/springframework/util/MultiValueMapAdapter.java index cfbf79cb3b7b..9870cf55b704 100644 --- a/spring-core/src/main/java/org/springframework/util/MultiValueMapAdapter.java +++ b/spring-core/src/main/java/org/springframework/util/MultiValueMapAdapter.java @@ -48,6 +48,8 @@ class MultiValueMapAdapter implements MultiValueMap, Serializable { } + // MultiValueMap implementation + @Override @Nullable public V getFirst(K key) { @@ -97,6 +99,9 @@ public Map toSingleValueMap() { return singleValueMap; } + + // Map implementation + @Override public int size() { return this.targetMap.size();