下面列出了java.util.Map#merge ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
/**
* Returns a new map that is the result of deeply merging original and overrides.
*
* <p>Each key in original is merged with the corresponding key in overrides as follows: - an
* explicitly null entry in overrides removes a key in original - a map in original is merged with
* a map from overrides (via call to merge) - a non map in overrides results in an
* IllegalStateException - a collection in original is replaced with a collection in overrides - a
* non collection in overrides results in an IllegalStateException - the value is taken from
* overrides
*
* <p>Each remaining key in overrides is then added to the resulting map.
*
* @param original the original Map
* @param override the Map to override/merge into original
* @return a new Map containing the merge of original and overrides (never null)
* @throws IllegalStateException if incompatible types exist between original and overrides
*/
@Nonnull
public static Map<String, Object> merge(
@Nonnull Map<String, Object> original, @Nonnull Map<String, Object> override) {
final Set<String> remainingKeys = new LinkedHashSet<>(override.keySet());
final Map<String, Object> result = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : original.entrySet()) {
final String key = entry.getKey();
result.put(key, entry.getValue());
if (override.containsKey(key)) {
final Object value = override.get(key);
if (value == null) {
result.remove(key);
} else {
result.merge(key, value, MapUtils::mergeObject);
}
}
remainingKeys.remove(key);
}
for (String newKey : remainingKeys) {
result.put(newKey, override.get(newKey));
}
return result;
}
@Override
public void updateCount(Tuple4<Long, Long, Long, IntType> value, Map<Long, Integer> windowCounts) {
// verify the contents of that window, Tuple4.f1 and .f2 are the window start/end
// the sum should be "sum (start .. end-1)"
int expectedSum = 0;
// we shorten the range if it goes beyond elementsPerKey, because those are "incomplete" sliding windows
long countUntil = Math.min(value.f2, elementsPerKey);
for (long i = value.f1; i < countUntil; i++) {
// only sum up positive vals, to filter out the negative start of the
// first sliding windows
if (i > 0) {
expectedSum += i;
}
}
assertEquals("Window start: " + value.f1 + " end: " + value.f2, expectedSum, value.f3.value);
windowCounts.merge(value.f0, 1, (val, increment) -> val + increment);
}
@Override
public void updateCount(Tuple4<Long, Long, Long, IntType> value, Map<Long, Integer> windowCounts) {
// verify the contents of that window, Tuple4.f1 and .f2 are the window start/end
// the sum should be "sum (start .. end-1)"
int expectedSum = 0;
// we shorten the range if it goes beyond elementsPerKey, because those are "incomplete" sliding windows
long countUntil = Math.min(value.f2, elementsPerKey);
for (long i = value.f1; i < countUntil; i++) {
// only sum up positive vals, to filter out the negative start of the
// first sliding windows
if (i > 0) {
expectedSum += i;
}
}
assertEquals("Window start: " + value.f1 + " end: " + value.f2, expectedSum, value.f3.value);
windowCounts.merge(value.f0, 1, (val, increment) -> val + increment);
}
@Test(dataProvider = "caches", expectedExceptions = DeleteException.class)
@CacheSpec(keys = ReferenceType.STRONG, values = {ReferenceType.WEAK, ReferenceType.SOFT},
implementation = Implementation.Caffeine, expireAfterAccess = Expire.DISABLED,
expireAfterWrite = Expire.DISABLED, maximumSize = Maximum.DISABLED,
weigher = CacheWeigher.DEFAULT, population = Population.FULL, stats = Stats.ENABLED,
compute = Compute.SYNC, removalListener = Listener.CONSUMING, writer = Writer.EXCEPTIONAL)
public void merge_writerFails(Map<Integer, Integer> map, CacheContext context) {
Integer key = context.firstKey();
try {
context.clear();
GcFinalization.awaitFullGc();
map.merge(key, context.absentValue(), (k, v) -> v);
} finally {
context.disableRejectingCacheWriter();
assertThat(map.isEmpty(), is(false));
}
}
@Test(dataProvider = "caches")
@CacheSpec(population = Population.FULL, expiryTime = Expire.ONE_MINUTE,
mustExpireWithAnyOf = { AFTER_WRITE, VARIABLE },
expiry = { CacheExpiry.DISABLED, CacheExpiry.WRITE },
expireAfterWrite = {Expire.DISABLED, Expire.ONE_MINUTE})
public void merge_writeTime(Map<Integer, Integer> map, CacheContext context) {
Integer key = context.firstKey();
Integer value = context.absentValue();
map.merge(key, value, (oldValue, v) -> {
context.ticker().advance(5, TimeUnit.MINUTES);
return value;
});
context.cleanUp();
assertThat(map.size(), is(1));
assertThat(map.containsKey(key), is(true));
}
public static void main(String[] args) {
Map<Integer, String> map = new HashMap<>();
for (int i = 0; i < 10; i++) {
map.putIfAbsent(i, "val" + i);
}
map.forEach((id, val) -> System.out.println(val));
map.computeIfPresent(3, (num, val) -> val + num);
System.out.println(map.get(3)); // val33
map.computeIfPresent(9, (num, val) -> null);
System.out.println(map.containsKey(9)); // false
map.computeIfAbsent(23, num -> "val" + num);
System.out.println(map.containsKey(23)); // true
map.computeIfAbsent(3, num -> "bam");
System.out.println(map.get(3)); // val33
System.out.println(map.getOrDefault(42, "not found")); // not found
map.remove(3, "val3");
System.out.println(map.get(3)); // val33
map.remove(3, "val33");
System.out.println(map.get(3)); // null
map.merge(9, "val9", (value, newValue) -> value.concat(newValue));
System.out.println(map.get(9)); // val9
map.merge(9, "concat", (value, newValue) -> value.concat(newValue));
System.out.println(map.get(9)); // val9concat
}
private boolean runTests(List<TestClass> tests) {
Map<TestResult, Integer> totals = new ConcurrentHashMap<>();
for (TestClass testClass : tests) {
logger.info("Running test {} ...", testClass.getName());
int testCount = testClass.getTestCount();
try {
if (!testClass.beforeClass()) {
totals.merge(TestResult.FAILED, testCount, Integer::sum);
continue;
}
for (int i = 0; i < testCount; ++i) {
TestResult result = testClass.runTest(i);
totals.merge(result, 1, Integer::sum);
}
} finally {
testClass.afterClass();
}
}
int totalFailed = totals.getOrDefault(TestResult.FAILED, 0);
int totalPassed = totals.getOrDefault(TestResult.SUCCESS, 0);
int totalSkipped = totals.getOrDefault(TestResult.SKIPPED, 0);
int totalUnsupported = totals.getOrDefault(TestResult.UNSUPPORTED, 0);
if (totalSkipped > 0) {
logger.info("Skipped: {} tests", totalSkipped);
}
if (totalUnsupported > 0) {
logger.info("Unsupported: {} tests", totalUnsupported);
}
if (totalFailed > 0) {
logger.error("Failed {} out of {} tests", totalFailed, totalFailed + totalPassed);
} else {
logger.info("Passed all {} tests", totalPassed);
}
return totalFailed == 0;
}
@CheckNoWriter
@Test(dataProvider = "caches")
@CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL },
removalListener = { Listener.DEFAULT, Listener.REJECTING })
public void merge_error(Map<Integer, Integer> map, CacheContext context) {
try {
map.merge(context.firstKey(), context.original().get(context.firstKey()),
(oldValue, value) -> { throw new Error(); });
} catch (Error e) {}
assertThat(context, both(hasMissCount(0)).and(hasHitCount(0)));
assertThat(context, both(hasLoadSuccessCount(0)).and(hasLoadFailureCount(1)));
assertThat(map, is(equalTo(context.original())));
}
@CheckNoWriter @CheckNoStats
@Test(dataProvider = "caches")
@CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING })
public void merge_absent(Map<Integer, Integer> map, CacheContext context) {
Integer result = map.merge(context.absentKey(),
context.absentValue(), (oldValue, value) -> value);
assertThat(result, is(context.absentValue()));
assertThat(map.get(context.absentKey()), is(context.absentValue()));
assertThat(map.size(), is(1 + context.original().size()));
}
/**
* @param field the field to use for indexing and searching against
* @param searchable set to true if searches against "field" should succeed, false if field is only stored and searches should always get numFound=0
*/
private void doTestFloatPointFieldExactQuery(final String field, final boolean searchable, final boolean testDouble)
throws Exception {
int numValues = 10 * RANDOM_MULTIPLIER;
Map<String,Integer> randCount = new HashMap<>(numValues);
String[] rand = testDouble ? toStringArray(getRandomDoubles(numValues, false))
: toStringArray(getRandomFloats(numValues, false));
for (int i = 0 ; i < numValues ; i++) {
randCount.merge(rand[i], 1, (a, b) -> a + b); // count unique values
assertU(adoc("id", String.valueOf(i), field, rand[i]));
}
assertU(commit());
for (int i = 0 ; i < numValues ; i++) {
assertQ(req("q", field + ":" + (rand[i].startsWith("-") ? "\\" : "") + rand[i],
"fl", "id," + field), getTestString(searchable, randCount.get(rand[i])));
}
StringBuilder builder = new StringBuilder();
for (String value : randCount.keySet()) {
if (builder.length() != 0) {
builder.append(" OR ");
}
if (value.startsWith("-")) {
builder.append("\\"); // escape negative sign
}
builder.append(value);
}
assertQ(req("debug", "true", "q", field + ":(" + builder.toString() + ")"), getTestString(searchable, numValues));
clearIndex();
assertU(commit());
}
@Test(dataProvider = "hashMapsWithObjects")
void testMergeNonNull(String desc, Supplier<Map<IntKey, IntKey>> ms, IntKey val) {
// remove a third of the keys
// call merge() for all keys[]
// all keys should be present: removed keys now -> EXTRA, other keys -> k-1
Map<IntKey, IntKey> map = ms.get();
IntKey[] keys = map.keySet().stream().sorted().toArray(IntKey[]::new);
// Map to preceding key
BiFunction<IntKey, IntKey, IntKey> mappingFunction
= (k, v) -> keys[k.getValue() - 1];
removeThirdKeys(map, keys);
for (int i = 1; i < keys.length; i++) {
IntKey retVal = map.merge(keys[i], val, mappingFunction);
if (i % 3 != 2) { // key present, should be mapped to k-1
assertEquals(retVal, keys[i - 1],
String.format("compute: retVal(%s[%d])", desc, i));
assertEquals(keys[i - 1], map.get(keys[i]),
String.format("compute: get(%s[%d])", desc, i));
} else { // odd: was removed, should be replaced with EXTRA
assertEquals(retVal, val,
String.format("compute: retVal(%s[%d])", desc, i));
assertEquals(val, map.get(keys[i]),
String.format("compute: get(%s[%d])", desc, i));
}
assertTrue(map.containsKey(keys[i]),
String.format("compute: containsKey(%s[%d])", desc, i));
}
assertEquals(map.size(), keys.length,
String.format("map expected size#1 m%d != k%d", map.size(), keys.length));
assertTrue(map.containsValue(val),
String.format("compute: containsValue(%s[%s])", desc, val));
assertFalse(map.containsValue(null),
String.format("compute: !containsValue(%s,[null])", desc));
}
private Callback callback(int superstep, K readOnlyKey, K vertex, List<Message> messages) {
return (metadata, error) -> {
if (error == null) {
try {
// Activate partition for next step
int p = vertexToPartition(vertex, serialized.keySerde().serializer(), numPartitions);
log.debug("Step {}, adding partition {} for vertex {}", superstep, p, vertex);
ZKUtils.addChild(curator, applicationId, new PregelState(State.RUNNING, superstep + 1, Stage.SEND), childPath(p));
Map<Integer, Long> endOffsets = lastWrittenOffsets.computeIfAbsent(superstep, k -> new ConcurrentHashMap<>());
endOffsets.merge(metadata.partition(), metadata.offset(), Math::max);
} catch (Exception e) {
throw toRuntimeException(e);
}
} else if (error instanceof RecordTooLargeException && messages.size() > 1) {
log.warn("Record too large, retrying with smaller messages");
for (Message message : messages) {
List<Message> singleton = Collections.singletonList(message);
Tuple3<Integer, K, List<Message>> tuple = new Tuple3<>(superstep + 1, readOnlyKey, singleton);
ProducerRecord<K, Tuple3<Integer, K, List<Message>>> record =
new ProducerRecord<>(workSetTopic, vertex, tuple);
producer.send(record, callback(superstep, readOnlyKey, vertex, singleton));
}
} else {
log.error("Failed to send record to {}: {}", workSetTopic, error);
}
};
}
@CheckNoWriter @CheckNoStats
@CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING })
@Test(dataProvider = "caches", expectedExceptions = NullPointerException.class)
public void merge_nullMappingFunction(Map<Integer, Integer> map, CacheContext context) {
map.merge(1, 1, null);
}
@Nullable
private static Boolean compareTextContainsAllWithin(@Nonnull Iterator<? extends CharSequence> valueIterator, @Nonnull List<String> comparand, int maxDistance) {
final Set<String> comparandSet = getComparandSet(comparand);
if (comparandSet.isEmpty()) {
return null;
}
if (comparandSet.size() == 1) {
return compareTextContainsSingle(valueIterator, comparandSet.iterator().next());
}
// Maintain a queue of the last maxDistance tokens. Then keep a histogram
// of the number of times we've seen each token we care about in that
// range. Then we know we've seen all of them when the size of the
// map is equal to the size of the set.
final Map<String, Integer> seenMap = new HashMap<>(comparandSet.size());
final Queue<String> lastTokensQueue = new ArrayDeque<>(maxDistance);
while (valueIterator.hasNext()) {
final String nextToken = valueIterator.next().toString();
if (!nextToken.isEmpty() && comparandSet.contains(nextToken)) {
// We have a match. Add one (or set the count to 1) for the
// matched token.
seenMap.merge(nextToken, 1, Integer::sum);
if (seenMap.size() == comparandSet.size()) {
return Boolean.TRUE;
}
}
// Update the histogram and the queue, removing the old
// queue entry and then adding this next token if we
// have hit the end of the road.
if (lastTokensQueue.size() == maxDistance) {
final String lastToken = lastTokensQueue.poll();
seenMap.computeIfPresent(lastToken, (ignore, currentCount) -> {
if (currentCount > 1) {
return currentCount - 1;
} else {
// Gone to zero. Remove from map.
return null;
}
});
}
lastTokensQueue.offer(nextToken);
}
return Boolean.FALSE;
}
@Test(dataProvider = "MergeCases")
private void testMerge(String description, Map<IntegerEnum, String> map, Merging.Value oldValue, Merging.Value newValue, Merging.Merger merger, Merging.Value put, Merging.Value result) {
// add and check initial conditions.
switch(oldValue) {
case ABSENT :
map.remove(EXTRA_KEY);
assertFalse(map.containsKey(EXTRA_KEY), "key not absent");
break;
case NULL :
map.put(EXTRA_KEY, null);
assertTrue(map.containsKey(EXTRA_KEY), "key absent");
assertNull(map.get(EXTRA_KEY), "wrong value");
break;
case OLDVALUE :
map.put(EXTRA_KEY, VALUES[1]);
assertTrue(map.containsKey(EXTRA_KEY), "key absent");
assertSame(map.get(EXTRA_KEY), VALUES[1], "wrong value");
break;
default:
fail("unexpected old value");
}
String returned = map.merge(EXTRA_KEY,
newValue == Merging.Value.NULL ? (String) null : VALUES[2],
merger
);
// check result
switch(result) {
case NULL :
assertNull(returned, "wrong value");
break;
case NEWVALUE :
assertSame(returned, VALUES[2], "wrong value");
break;
case RESULT :
assertSame(returned, VALUES[3], "wrong value");
break;
default:
fail("unexpected new value");
}
// check map
switch(put) {
case ABSENT :
assertFalse(map.containsKey(EXTRA_KEY), "key not absent");
break;
case NULL :
assertTrue(map.containsKey(EXTRA_KEY), "key absent");
assertNull(map.get(EXTRA_KEY), "wrong value");
break;
case NEWVALUE :
assertTrue(map.containsKey(EXTRA_KEY), "key absent");
assertSame(map.get(EXTRA_KEY), VALUES[2], "wrong value");
break;
case RESULT :
assertTrue(map.containsKey(EXTRA_KEY), "key absent");
assertSame(map.get(EXTRA_KEY), VALUES[3], "wrong value");
break;
default:
fail("unexpected new value");
}
}
private static void printDistinctCharsWithCount(String input) {
Map<Character, Integer> charsWithCountMap = new HashMap<>();
// using Map merge method from Java 8
for (char c : input.toCharArray())
charsWithCountMap.merge(c, 1, Integer::sum);
System.out.println(charsWithCountMap);
// another way using latest Java enhancements and no for loop, a bit complex though
List<Character> list = input.chars().mapToObj(c -> (char) c).collect(Collectors.toList());
list.stream().forEach(c -> charsWithCountMap.merge(c, 1, Integer::sum));
System.out.println(charsWithCountMap);
}
@Test(dataProvider = "MergeCases")
private void testMerge(String description, Map<IntegerEnum, String> map, Merging.Value oldValue, Merging.Value newValue, Merging.Merger merger, Merging.Value put, Merging.Value result) {
// add and check initial conditions.
switch(oldValue) {
case ABSENT :
map.remove(EXTRA_KEY);
assertFalse(map.containsKey(EXTRA_KEY), "key not absent");
break;
case NULL :
map.put(EXTRA_KEY, null);
assertTrue(map.containsKey(EXTRA_KEY), "key absent");
assertNull(map.get(EXTRA_KEY), "wrong value");
break;
case OLDVALUE :
map.put(EXTRA_KEY, VALUES[1]);
assertTrue(map.containsKey(EXTRA_KEY), "key absent");
assertSame(map.get(EXTRA_KEY), VALUES[1], "wrong value");
break;
default:
fail("unexpected old value");
}
String returned = map.merge(EXTRA_KEY,
newValue == Merging.Value.NULL ? (String) null : VALUES[2],
merger
);
// check result
switch(result) {
case NULL :
assertNull(returned, "wrong value");
break;
case NEWVALUE :
assertSame(returned, VALUES[2], "wrong value");
break;
case RESULT :
assertSame(returned, VALUES[3], "wrong value");
break;
default:
fail("unexpected new value");
}
// check map
switch(put) {
case ABSENT :
assertFalse(map.containsKey(EXTRA_KEY), "key not absent");
break;
case NULL :
assertTrue(map.containsKey(EXTRA_KEY), "key absent");
assertNull(map.get(EXTRA_KEY), "wrong value");
break;
case NEWVALUE :
assertTrue(map.containsKey(EXTRA_KEY), "key absent");
assertSame(map.get(EXTRA_KEY), VALUES[2], "wrong value");
break;
case RESULT :
assertTrue(map.containsKey(EXTRA_KEY), "key absent");
assertSame(map.get(EXTRA_KEY), VALUES[3], "wrong value");
break;
default:
fail("unexpected new value");
}
}
/**
* Asserts that the given map is checked (rejects keys/values of type Object).
*
* @param map a checked Map that contains the entry (presentKey, preventValue), does not
* contain key absentKey or value absentValue, and rejects keys/types of type Object.
*/
private static<K,V> void check_map_isChecked(Map map,
K presentKey, V presentValue, K absentKey, V absentValue) {
Map copyOfMap = new HashMap(map);
assertEquals(map.get(presentKey), presentValue);
assertFalse(map.containsKey(absentKey));
assertFalse(map.values().contains(absentValue));
assertThrowsCce(() -> { map.replaceAll((k, v) -> new Object()); });
assertThrowsCce(() -> { map.putIfAbsent(presentKey, new Object()); });
assertThrowsCce(() -> { map.putIfAbsent(absentKey, new Object()); });
assertThrowsCce(() -> { map.putIfAbsent(new Object(), presentValue); });
assertThrowsCce(() -> { map.remove(new Object()); });
assertThrowsCce(() -> { map.replace(new Object(), presentValue); });
assertThrowsCce(() -> { map.replace(presentKey, new Object()); });
assertThrowsCce(() -> { map.replace(new Object(), presentValue, absentValue); });
// doesn't throw, but has no effect since oldValue doesn't match
assertFalse(map.replace(presentKey, new Object(), absentValue));
assertThrowsCce(() -> { map.replace(presentKey, presentValue, new Object()); });
assertThrowsCce(() -> { map.computeIfAbsent(new Object(), k -> presentValue); });
// doesn't throw, but has no effect since presentKey is present
assertEquals(presentValue, map.computeIfAbsent(presentKey, k -> new Object()));
assertThrowsCce(() -> { map.computeIfAbsent(absentKey, k -> new Object()); });
assertThrowsCce(() -> { map.computeIfPresent(new Object(), (k, v) -> presentValue); });
assertThrowsCce(() -> { map.computeIfPresent(presentKey, (k, v) -> new Object()); });
// doesn't throw, but has no effect since absentKey is absent
assertNull(map.computeIfPresent(absentKey, (k, v) -> new Object()));
assertThrowsCce(() -> { map.compute(new Object(), (k, v) -> presentValue); });
assertThrowsCce(() -> { map.compute(presentKey, (k, v) -> new Object()); });
assertThrowsCce(() -> { map.compute(absentKey, (k, v) -> new Object()); });
assertThrowsCce(() -> { map.merge(new Object(), presentValue, (v1, v2) -> presentValue); });
assertThrowsCce(() -> { map.merge(presentKey, presentValue, (v1, v2) -> new Object()); });
// doesn't throw, puts (absentKey, absentValue) into the map
map.merge(absentKey, absentValue, (v1, v2) -> new Object());
assertEquals(absentValue, map.remove(absentKey)); // restore previous state
assertThrowsCce(() -> { map.put(new Object(), absentValue); });
assertThrowsCce(() -> { map.put(absentKey, new Object()); });
assertThrowsCce(() -> { map.put(new Object(), presentValue); });
assertThrowsCce(() -> { map.put(presentKey, new Object()); });
assertEquals("map should be unchanged", copyOfMap, map);
}
@Test(dataProvider = "MergeCases")
private void testMerge(String description, Map<IntegerEnum, String> map, Merging.Value oldValue, Merging.Value newValue, Merging.Merger merger, Merging.Value put, Merging.Value result) {
// add and check initial conditions.
switch(oldValue) {
case ABSENT :
map.remove(EXTRA_KEY);
assertFalse(map.containsKey(EXTRA_KEY), "key not absent");
break;
case NULL :
map.put(EXTRA_KEY, null);
assertTrue(map.containsKey(EXTRA_KEY), "key absent");
assertNull(map.get(EXTRA_KEY), "wrong value");
break;
case OLDVALUE :
map.put(EXTRA_KEY, VALUES[1]);
assertTrue(map.containsKey(EXTRA_KEY), "key absent");
assertSame(map.get(EXTRA_KEY), VALUES[1], "wrong value");
break;
default:
fail("unexpected old value");
}
String returned = map.merge(EXTRA_KEY,
newValue == Merging.Value.NULL ? (String) null : VALUES[2],
merger
);
// check result
switch(result) {
case NULL :
assertNull(returned, "wrong value");
break;
case NEWVALUE :
assertSame(returned, VALUES[2], "wrong value");
break;
case RESULT :
assertSame(returned, VALUES[3], "wrong value");
break;
default:
fail("unexpected new value");
}
// check map
switch(put) {
case ABSENT :
assertFalse(map.containsKey(EXTRA_KEY), "key not absent");
break;
case NULL :
assertTrue(map.containsKey(EXTRA_KEY), "key absent");
assertNull(map.get(EXTRA_KEY), "wrong value");
break;
case NEWVALUE :
assertTrue(map.containsKey(EXTRA_KEY), "key absent");
assertSame(map.get(EXTRA_KEY), VALUES[2], "wrong value");
break;
case RESULT :
assertTrue(map.containsKey(EXTRA_KEY), "key absent");
assertSame(map.get(EXTRA_KEY), VALUES[3], "wrong value");
break;
default:
fail("unexpected new value");
}
}
private void updateHook(Map<String, List<String>> alreadyExecutedHooks, List<HookPhase> currentHookPhasesForExecution, Hook hook) {
List<String> hookPhasesBasedOnCurrentHookPhase = getHookPhasesBasedOnCurrentHookPhase(currentHookPhasesForExecution,
hook.getPhases());
alreadyExecutedHooks.merge(hook.getName(), hookPhasesBasedOnCurrentHookPhase, ListUtils::union);
}