Java 具有相同密钥的多个条目不可变映射错误
我在多线程应用程序中使用了一个bellowbuilder类,所以我使它成为线程安全的。为了简单起见,我在这里只展示了几个字段来演示这个问题Java 具有相同密钥的多个条目不可变映射错误,java,multithreading,guava,builder,builder-pattern,Java,Multithreading,Guava,Builder,Builder Pattern,我在多线程应用程序中使用了一个bellowbuilder类,所以我使它成为线程安全的。为了简单起见,我在这里只展示了几个字段来演示这个问题 public final class ClientKey { private final long userId; private final int clientId; private final String processName; private final Map<String, String> parameterMap
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
}
现在,当我尝试克隆如下所示的keys
对象时,它抛出异常
ClientKey clonedKey = new ClientKey.Builder(keys).processName("hello").build();
它抛出异常,错误消息如下:java.lang.IllegalArgumentException:具有相同键的多个条目:is_clientid=true和is_clientid=true
builder.parameterMap.put("is_clientid", (clientId == 0) ? "false" : "true");
// from below line exception is coming
this.parameterMap = builder.parameterMap.build();
我如何解决这个问题?我想使我的映射不可变,但我还想用必填字段初始化,并且我只能在
ClientKey
类的构造函数中进行初始化。克隆ClientKey
对象时,它会引发异常。您会遇到异常,因为您试图在单个ClientKey.Builder
类所使用的ImmutableMap.Builder
中为key设置值
builder.parameterMap.put("is_clientid", (clientId == 0) ? "false" : "true");
如图所示:
将键与生成的映射中的值相关联。不允许使用重复的密钥,这将导致build()
失败
不要重复使用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.Builder使用的ImmutableMap.Builder
类中的密钥is\u clientid
设置一个值:
builder.parameterMap.put("is_clientid", (clientId == 0) ? "false" : "true");
如图所示:
将键与生成的映射中的值相关联。不允许使用重复的密钥,这将导致build()
失败
不要重复使用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\u clientid”
键放入映射中。因此,如果调用ClientKey.Builder(ClientKey)
构造函数,putAll
调用将把它复制到新的ImmutableMap.Builder
实例中。然后,当您构建克隆的ClientKey
时,ClientKey
构造函数将再次尝试向映射添加相同的键,这会导致异常
ClientKey clonedKey = new ClientKey.Builder(keys).processName("hello").build();
ImmutableMap.Builder
本可以用不同的方式编写,但事实并非如此。如果你想使用它,你就必须接受它
一种解决方案是不将带有“is_clientid”
键的条目复制到生成器的构造函数中的新ImmutableMap.Builder
。而不是this.parameterMap=ImmutableMap.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());
}
}
this.parameterMap = Collections.unmodifiableMap(builder.parameterMap);
你也可以写:
this.parameterMap = ImmutableMap.copyOf(builder.parameterMap);
但这会生成地图的完整副本,对于非常大的地图,这可能需要一些时间
最后一句话:如果你只想复制一个ClientKey
,你不需要一个生成器;惯用Java将使用复制构造函数或clone()
方法(尽管有些人不鼓励使用后者)。当您构建ClientKey
时,将“is\u clientid”
键放在映射中。因此,如果调用ClientKey.Builder(ClientKey)
构造函数,putAll
调用将把它复制到新的ImmutableMap.Builder
实例中。然后,当您构建克隆的ClientKey
时,ClientKey
构造函数将再次尝试向映射添加相同的键,这会导致异常
ClientKey clonedKey = new ClientKey.Builder(keys).processName("hello").build();
ImmutableMap.Builder
本可以用不同的方式编写,但事实并非如此。如果你想使用它,你就必须接受它
一种解决方案是不将带有“is_clientid”
键的条目复制到生成器的构造函数中的新ImmutableMap.Builder
。而不是this.parameterMap=ImmutableMap.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());
}
}
this.parameterMap = Collections.unmodifiableMap(builder.parameterMap);
你也可以写:
this.parameterMap = ImmutableMap.copyOf(builder.parameterMap);
但这会生成地图的完整副本,对于非常大的地图,这可能需要一些时间
最后一句话:如果你只想复制一个ClientKey
,你不需要一个生成器;惯用Java会使用复制构造函数或clone()
方法(尽管有些人不鼓励使用后者)。您能澄清错误消息的抛出位置吗?从上面的代码来看,ClientKey clonedKey=new ClientKey.Builder(keys.processName(“hello”).build()似乎并不清楚
将引发异常,因为您甚至没有密钥is\u clientid
。代码中的下一行发生在哪里?您确定这确实是生成器模式的实例吗?--我从未见过有人将“生成器”传递给私有构造函数只是为了设置新对象的字段。@jamesw1234在这一行this.parameterMap=builder.parameterMap.build()抛出异常
但仅当我尝试将keys
对象克隆到一个新的clonedKey
对象时,因为在克隆时,我的ClientKey
构造函数将被再次调用,然后它将尝试再次将相同的is\u clientid
插入映射,这就是它引发异常的原因。在哪里实例化了builder
呢?您能否澄清错误消息是在哪里引发的?从上面的代码来看,ClientKey clonedKey=new ClientKey.Builder(keys.processName(“hello”).build()似乎并不清楚
将引发异常,因为您甚至没有密钥is\u clientid
。代码中的下一行发生在哪里?您确定这确实是生成器模式的实例吗?--我从未见过有人将“生成器”传递给私有构造函数只是为了设置新对象的字段。@jamesw1234在这一行this.parameterMap=builder.parameter抛出异常