16
16
17
17
package org .springframework .util ;
18
18
19
+ import java .io .Serializable ;
20
+ import java .util .Collection ;
19
21
import java .util .HashMap ;
20
22
import java .util .LinkedHashMap ;
21
23
import java .util .Locale ;
22
24
import java .util .Map ;
25
+ import java .util .Set ;
23
26
24
27
/**
25
28
* {@link LinkedHashMap} variant that stores String keys in a case-insensitive
34
37
* @since 3.0
35
38
*/
36
39
@ SuppressWarnings ("serial" )
37
- public class LinkedCaseInsensitiveMap <V > extends LinkedHashMap <String , V > {
40
+ public class LinkedCaseInsensitiveMap <V > implements Map <String , V >, Serializable , Cloneable {
38
41
39
- private Map <String , String > caseInsensitiveKeys ;
42
+ private final LinkedHashMap <String , V > targetMap ;
43
+
44
+ private final HashMap <String , String > caseInsensitiveKeys ;
40
45
41
46
private final Locale locale ;
42
47
@@ -46,7 +51,7 @@ public class LinkedCaseInsensitiveMap<V> extends LinkedHashMap<String, V> {
46
51
* @see java.lang.String#toLowerCase()
47
52
*/
48
53
public LinkedCaseInsensitiveMap () {
49
- this (null );
54
+ this (( Locale ) null );
50
55
}
51
56
52
57
/**
@@ -56,9 +61,7 @@ public LinkedCaseInsensitiveMap() {
56
61
* @see java.lang.String#toLowerCase(java.util.Locale)
57
62
*/
58
63
public LinkedCaseInsensitiveMap (Locale locale ) {
59
- super ();
60
- this .caseInsensitiveKeys = new HashMap <String , String >();
61
- this .locale = (locale != null ? locale : Locale .getDefault ());
64
+ this (16 , locale );
62
65
}
63
66
64
67
/**
@@ -81,19 +84,68 @@ public LinkedCaseInsensitiveMap(int initialCapacity) {
81
84
* @see java.lang.String#toLowerCase(java.util.Locale)
82
85
*/
83
86
public LinkedCaseInsensitiveMap (int initialCapacity , Locale locale ) {
84
- super (initialCapacity );
87
+ this .targetMap = new LinkedHashMap <String , V >(initialCapacity ) {
88
+ @ Override
89
+ protected boolean removeEldestEntry (Map .Entry <String , V > eldest ) {
90
+ boolean doRemove = LinkedCaseInsensitiveMap .this .removeEldestEntry (eldest );
91
+ if (doRemove ) {
92
+ caseInsensitiveKeys .remove (convertKey (eldest .getKey ()));
93
+ }
94
+ return doRemove ;
95
+ }
96
+ };
85
97
this .caseInsensitiveKeys = new HashMap <String , String >(initialCapacity );
86
98
this .locale = (locale != null ? locale : Locale .getDefault ());
87
99
}
88
100
101
+ /**
102
+ * Copy constructor.
103
+ */
104
+ @ SuppressWarnings ("unchecked" )
105
+ private LinkedCaseInsensitiveMap (LinkedCaseInsensitiveMap <V > other ) {
106
+ this .targetMap = (LinkedHashMap <String , V >) other .targetMap .clone ();
107
+ this .caseInsensitiveKeys = (HashMap <String , String >) other .caseInsensitiveKeys .clone ();
108
+ this .locale = other .locale ;
109
+ }
110
+
111
+
112
+ @ Override
113
+ public int size () {
114
+ return this .targetMap .size ();
115
+ }
116
+
117
+ @ Override
118
+ public boolean isEmpty () {
119
+ return this .targetMap .isEmpty ();
120
+ }
121
+
122
+ @ Override
123
+ public boolean containsValue (Object value ) {
124
+ return this .targetMap .containsValue (value );
125
+ }
126
+
127
+ @ Override
128
+ public Set <String > keySet () {
129
+ return this .targetMap .keySet ();
130
+ }
131
+
132
+ @ Override
133
+ public Collection <V > values () {
134
+ return this .targetMap .values ();
135
+ }
136
+
137
+ @ Override
138
+ public Set <Entry <String , V >> entrySet () {
139
+ return this .targetMap .entrySet ();
140
+ }
89
141
90
142
@ Override
91
143
public V put (String key , V value ) {
92
144
String oldKey = this .caseInsensitiveKeys .put (convertKey (key ), key );
93
145
if (oldKey != null && !oldKey .equals (key )) {
94
- super .remove (oldKey );
146
+ this . targetMap .remove (oldKey );
95
147
}
96
- return super .put (key , value );
148
+ return this . targetMap .put (key , value );
97
149
}
98
150
99
151
@ Override
@@ -116,19 +168,18 @@ public V get(Object key) {
116
168
if (key instanceof String ) {
117
169
String caseInsensitiveKey = this .caseInsensitiveKeys .get (convertKey ((String ) key ));
118
170
if (caseInsensitiveKey != null ) {
119
- return super .get (caseInsensitiveKey );
171
+ return this . targetMap .get (caseInsensitiveKey );
120
172
}
121
173
}
122
174
return null ;
123
175
}
124
176
125
- // Overridden to avoid LinkedHashMap's own hash computation in its getOrDefault impl
126
177
@ Override
127
178
public V getOrDefault (Object key , V defaultValue ) {
128
179
if (key instanceof String ) {
129
180
String caseInsensitiveKey = this .caseInsensitiveKeys .get (convertKey ((String ) key ));
130
181
if (caseInsensitiveKey != null ) {
131
- return super .get (caseInsensitiveKey );
182
+ return this . targetMap .get (caseInsensitiveKey );
132
183
}
133
184
}
134
185
return defaultValue ;
@@ -139,7 +190,7 @@ public V remove(Object key) {
139
190
if (key instanceof String ) {
140
191
String caseInsensitiveKey = this .caseInsensitiveKeys .remove (convertKey ((String ) key ));
141
192
if (caseInsensitiveKey != null ) {
142
- return super .remove (caseInsensitiveKey );
193
+ return this . targetMap .remove (caseInsensitiveKey );
143
194
}
144
195
}
145
196
return null ;
@@ -148,15 +199,28 @@ public V remove(Object key) {
148
199
@ Override
149
200
public void clear () {
150
201
this .caseInsensitiveKeys .clear ();
151
- super .clear ();
202
+ this . targetMap .clear ();
152
203
}
153
204
205
+
154
206
@ Override
155
- @ SuppressWarnings ("unchecked" )
156
- public Object clone () {
157
- LinkedCaseInsensitiveMap <V > copy = (LinkedCaseInsensitiveMap <V >) super .clone ();
158
- copy .caseInsensitiveKeys = new HashMap <String , String >(this .caseInsensitiveKeys );
159
- return copy ;
207
+ public LinkedCaseInsensitiveMap <V > clone () {
208
+ return new LinkedCaseInsensitiveMap <V >(this );
209
+ }
210
+
211
+ @ Override
212
+ public boolean equals (Object obj ) {
213
+ return this .targetMap .equals (obj );
214
+ }
215
+
216
+ @ Override
217
+ public int hashCode () {
218
+ return this .targetMap .hashCode ();
219
+ }
220
+
221
+ @ Override
222
+ public String toString () {
223
+ return this .targetMap .toString ();
160
224
}
161
225
162
226
@@ -172,4 +236,14 @@ protected String convertKey(String key) {
172
236
return key .toLowerCase (this .locale );
173
237
}
174
238
239
+ /**
240
+ * Determine whether this map should remove the given eldest entry.
241
+ * @param eldest the candidate entry
242
+ * @return {@code true} for removing it, {@code false} for keeping it
243
+ * @see LinkedHashMap#removeEldestEntry
244
+ */
245
+ protected boolean removeEldestEntry (Map .Entry <String , V > eldest ) {
246
+ return false ;
247
+ }
248
+
175
249
}
0 commit comments