1 /* 2 * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8005698 27 * @run testng/othervm -Dtest.map.collisions.shortrun=true InPlaceOpsCollisions 28 * @summary Ensure overrides of in-place operations in Maps behave well with lots of collisions. 29 */ 30 package test.java.util.Map; 31 32 import java.util.Map; 33 import java.util.function.BiFunction; 34 import java.util.function.Function; 35 import java.util.function.Supplier; 36 37 import org.testng.annotations.Test; 38 import static org.testng.Assert.assertTrue; 39 import static org.testng.Assert.assertFalse; 40 import static org.testng.Assert.assertEquals; 41 import static org.testng.Assert.assertNull; 42 43 import android.platform.test.annotations.LargeTest; 44 45 @LargeTest 46 public class InPlaceOpsCollisions extends MapWithCollisionsProviders { 47 48 @Test(dataProvider = "mapsWithObjectsAndStrings") testPutIfAbsent(String desc, Supplier<Map<Object, Object>> ms, Object val)49 public void testPutIfAbsent(String desc, Supplier<Map<Object, Object>> ms, Object val) { 50 Map<Object, Object> map = ms.get(); 51 Object[] keys = map.keySet().toArray(); 52 Object retVal; 53 removeOddKeys(map, keys); 54 for (int i = 0; i < keys.length; i++) { 55 retVal = map.putIfAbsent(keys[i], val); 56 if (i % 2 == 0) { // even: not absent, not put 57 58 assertEquals(retVal, keys[i], 59 String.format("putIfAbsent: (%s[%d]) retVal", desc, i)); 60 assertEquals(keys[i], map.get(keys[i]), 61 String.format("putIfAbsent: get(%s[%d])", desc, i)); 62 assertTrue(map.containsValue(keys[i]), 63 String.format("putIfAbsent: containsValue(%s[%d])", desc, i)); 64 } else { // odd: absent, was put 65 assertNull(retVal, 66 String.format("putIfAbsent: (%s[%d]) retVal", desc, i)); 67 assertEquals(val, map.get(keys[i]), 68 String.format("putIfAbsent: get(%s[%d])", desc, i)); 69 assertFalse(map.containsValue(keys[i]), 70 String.format("putIfAbsent: !containsValue(%s[%d])", desc, i)); 71 } 72 assertTrue(map.containsKey(keys[i]), 73 String.format("insertion: containsKey(%s[%d])", desc, i)); 74 } 75 assertEquals(map.size(), keys.length, 76 String.format("map expected size m%d != k%d", map.size(), keys.length)); 77 } 78 79 @Test(dataProvider = "mapsWithObjectsAndStrings") testRemoveMapping(String desc, Supplier<Map<Object, Object>> ms, Object val)80 public void testRemoveMapping(String desc, Supplier<Map<Object, Object>> ms, Object val) { 81 Map<Object, Object> map = ms.get(); 82 Object[] keys = map.keySet().toArray(); 83 boolean removed; 84 int removes = 0; 85 remapOddKeys(map, keys, val); 86 for (int i = 0; i < keys.length; i++) { 87 removed = map.remove(keys[i], keys[i]); 88 if (i % 2 == 0) { // even: original mapping, should be removed 89 assertTrue(removed, 90 String.format("removeMapping: retVal(%s[%d])", desc, i)); 91 assertNull(map.get(keys[i]), 92 String.format("removeMapping: get(%s[%d])", desc, i)); 93 assertFalse(map.containsKey(keys[i]), 94 String.format("removeMapping: !containsKey(%s[%d])", desc, i)); 95 assertFalse(map.containsValue(keys[i]), 96 String.format("removeMapping: !containsValue(%s[%d])", desc, i)); 97 removes++; 98 } else { // odd: new mapping, not removed 99 assertFalse(removed, 100 String.format("removeMapping: retVal(%s[%d])", desc, i)); 101 assertEquals(val, map.get(keys[i]), 102 String.format("removeMapping: get(%s[%d])", desc, i)); 103 assertTrue(map.containsKey(keys[i]), 104 String.format("removeMapping: containsKey(%s[%d])", desc, i)); 105 assertTrue(map.containsValue(val), 106 String.format("removeMapping: containsValue(%s[%d])", desc, i)); 107 } 108 } 109 assertEquals(map.size(), keys.length - removes, 110 String.format("map expected size m%d != k%d", map.size(), keys.length - removes)); 111 } 112 113 @Test(dataProvider = "mapsWithObjectsAndStrings") testReplaceOldValue(String desc, Supplier<Map<Object, Object>> ms, Object val)114 public void testReplaceOldValue(String desc, Supplier<Map<Object, Object>> ms, Object val) { 115 // remap odds to val 116 // call replace to replace for val, for all keys 117 // check that all keys map to value from keys array 118 Map<Object, Object> map = ms.get(); 119 Object[] keys = map.keySet().toArray(); 120 boolean replaced; 121 remapOddKeys(map, keys, val); 122 123 for (int i = 0; i < keys.length; i++) { 124 replaced = map.replace(keys[i], val, keys[i]); 125 if (i % 2 == 0) { // even: original mapping, should not be replaced 126 assertFalse(replaced, 127 String.format("replaceOldValue: retVal(%s[%d])", desc, i)); 128 } else { // odd: new mapping, should be replaced 129 assertTrue(replaced, 130 String.format("replaceOldValue: get(%s[%d])", desc, i)); 131 } 132 assertEquals(keys[i], map.get(keys[i]), 133 String.format("replaceOldValue: get(%s[%d])", desc, i)); 134 assertTrue(map.containsKey(keys[i]), 135 String.format("replaceOldValue: containsKey(%s[%d])", desc, i)); 136 assertTrue(map.containsValue(keys[i]), 137 String.format("replaceOldValue: containsValue(%s[%d])", desc, i)); 138 } 139 assertFalse(map.containsValue(val), 140 String.format("replaceOldValue: !containsValue(%s[%s])", desc, val)); 141 assertEquals(map.size(), keys.length, 142 String.format("map expected size m%d != k%d", map.size(), keys.length)); 143 } 144 145 @Test(dataProvider = "mapsWithObjectsAndStrings") testReplaceIfMapped(String desc, Supplier<Map<Object, Object>> ms, Object val)146 public void testReplaceIfMapped(String desc, Supplier<Map<Object, Object>> ms, Object val) { 147 // remove odd keys 148 // call replace for all keys[] 149 // odd keys should remain absent, even keys should be mapped to EXTRA, no value from keys[] should be in map 150 Map<Object, Object> map = ms.get(); 151 Object[] keys = map.keySet().toArray(); 152 int expectedSize1 = 0; 153 removeOddKeys(map, keys); 154 int expectedSize2 = map.size(); 155 156 for (int i = 0; i < keys.length; i++) { 157 Object retVal = map.replace(keys[i], val); 158 if (i % 2 == 0) { // even: still in map, should be replaced 159 assertEquals(retVal, keys[i], 160 String.format("replaceIfMapped: retVal(%s[%d])", desc, i)); 161 assertEquals(val, map.get(keys[i]), 162 String.format("replaceIfMapped: get(%s[%d])", desc, i)); 163 assertTrue(map.containsKey(keys[i]), 164 String.format("replaceIfMapped: containsKey(%s[%d])", desc, i)); 165 expectedSize1++; 166 } else { // odd: was removed, should not be replaced 167 assertNull(retVal, 168 String.format("replaceIfMapped: retVal(%s[%d])", desc, i)); 169 assertNull(map.get(keys[i]), 170 String.format("replaceIfMapped: get(%s[%d])", desc, i)); 171 assertFalse(map.containsKey(keys[i]), 172 String.format("replaceIfMapped: containsKey(%s[%d])", desc, i)); 173 } 174 assertFalse(map.containsValue(keys[i]), 175 String.format("replaceIfMapped: !containsValue(%s[%d])", desc, i)); 176 } 177 assertTrue(map.containsValue(val), 178 String.format("replaceIfMapped: containsValue(%s[%s])", desc, val)); 179 assertEquals(map.size(), expectedSize1, 180 String.format("map expected size#1 m%d != k%d", map.size(), expectedSize1)); 181 assertEquals(map.size(), expectedSize2, 182 String.format("map expected size#2 m%d != k%d", map.size(), expectedSize2)); 183 184 } 185 testComputeIfAbsent(Map<T, T> map, String desc, T[] keys, Function<T, T> mappingFunction)186 private static <T> void testComputeIfAbsent(Map<T, T> map, String desc, T[] keys, 187 Function<T, T> mappingFunction) { 188 // remove a third of the keys 189 // call computeIfAbsent for all keys, func returns EXTRA 190 // check that removed keys now -> EXTRA, other keys -> original val 191 T expectedVal = mappingFunction.apply(keys[0]); 192 T retVal; 193 int expectedSize = 0; 194 removeThirdKeys(map, keys); 195 for (int i = 0; i < keys.length; i++) { 196 retVal = map.computeIfAbsent(keys[i], mappingFunction); 197 if (i % 3 != 2) { // key present, not computed 198 assertEquals(retVal, keys[i], 199 String.format("computeIfAbsent: (%s[%d]) retVal", desc, i)); 200 assertEquals(keys[i], map.get(keys[i]), 201 String.format("computeIfAbsent: get(%s[%d])", desc, i)); 202 assertTrue(map.containsValue(keys[i]), 203 String.format("computeIfAbsent: containsValue(%s[%d])", desc, i)); 204 assertTrue(map.containsKey(keys[i]), 205 String.format("insertion: containsKey(%s[%d])", desc, i)); 206 expectedSize++; 207 } else { // key absent, computed unless function return null 208 assertEquals(retVal, expectedVal, 209 String.format("computeIfAbsent: (%s[%d]) retVal", desc, i)); 210 assertEquals(expectedVal, map.get(keys[i]), 211 String.format("computeIfAbsent: get(%s[%d])", desc, i)); 212 assertFalse(map.containsValue(keys[i]), 213 String.format("computeIfAbsent: !containsValue(%s[%d])", desc, i)); 214 // mapping should not be added if function returns null 215 assertTrue(map.containsKey(keys[i]) != (expectedVal == null), 216 String.format("insertion: containsKey(%s[%d])", desc, i)); 217 if (expectedVal != null) { 218 expectedSize++; 219 } 220 } 221 } 222 if (expectedVal != null) { 223 assertTrue(map.containsValue(expectedVal), 224 String.format("computeIfAbsent: containsValue(%s[%s])", desc, expectedVal)); 225 } 226 assertEquals(map.size(), expectedSize, 227 String.format("map expected size m%d != k%d", map.size(), expectedSize)); 228 } 229 230 @Test(dataProvider = "mapsWithObjectsAndStrings") testComputeIfAbsentNonNull(String desc, Supplier<Map<Object, Object>> ms, Object val)231 public void testComputeIfAbsentNonNull(String desc, Supplier<Map<Object, Object>> ms, Object val) { 232 Map<Object, Object> map = ms.get(); 233 Object[] keys = map.keySet().toArray(); 234 testComputeIfAbsent(map, desc, keys, (k) -> val); 235 } 236 237 @Test(dataProvider = "mapsWithObjectsAndStrings") testComputeIfAbsentNull(String desc, Supplier<Map<Object, Object>> ms, Object val)238 public void testComputeIfAbsentNull(String desc, Supplier<Map<Object, Object>> ms, Object val) { 239 Map<Object, Object> map = ms.get(); 240 Object[] keys = map.keySet().toArray(); 241 testComputeIfAbsent(map, desc, keys, (k) -> null); 242 } 243 testComputeIfPresent(Map<T, T> map, String desc, T[] keys, BiFunction<T, T, T> mappingFunction)244 private static <T> void testComputeIfPresent(Map<T, T> map, String desc, T[] keys, 245 BiFunction<T, T, T> mappingFunction) { 246 // remove a third of the keys 247 // call testComputeIfPresent for all keys[] 248 // removed keys should remain absent, even keys should be mapped to $RESULT 249 // no value from keys[] should be in map 250 T funcResult = mappingFunction.apply(keys[0], keys[0]); 251 int expectedSize1 = 0; 252 removeThirdKeys(map, keys); 253 254 for (int i = 0; i < keys.length; i++) { 255 T retVal = map.computeIfPresent(keys[i], mappingFunction); 256 if (i % 3 != 2) { // key present 257 if (funcResult == null) { // was removed 258 assertFalse(map.containsKey(keys[i]), 259 String.format("replaceIfMapped: containsKey(%s[%d])", desc, i)); 260 } else { // value was replaced 261 assertTrue(map.containsKey(keys[i]), 262 String.format("replaceIfMapped: containsKey(%s[%d])", desc, i)); 263 expectedSize1++; 264 } 265 assertEquals(retVal, funcResult, 266 String.format("computeIfPresent: retVal(%s[%s])", desc, i)); 267 assertEquals(funcResult, map.get(keys[i]), 268 String.format("replaceIfMapped: get(%s[%d])", desc, i)); 269 270 } else { // odd: was removed, should not be replaced 271 assertNull(retVal, 272 String.format("replaceIfMapped: retVal(%s[%d])", desc, i)); 273 assertNull(map.get(keys[i]), 274 String.format("replaceIfMapped: get(%s[%d])", desc, i)); 275 assertFalse(map.containsKey(keys[i]), 276 String.format("replaceIfMapped: containsKey(%s[%d])", desc, i)); 277 } 278 assertFalse(map.containsValue(keys[i]), 279 String.format("replaceIfMapped: !containsValue(%s[%d])", desc, i)); 280 } 281 assertEquals(map.size(), expectedSize1, 282 String.format("map expected size#1 m%d != k%d", map.size(), expectedSize1)); 283 } 284 285 @Test(dataProvider = "mapsWithObjectsAndStrings") testComputeIfPresentNonNull(String desc, Supplier<Map<Object, Object>> ms, Object val)286 public void testComputeIfPresentNonNull(String desc, Supplier<Map<Object, Object>> ms, Object val) { 287 Map<Object, Object> map = ms.get(); 288 Object[] keys = map.keySet().toArray(); 289 testComputeIfPresent(map, desc, keys, (k, v) -> val); 290 } 291 292 @Test(dataProvider = "mapsWithObjectsAndStrings") testComputeIfPresentNull(String desc, Supplier<Map<Object, Object>> ms, Object val)293 public void testComputeIfPresentNull(String desc, Supplier<Map<Object, Object>> ms, Object val) { 294 Map<Object, Object> map = ms.get(); 295 Object[] keys = map.keySet().toArray(); 296 testComputeIfPresent(map, desc, keys, (k, v) -> null); 297 } 298 299 @Test(dataProvider = "hashMapsWithObjects") testComputeNonNull(String desc, Supplier<Map<IntKey, IntKey>> ms, IntKey val)300 public void testComputeNonNull(String desc, Supplier<Map<IntKey, IntKey>> ms, IntKey val) { 301 // remove a third of the keys 302 // call compute() for all keys[] 303 // all keys should be present: removed keys -> EXTRA, others to k-1 304 Map<IntKey, IntKey> map = ms.get(); 305 IntKey[] keys = map.keySet().stream().sorted().toArray(IntKey[]::new); 306 BiFunction<IntKey, IntKey, IntKey> mappingFunction = (k, v) -> { 307 if (v == null) { 308 return val; 309 } else { 310 return keys[k.getValue() - 1]; 311 } 312 }; 313 removeThirdKeys(map, keys); 314 for (int i = 1; i < keys.length; i++) { 315 IntKey retVal = map.compute(keys[i], mappingFunction); 316 if (i % 3 != 2) { // key present, should be mapped to k-1 317 assertEquals(retVal, keys[i - 1], 318 String.format("compute: retVal(%s[%d])", desc, i)); 319 assertEquals(keys[i - 1], map.get(keys[i]), 320 String.format("compute: get(%s[%d])", desc, i)); 321 } else { // odd: was removed, should be replaced with EXTRA 322 assertEquals(retVal, val, 323 String.format("compute: retVal(%s[%d])", desc, i)); 324 assertEquals(val, map.get(keys[i]), 325 String.format("compute: get(%s[%d])", desc, i)); 326 } 327 assertTrue(map.containsKey(keys[i]), 328 String.format("compute: containsKey(%s[%d])", desc, i)); 329 } 330 assertEquals(map.size(), keys.length, 331 String.format("map expected size#1 m%d != k%d", map.size(), keys.length)); 332 assertTrue(map.containsValue(val), 333 String.format("compute: containsValue(%s[%s])", desc, val)); 334 assertFalse(map.containsValue(null), 335 String.format("compute: !containsValue(%s,[null])", desc)); 336 } 337 338 @Test(dataProvider = "mapsWithObjectsAndStrings") testComputeNull(String desc, Supplier<Map<Object, Object>> ms, Object val)339 public void testComputeNull(String desc, Supplier<Map<Object, Object>> ms, Object val) { 340 // remove a third of the keys 341 // call compute() for all keys[] 342 // removed keys should -> EXTRA 343 // for other keys: func returns null, should have no mapping 344 Map<Object, Object> map = ms.get(); 345 Object[] keys = map.keySet().toArray(); 346 BiFunction<Object, Object, Object> mappingFunction = (k, v) -> { 347 // if absent/null -> EXTRA 348 // if present -> null 349 if (v == null) { 350 return val; 351 } else { 352 return null; 353 } 354 }; 355 int expectedSize = 0; 356 removeThirdKeys(map, keys); 357 for (int i = 0; i < keys.length; i++) { 358 Object retVal = map.compute(keys[i], mappingFunction); 359 if (i % 3 != 2) { // key present, func returned null, should be absent from map 360 assertNull(retVal, 361 String.format("compute: retVal(%s[%d])", desc, i)); 362 assertNull(map.get(keys[i]), 363 String.format("compute: get(%s[%d])", desc, i)); 364 assertFalse(map.containsKey(keys[i]), 365 String.format("compute: containsKey(%s[%d])", desc, i)); 366 assertFalse(map.containsValue(keys[i]), 367 String.format("compute: containsValue(%s[%s])", desc, i)); 368 } else { // odd: was removed, should now be mapped to EXTRA 369 assertEquals(retVal, val, 370 String.format("compute: retVal(%s[%d])", desc, i)); 371 assertEquals(val, map.get(keys[i]), 372 String.format("compute: get(%s[%d])", desc, i)); 373 assertTrue(map.containsKey(keys[i]), 374 String.format("compute: containsKey(%s[%d])", desc, i)); 375 expectedSize++; 376 } 377 } 378 assertTrue(map.containsValue(val), 379 String.format("compute: containsValue(%s[%s])", desc, val)); 380 assertEquals(map.size(), expectedSize, 381 String.format("map expected size#1 m%d != k%d", map.size(), expectedSize)); 382 } 383 384 @Test(dataProvider = "hashMapsWithObjects") testMergeNonNull(String desc, Supplier<Map<IntKey, IntKey>> ms, IntKey val)385 public void testMergeNonNull(String desc, Supplier<Map<IntKey, IntKey>> ms, IntKey val) { 386 // remove a third of the keys 387 // call merge() for all keys[] 388 // all keys should be present: removed keys now -> EXTRA, other keys -> k-1 389 Map<IntKey, IntKey> map = ms.get(); 390 IntKey[] keys = map.keySet().stream().sorted().toArray(IntKey[]::new); 391 392 // Map to preceding key 393 BiFunction<IntKey, IntKey, IntKey> mappingFunction 394 = (k, v) -> keys[k.getValue() - 1]; 395 removeThirdKeys(map, keys); 396 for (int i = 1; i < keys.length; i++) { 397 IntKey retVal = map.merge(keys[i], val, mappingFunction); 398 if (i % 3 != 2) { // key present, should be mapped to k-1 399 assertEquals(retVal, keys[i - 1], 400 String.format("compute: retVal(%s[%d])", desc, i)); 401 assertEquals(keys[i - 1], map.get(keys[i]), 402 String.format("compute: get(%s[%d])", desc, i)); 403 } else { // odd: was removed, should be replaced with EXTRA 404 assertEquals(retVal, val, 405 String.format("compute: retVal(%s[%d])", desc, i)); 406 assertEquals(val, map.get(keys[i]), 407 String.format("compute: get(%s[%d])", desc, i)); 408 } 409 assertTrue(map.containsKey(keys[i]), 410 String.format("compute: containsKey(%s[%d])", desc, i)); 411 } 412 413 assertEquals(map.size(), keys.length, 414 String.format("map expected size#1 m%d != k%d", map.size(), keys.length)); 415 assertTrue(map.containsValue(val), 416 String.format("compute: containsValue(%s[%s])", desc, val)); 417 assertFalse(map.containsValue(null), 418 String.format("compute: !containsValue(%s,[null])", desc)); 419 } 420 421 @Test(dataProvider = "mapsWithObjectsAndStrings") testMergeNull(String desc, Supplier<Map<Object, Object>> ms, Object val)422 public void testMergeNull(String desc, Supplier<Map<Object, Object>> ms, Object val) { 423 // remove a third of the keys 424 // call merge() for all keys[] 425 // result: removed keys -> EXTRA, other keys absent 426 427 Map<Object, Object> map = ms.get(); 428 Object[] keys = map.keySet().toArray(); 429 BiFunction<Object, Object, Object> mappingFunction = (k, v) -> null; 430 int expectedSize = 0; 431 removeThirdKeys(map, keys); 432 for (int i = 0; i < keys.length; i++) { 433 Object retVal = map.merge(keys[i], val, mappingFunction); 434 if (i % 3 != 2) { // key present, func returned null, should be absent from map 435 assertNull(retVal, 436 String.format("compute: retVal(%s[%d])", desc, i)); 437 assertNull(map.get(keys[i]), 438 String.format("compute: get(%s[%d])", desc, i)); 439 assertFalse(map.containsKey(keys[i]), 440 String.format("compute: containsKey(%s[%d])", desc, i)); 441 } else { // odd: was removed, should now be mapped to EXTRA 442 assertEquals(retVal, val, 443 String.format("compute: retVal(%s[%d])", desc, i)); 444 assertEquals(val, map.get(keys[i]), 445 String.format("compute: get(%s[%d])", desc, i)); 446 assertTrue(map.containsKey(keys[i]), 447 String.format("compute: containsKey(%s[%d])", desc, i)); 448 expectedSize++; 449 } 450 assertFalse(map.containsValue(keys[i]), 451 String.format("compute: containsValue(%s[%s])", desc, i)); 452 } 453 assertTrue(map.containsValue(val), 454 String.format("compute: containsValue(%s[%s])", desc, val)); 455 assertEquals(map.size(), expectedSize, 456 String.format("map expected size#1 m%d != k%d", map.size(), expectedSize)); 457 } 458 459 /* 460 * Remove half of the keys 461 */ removeOddKeys(Map<T, T> map, T[] keys)462 private static <T> void removeOddKeys(Map<T, T> map, /*String keys_desc, */ T[] keys) { 463 int removes = 0; 464 for (int i = 0; i < keys.length; i++) { 465 if (i % 2 != 0) { 466 map.remove(keys[i]); 467 removes++; 468 } 469 } 470 assertEquals(map.size(), keys.length - removes, 471 String.format("map expected size m%d != k%d", map.size(), keys.length - removes)); 472 } 473 474 /* 475 * Remove every third key 476 * This will hopefully leave some removed keys in TreeBins for, e.g., computeIfAbsent 477 * w/ a func that returns null. 478 * 479 * TODO: consider using this in other tests (and maybe adding a remapThirdKeys) 480 */ removeThirdKeys(Map<T, T> map, T[] keys)481 private static <T> void removeThirdKeys(Map<T, T> map, /*String keys_desc, */ T[] keys) { 482 int removes = 0; 483 for (int i = 0; i < keys.length; i++) { 484 if (i % 3 == 2) { 485 map.remove(keys[i]); 486 removes++; 487 } 488 } 489 assertEquals(map.size(), keys.length - removes, 490 String.format("map expected size m%d != k%d", map.size(), keys.length - removes)); 491 } 492 493 /* 494 * Re-map the odd-numbered keys to map to the EXTRA value 495 */ remapOddKeys(Map<T, T> map, T[] keys, T val)496 private static <T> void remapOddKeys(Map<T, T> map, T[] keys, T val) { 497 for (int i = 0; i < keys.length; i++) { 498 if (i % 2 != 0) { 499 map.put(keys[i], val); 500 } 501 } 502 } 503 504 }