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 027/** 028 * Simple implementation of {@link MultiValueMap} that wraps a {@link LinkedHashMap}, 029 * storing multiple values in a {@link LinkedList}. 030 * 031 * <p>This Map implementation is generally not thread-safe. It is primarily designed 032 * for data structures exposed from request objects, for use in a single thread only. 033 * 034 * @author Arjen Poutsma 035 * @author Juergen Hoeller 036 * @since 3.0 037 * @param <K> the key type 038 * @param <V> the value element type 039 */ 040public class LinkedMultiValueMap<K, V> implements MultiValueMap<K, V>, Serializable, Cloneable { 041 042 private static final long serialVersionUID = 3801124242820219131L; 043 044 private final Map<K, List<V>> targetMap; 045 046 047 /** 048 * Create a new LinkedMultiValueMap that wraps a {@link LinkedHashMap}. 049 */ 050 public LinkedMultiValueMap() { 051 this.targetMap = new LinkedHashMap<K, List<V>>(); 052 } 053 054 /** 055 * Create a new LinkedMultiValueMap that wraps a {@link LinkedHashMap} 056 * with the given initial capacity. 057 * @param initialCapacity the initial capacity 058 */ 059 public LinkedMultiValueMap(int initialCapacity) { 060 this.targetMap = new LinkedHashMap<K, List<V>>(initialCapacity); 061 } 062 063 /** 064 * Copy constructor: Create a new LinkedMultiValueMap with the same mappings as 065 * the specified Map. Note that this will be a shallow copy; its value-holding 066 * List entries will get reused and therefore cannot get modified independently. 067 * @param otherMap the Map whose mappings are to be placed in this Map 068 * @see #clone() 069 * @see #deepCopy() 070 */ 071 public LinkedMultiValueMap(Map<K, List<V>> otherMap) { 072 this.targetMap = new LinkedHashMap<K, List<V>>(otherMap); 073 } 074 075 076 // MultiValueMap implementation 077 078 @Override 079 public V getFirst(K key) { 080 List<V> values = this.targetMap.get(key); 081 return (values != null && !values.isEmpty() ? values.get(0) : null); 082 } 083 084 @Override 085 public void add(K key, V value) { 086 List<V> values = this.targetMap.get(key); 087 if (values == null) { 088 values = new LinkedList<V>(); 089 this.targetMap.put(key, values); 090 } 091 values.add(value); 092 } 093 094 @Override 095 public void set(K key, V value) { 096 List<V> values = new LinkedList<V>(); 097 values.add(value); 098 this.targetMap.put(key, values); 099 } 100 101 @Override 102 public void setAll(Map<K, V> values) { 103 for (Entry<K, V> entry : values.entrySet()) { 104 set(entry.getKey(), entry.getValue()); 105 } 106 } 107 108 @Override 109 public Map<K, V> toSingleValueMap() { 110 LinkedHashMap<K, V> singleValueMap = new LinkedHashMap<K,V>(this.targetMap.size()); 111 for (Entry<K, List<V>> entry : this.targetMap.entrySet()) { 112 List<V> values = entry.getValue(); 113 if (values != null && !values.isEmpty()) { 114 singleValueMap.put(entry.getKey(), values.get(0)); 115 } 116 } 117 return singleValueMap; 118 } 119 120 121 // Map implementation 122 123 @Override 124 public int size() { 125 return this.targetMap.size(); 126 } 127 128 @Override 129 public boolean isEmpty() { 130 return this.targetMap.isEmpty(); 131 } 132 133 @Override 134 public boolean containsKey(Object key) { 135 return this.targetMap.containsKey(key); 136 } 137 138 @Override 139 public boolean containsValue(Object value) { 140 return this.targetMap.containsValue(value); 141 } 142 143 @Override 144 public List<V> get(Object key) { 145 return this.targetMap.get(key); 146 } 147 148 @Override 149 public List<V> put(K key, List<V> value) { 150 return this.targetMap.put(key, value); 151 } 152 153 @Override 154 public List<V> remove(Object key) { 155 return this.targetMap.remove(key); 156 } 157 158 @Override 159 public void putAll(Map<? extends K, ? extends List<V>> map) { 160 this.targetMap.putAll(map); 161 } 162 163 @Override 164 public void clear() { 165 this.targetMap.clear(); 166 } 167 168 @Override 169 public Set<K> keySet() { 170 return this.targetMap.keySet(); 171 } 172 173 @Override 174 public Collection<List<V>> values() { 175 return this.targetMap.values(); 176 } 177 178 @Override 179 public Set<Entry<K, List<V>>> entrySet() { 180 return this.targetMap.entrySet(); 181 } 182 183 184 /** 185 * Create a deep copy of this Map. 186 * @return a copy of this Map, including a copy of each value-holding List entry 187 * @since 4.2 188 * @see #clone() 189 */ 190 public LinkedMultiValueMap<K, V> deepCopy() { 191 LinkedMultiValueMap<K, V> copy = new LinkedMultiValueMap<K, V>(this.targetMap.size()); 192 for (Map.Entry<K, List<V>> entry : this.targetMap.entrySet()) { 193 copy.put(entry.getKey(), new LinkedList<V>(entry.getValue())); 194 } 195 return copy; 196 } 197 198 /** 199 * Create a regular copy of this Map. 200 * @return a shallow copy of this Map, reusing this Map's value-holding List entries 201 * @since 4.2 202 * @see LinkedMultiValueMap#LinkedMultiValueMap(Map) 203 * @see #deepCopy() 204 */ 205 @Override 206 public LinkedMultiValueMap<K, V> clone() { 207 return new LinkedMultiValueMap<K, V>(this); 208 } 209 210 @Override 211 public boolean equals(Object other) { 212 return (this == other || this.targetMap.equals(other)); 213 } 214 215 @Override 216 public int hashCode() { 217 return this.targetMap.hashCode(); 218 } 219 220 @Override 221 public String toString() { 222 return this.targetMap.toString(); 223 } 224 225}