Skip to content

Commit d48a10b

Browse files
committed
Add allowDuplicateKeys to ImmutableMap Builder
1 parent a802e31 commit d48a10b

File tree

6 files changed

+56
-11
lines changed

6 files changed

+56
-11
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"type": "bugfix",
2+
"type": "feature",
33
"category": "AWS SDK for Java v2",
44
"contributor": "",
5-
"description": "Log error instead of throwing error for duplicate keys in ImmutableMap"
5+
"description": "Add allowDuplicateKeys to ImmutableMap Builder"
66
}

codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/ServiceMetadataGenerator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ private CodeBlock dnsSuffixesByPartition(Partitions partitions) {
257257
private CodeBlock hostnamesByRegion(Partitions partitions) {
258258
Map<Partition, Service> services = getServiceData(partitions);
259259

260-
CodeBlock.Builder builder = CodeBlock.builder().add("$T.<$T, $T>builder()",
260+
CodeBlock.Builder builder = CodeBlock.builder().add("$T.<$T, $T>builder().allowDuplicateKeys(true)",
261261
ImmutableMap.class, serviceEndpointKeyClass(), String.class);
262262

263263
services.forEach((partition, service) -> {

codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/s3-service-metadata.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public final class S3ServiceMetadata implements ServiceMetadata {
6969

7070
private static final Map<ServiceEndpointKey, String> HOSTNAMES_BY_REGION = ImmutableMap
7171
.<ServiceEndpointKey, String> builder()
72+
.allowDuplicateKeys(true)
7273
.put(ServiceEndpointKey.builder().region(Region.of("af-south-1")).tags(EndpointTag.of("dualstack")).build(),
7374
"s3.dualstack.af-south-1.amazonaws.com")
7475
.put(ServiceEndpointKey.builder().region(Region.of("ap-east-1")).tags(EndpointTag.of("dualstack")).build(),

codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/sts-service-metadata.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public final class StsServiceMetadata implements ServiceMetadata {
6060

6161
private static final Map<ServiceEndpointKey, String> HOSTNAMES_BY_REGION = ImmutableMap
6262
.<ServiceEndpointKey, String> builder()
63+
.allowDuplicateKeys(true)
6364
.put(ServiceEndpointKey.builder().region(Region.of("aws-global")).build(), "sts.amazonaws.com")
6465
.put(ServiceEndpointKey.builder().region(Region.of("us-east-1")).tags(EndpointTag.of("fips")).build(),
6566
"sts-fips.us-east-1.amazonaws.com")

utils/src/main/java/software/amazon/awssdk/utils/ImmutableMap.java

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public final class ImmutableMap<K, V> implements Map<K, V> {
6060

6161
private static final Logger log = Logger.loggerFor(ImmutableMap.class);
6262
private static final String UNMODIFIABLE_MESSAGE = "This is an immutable map.";
63+
private static final String DUPLICATED_KEY_MESSAGE = "Duplicate keys are provided.";
6364

6465
private final Map<K, V> map;
6566

@@ -198,10 +199,18 @@ public static <K, V> ImmutableMap<K, V> of(K k0, V v0, K k1, V v1,
198199

199200
private static <K, V> void putAndWarnDuplicateKeys(Map<K, V> map, K key,
200201
V value) {
202+
putAndWarnDuplicateKeys(map, key, value, false);
203+
}
204+
205+
private static <K, V> void putAndWarnDuplicateKeys(Map<K, V> map, K key, V value, boolean allowDuplicateKeys) {
201206
if (map.containsKey(key)) {
202-
log.error(() -> String.format("Duplicate keys are provided for [%s]. The first value [%s] will be kept, and the "
203-
+ "second value [%s] will be ignored.", key, map.get(key), value));
204-
return;
207+
208+
if (allowDuplicateKeys) {
209+
log.warn(() -> String.format("Duplicate keys are provided for [%s]. The newer value [%s] will be saved, and the "
210+
+ "existing value [%s] will be overwritten.", key, value, map.get(key)));
211+
} else {
212+
throw new IllegalArgumentException(DUPLICATED_KEY_MESSAGE);
213+
}
205214
}
206215
map.put(key, value);
207216
}
@@ -291,20 +300,38 @@ public String toString() {
291300
public static class Builder<K, V> {
292301

293302
private final Map<K, V> entries;
303+
private boolean allowDuplicateKeys;
294304

295305
public Builder() {
296306
this.entries = new HashMap<>();
297307
}
298308

299309
/**
300-
* Add a key-value pair into the built map. If duplicate keys are provided, the first value will be kept and the second
301-
* value will be ignored, and the SDK will log an error.
310+
* Add a key-value pair into the built map. The method will throw
311+
* IllegalArgumentException immediately when duplicate keys are
312+
* provided and allowDuplicateKeys is false (default value).
313+
*
314+
*
315+
* If duplicate keys are provided and allowDuplicateKeys is true, the latest value will overwrite the existing value, and
316+
* the SDK will log a message at WARN level.
302317
*
303318
* @return Returns a reference to this object so that method calls can
304319
* be chained together.
305320
*/
306321
public Builder<K, V> put(K key, V value) {
307-
putAndWarnDuplicateKeys(entries, key, value);
322+
putAndWarnDuplicateKeys(entries, key, value, allowDuplicateKeys);
323+
return this;
324+
}
325+
326+
/**
327+
* Sets whether duplicate keys are allowed. If true, the latest value will overwrite the existing value. If
328+
* false, an error will be thrown when attempting to put an entry with a duplicate key. Default value is false.
329+
*
330+
* @return Returns a reference to this object so that method calls can
331+
* be chained together.
332+
*/
333+
public Builder<K, V> allowDuplicateKeys(boolean allowDuplicateKeys) {
334+
this.allowDuplicateKeys = allowDuplicateKeys;
308335
return this;
309336
}
310337

utils/src/test/java/software/amazon/awssdk/utils/ImmutableMapTest.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,29 @@ public void testOfBuilder() {
7171
}
7272

7373
@Test
74-
public void putDuplicateKeys_keepsFirstOneAndDoesNotThrowError() {
74+
public void testErrorOnDuplicateKeys() {
75+
try {
76+
Map<Integer, String> builtMap = new ImmutableMap.Builder<Integer, String>()
77+
.put(1, "one")
78+
.put(1, "two")
79+
.build();
80+
fail("IllegalArgumentException expected.");
81+
} catch (IllegalArgumentException iae) {
82+
// Ignored or expected.
83+
} catch (Exception e) {
84+
fail("IllegalArgumentException expected.");
85+
}
86+
}
87+
88+
@Test
89+
public void putDuplicateKeys_allowDuplicateKeysTrue_doesNotThrowErrorAndKeepsNewestValue() {
7590
Map<Integer, String> builtMap = new ImmutableMap.Builder<Integer, String>()
91+
.allowDuplicateKeys(true)
7692
.put(1, "one")
7793
.put(1, "two")
7894
.build();
7995

80-
assertEquals("one", builtMap.get(1));
96+
assertEquals("two", builtMap.get(1));
8197
}
8298

8399
@Test

0 commit comments

Comments
 (0)