具有相同键的多个条目不可变映射错误
Multiple entries with same key immutable map error
我有一个下面的构建器 class,我在多线程应用程序中使用它,所以我使它成为线程安全的。为了简单起见,我在这里只显示几个字段来演示问题。
public final class ClientKey {
private final long userId;
private final int clientId;
private final String processName;
private final Map<String, String> parameterMap;
private ClientKey(Builder builder) {
this.userId = builder.userId;
this.clientId = builder.clientId;
this.processName = builder.processName;
// initializing the required fields
// and below line throws exception once I try to clone the `ClientKey` object
builder.parameterMap.put("is_clientid", (clientId == 0) ? "false" : "true");
this.parameterMap = builder.parameterMap.build();
}
public static class Builder {
private final long userId;
private final int clientId;
private String processName;
private ImmutableMap.Builder<String, String> parameterMap = ImmutableMap.builder();
// this is for cloning
public Builder(ClientKey key) {
this.userId = key.userId;
this.clientId = key.clientId;
this.processName = key.processName;
this.parameterMap =
ImmutableMap.<String, String>builder().putAll(key.parameterMap);
}
public Builder(long userId, int clientId) {
this.userId = userId;
this.clientId = clientId;
}
public Builder parameterMap(Map<String, String> parameterMap) {
this.parameterMap.putAll(parameterMap);
return this;
}
public Builder processName(String processName) {
this.processName = processName;
return this;
}
public ClientKey build() {
return new ClientKey(this);
}
}
// getters
}
下面是我制作 ClientKey
的方法,效果很好。
Map<String, String> testMap = new HashMap<String, String>();
testMap.put("hello", "world");
ClientKey keys = new ClientKey.Builder(12345L, 200).parameterMap(testMap).build();
现在,当我尝试如下所示克隆 keys
对象时,它抛出异常。
ClientKey clonedKey = new ClientKey.Builder(keys).processName("hello").build();
它抛出异常,错误消息为:java.lang.IllegalArgumentException: Multiple entries with same key: is_clientid=true and is_clientid=true
builder.parameterMap.put("is_clientid", (clientId == 0) ? "false" : "true");
// from below line exception is coming
this.parameterMap = builder.parameterMap.build();
我该如何解决这个问题?我想让我的地图不可变,但我也想用必填字段进行初始化,而且我只能在 ClientKey
class 的构造函数中进行初始化。它在克隆 ClientKey
对象时抛出异常。
您遇到异常是因为您试图在您的单曲 ClientKey.Builder
class 使用的相同 ImmutableMap.Builder
中为键 is_clientid
设置一个值:
builder.parameterMap.put("is_clientid", (clientId == 0) ? "false" : "true");
如 the documentation 所示:
Associates key with value in the built map. Duplicate keys are not allowed, and will cause build()
to fail.
不要重复使用 ImmutableMap.Builder
.
的同一个实例
您可以像这样克隆一个对象:
public ClientKey(ClientKey copyee) {
// Copy fields here
this.parameterMap = ImmutableMap.copyOf(copyee.parameterMap);
}
如果你想使用某种构建器对象,你可以这样做:
public Builder(ClientKey copyee) {
this.oldParameterMap = copyee.parameterMap;
}
public ClientKey build() {
// Create new map here and pass it to new ClientKey somehow
ImmutableMap.copyOf(oldParameterMap);
return newKey;
}
当您构建 ClientKey
时,"is_clientid"
键将放入映射中。因此,如果您调用 ClientKey.Builder(ClientKey)
构造函数,putAll
调用会将其复制到新的 ImmutableMap.Builder
实例。当您随后构建克隆的 ClientKey
时,ClientKey
构造函数将再次尝试将相同的键添加到映射,这会导致异常。
ImmutableMap.Builder
本来可以用不同的方式写的,但事实并非如此。如果你想使用它,你将不得不忍受它。
一个解决方案是不将带有 "is_clientid"
键的条目复制到 Builder
的构造函数中的新 ImmutableMap.Builder
。而不是 this.parameterMap = ImmutableMap.<String, String>builder().putAll(key.parameterMap);
你写:
this.parameterMap = new ImmutableMap.Builder<>();
for (Map.Entry<String,String> entry : key.parameterMap.entrySet()) {
if (!"is_clientid".equals(entry.getKey()) {
this.parameterMap.put(entry.getKey(), entry.getValue());
}
}
另一种解决方案是不使用 Guava 的 ImmutableMap.Builder
,而是使用普通的 Java HashMap
(当您尝试将重复键放入其中时,它不会抛出异常,旧的输入只是被覆盖)。然后在你的 ClientKey
构造函数中你写:
this.parameterMap = Collections.unmodifiableMap(builder.parameterMap);
你也可以这样写:
this.parameterMap = ImmutableMap.copyOf(builder.parameterMap);
但这会制作地图的完整副本,对于非常大的地图,这可能需要一些时间。
结束语:如果您只想复制一个ClientKey
,则不需要生成器;惯用的 Java 将使用复制构造函数或 clone()
方法(尽管有些人不鼓励使用后者)。
我有一个下面的构建器 class,我在多线程应用程序中使用它,所以我使它成为线程安全的。为了简单起见,我在这里只显示几个字段来演示问题。
public final class ClientKey {
private final long userId;
private final int clientId;
private final String processName;
private final Map<String, String> parameterMap;
private ClientKey(Builder builder) {
this.userId = builder.userId;
this.clientId = builder.clientId;
this.processName = builder.processName;
// initializing the required fields
// and below line throws exception once I try to clone the `ClientKey` object
builder.parameterMap.put("is_clientid", (clientId == 0) ? "false" : "true");
this.parameterMap = builder.parameterMap.build();
}
public static class Builder {
private final long userId;
private final int clientId;
private String processName;
private ImmutableMap.Builder<String, String> parameterMap = ImmutableMap.builder();
// this is for cloning
public Builder(ClientKey key) {
this.userId = key.userId;
this.clientId = key.clientId;
this.processName = key.processName;
this.parameterMap =
ImmutableMap.<String, String>builder().putAll(key.parameterMap);
}
public Builder(long userId, int clientId) {
this.userId = userId;
this.clientId = clientId;
}
public Builder parameterMap(Map<String, String> parameterMap) {
this.parameterMap.putAll(parameterMap);
return this;
}
public Builder processName(String processName) {
this.processName = processName;
return this;
}
public ClientKey build() {
return new ClientKey(this);
}
}
// getters
}
下面是我制作 ClientKey
的方法,效果很好。
Map<String, String> testMap = new HashMap<String, String>();
testMap.put("hello", "world");
ClientKey keys = new ClientKey.Builder(12345L, 200).parameterMap(testMap).build();
现在,当我尝试如下所示克隆 keys
对象时,它抛出异常。
ClientKey clonedKey = new ClientKey.Builder(keys).processName("hello").build();
它抛出异常,错误消息为:java.lang.IllegalArgumentException: Multiple entries with same key: is_clientid=true and is_clientid=true
builder.parameterMap.put("is_clientid", (clientId == 0) ? "false" : "true");
// from below line exception is coming
this.parameterMap = builder.parameterMap.build();
我该如何解决这个问题?我想让我的地图不可变,但我也想用必填字段进行初始化,而且我只能在 ClientKey
class 的构造函数中进行初始化。它在克隆 ClientKey
对象时抛出异常。
您遇到异常是因为您试图在您的单曲 ClientKey.Builder
class 使用的相同 ImmutableMap.Builder
中为键 is_clientid
设置一个值:
builder.parameterMap.put("is_clientid", (clientId == 0) ? "false" : "true");
如 the documentation 所示:
Associates key with value in the built map. Duplicate keys are not allowed, and will cause
build()
to fail.
不要重复使用 ImmutableMap.Builder
.
您可以像这样克隆一个对象:
public ClientKey(ClientKey copyee) {
// Copy fields here
this.parameterMap = ImmutableMap.copyOf(copyee.parameterMap);
}
如果你想使用某种构建器对象,你可以这样做:
public Builder(ClientKey copyee) {
this.oldParameterMap = copyee.parameterMap;
}
public ClientKey build() {
// Create new map here and pass it to new ClientKey somehow
ImmutableMap.copyOf(oldParameterMap);
return newKey;
}
当您构建 ClientKey
时,"is_clientid"
键将放入映射中。因此,如果您调用 ClientKey.Builder(ClientKey)
构造函数,putAll
调用会将其复制到新的 ImmutableMap.Builder
实例。当您随后构建克隆的 ClientKey
时,ClientKey
构造函数将再次尝试将相同的键添加到映射,这会导致异常。
ImmutableMap.Builder
本来可以用不同的方式写的,但事实并非如此。如果你想使用它,你将不得不忍受它。
一个解决方案是不将带有 "is_clientid"
键的条目复制到 Builder
的构造函数中的新 ImmutableMap.Builder
。而不是 this.parameterMap = ImmutableMap.<String, String>builder().putAll(key.parameterMap);
你写:
this.parameterMap = new ImmutableMap.Builder<>();
for (Map.Entry<String,String> entry : key.parameterMap.entrySet()) {
if (!"is_clientid".equals(entry.getKey()) {
this.parameterMap.put(entry.getKey(), entry.getValue());
}
}
另一种解决方案是不使用 Guava 的 ImmutableMap.Builder
,而是使用普通的 Java HashMap
(当您尝试将重复键放入其中时,它不会抛出异常,旧的输入只是被覆盖)。然后在你的 ClientKey
构造函数中你写:
this.parameterMap = Collections.unmodifiableMap(builder.parameterMap);
你也可以这样写:
this.parameterMap = ImmutableMap.copyOf(builder.parameterMap);
但这会制作地图的完整副本,对于非常大的地图,这可能需要一些时间。
结束语:如果您只想复制一个ClientKey
,则不需要生成器;惯用的 Java 将使用复制构造函数或 clone()
方法(尽管有些人不鼓励使用后者)。