001/* 002 * Copyright 2002-2020 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.util; 018 019import java.io.Serializable; 020import java.util.Collection; 021import java.util.LinkedHashMap; 022import java.util.LinkedList; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026 027import org.springframework.lang.Nullable; 028 029/** 030 * Simple implementation of {@link MultiValueMap} that wraps a {@link LinkedHashMap}, 031 * storing multiple values in a {@link LinkedList}. 032 * 033 * <p>This Map implementation is generally not thread-safe. It is primarily designed 034 * for data structures exposed from request objects, for use in a single thread only. 035 * 036 * @author Arjen Poutsma 037 * @author Juergen Hoeller 038 * @since 3.0 039 * @param <K> the key type 040 * @param <V> the value element type 041 */ 042public class LinkedMultiValueMap<K, V> implements MultiValueMap<K, V>, Serializable, Cloneable { 043 044 private static final long serialVersionUID = 3801124242820219131L; 045 046 private final Map<K, List<V>> targetMap; 047 048 049 /** 050 * Create a new LinkedMultiValueMap that wraps a {@link LinkedHashMap}. 051 */ 052 public LinkedMultiValueMap() { 053 this.targetMap = new LinkedHashMap<>(); 054 } 055 056 /** 057 * Create a new LinkedMultiValueMap that wraps a {@link LinkedHashMap} 058 * with the given initial capacity. 059 * @param initialCapacity the initial capacity 060 */ 061 public LinkedMultiValueMap(int initialCapacity) { 062 this.targetMap = new LinkedHashMap<>(initialCapacity); 063 } 064 065 /** 066 * Copy constructor: Create a new LinkedMultiValueMap with the same mappings as 067 * the specified Map. Note that this will be a shallow copy; its value-holding 068 * List entries will get reused and therefore cannot get modified independently. 069 * @param otherMap the Map whose mappings are to be placed in this Map 070 * @see #clone() 071 * @see #deepCopy() 072 */ 073 public LinkedMultiValueMap(Map<K, List<V>> otherMap) { 074 this.targetMap = new LinkedHashMap<>(otherMap); 075 } 076 077 078 // MultiValueMap implementation 079 080 @Override 081 @Nullable 082 public V getFirst(K key) { 083 List<V> values = this.targetMap.get(key); 084 return (values != null && !values.isEmpty() ? values.get(0) : null); 085 } 086 087 @Override 088 public void add(K key, @Nullable V value) { 089 List<V> values = this.targetMap.computeIfAbsent(key, k -> new LinkedList<>()); 090 values.add(value); 091 } 092 093 @Override 094 public void addAll(K key, List<? extends V> values) { 095 List<V> currentValues = this.targetMap.computeIfAbsent(key, k -> new LinkedList<>()); 096 currentValues.addAll(values); 097 } 098 099 @Override 100 public void addAll(MultiValueMap<K, V> values) { 101 for (Entry<K, List<V>> entry : values.entrySet()) { 102 addAll(entry.getKey(), entry.getValue()); 103 } 104 } 105 106 @Override 107 public void set(K key, @Nullable V value) { 108 List<V> values = new LinkedList<>(); 109 values.add(value); 110 this.targetMap.put(key, values); 111 } 112 113 @Override 114 public void setAll(Map<K, V> values) { 115 values.forEach(this::set); 116 } 117 118 @Override 119 public Map<K, V> toSingleValueMap() { 120 Map<K, V> singleValueMap = new LinkedHashMap<>(this.targetMap.size()); 121 this.targetMap.forEach((key, values) -> { 122 if (values != null && !values.isEmpty()) { 123 singleValueMap.put(key, values.get(0)); 124 } 125 }); 126 return singleValueMap; 127 } 128 129 130 // Map implementation 131 132 @Override 133 public int size() { 134 return this.targetMap.size(); 135 } 136 137 @Override 138 public boolean isEmpty() { 139 return this.targetMap.isEmpty(); 140 } 141 142 @Override 143 public boolean containsKey(Object key) { 144 return this.targetMap.containsKey(key); 145 } 146 147 @Override 148 public boolean containsValue(Object value) { 149 return this.targetMap.containsValue(value); 150 } 151 152 @Override 153 @Nullable 154 public List<V> get(Object key) { 155 return this.targetMap.get(key); 156 } 157 158 @Override 159 @Nullable 160 public List<V> put(K key, List<V> value) { 161 return this.targetMap.put(key, value); 162 } 163 164 @Override 165 @Nullable 166 public List<V> remove(Object key) { 167 return this.targetMap.remove(key); 168 } 169 170 @Override 171 public void putAll(Map<? extends K, ? extends List<V>> map) { 172 this.targetMap.putAll(map); 173 } 174 175 @Override 176 public void clear() { 177 this.targetMap.clear(); 178 } 179 180 @Override 181 public Set<K> keySet() { 182 return this.targetMap.keySet(); 183 } 184 185 @Override 186 public Collection<List<V>> values() { 187 return this.targetMap.values(); 188 } 189 190 @Override 191 public Set<Entry<K, List<V>>> entrySet() { 192 return this.targetMap.entrySet(); 193 } 194 195 @Override 196 public boolean equals(@Nullable Object other) { 197 return (this == other || this.targetMap.equals(other)); 198 } 199 200 @Override 201 public int hashCode() { 202 return this.targetMap.hashCode(); 203 } 204 205 @Override 206 public String toString() { 207 return this.targetMap.toString(); 208 } 209 210 211 /** 212 * Create a deep copy of this Map. 213 * @return a copy of this Map, including a copy of each value-holding List entry 214 * (consistently using an independent modifiable {@link LinkedList} for each entry) 215 * along the lines of {@code MultiValueMap.addAll} semantics 216 * @since 4.2 217 * @see #addAll(MultiValueMap) 218 * @see #clone() 219 */ 220 public LinkedMultiValueMap<K, V> deepCopy() { 221 LinkedMultiValueMap<K, V> copy = new LinkedMultiValueMap<>(size()); 222 forEach((key, values) -> copy.put(key, new LinkedList<>(values))); 223 return copy; 224 } 225 226 /** 227 * Create a regular copy of this Map. 228 * @return a shallow copy of this Map, reusing this Map's value-holding List entries 229 * (even if some entries are shared or unmodifiable) along the lines of standard 230 * {@code Map.put} semantics 231 * @since 4.2 232 * @see #put(Object, List) 233 * @see #putAll(Map) 234 * @see LinkedMultiValueMap#LinkedMultiValueMap(Map) 235 * @see #deepCopy() 236 */ 237 @Override 238 public LinkedMultiValueMap<K, V> clone() { 239 return new LinkedMultiValueMap<>(this); 240 } 241 242}