[英]Multiple entries with same key immutable map error
I have a below builder class which I am using from multithread application so I have made it thread safe. 我在多线程应用程序中使用了下面的构建器类,因此使它成为线程安全的。 Just for simplicity, I am showing only few fields here to demonstrate the problem. 为了简单起见,我仅在此处显示几个字段来演示该问题。
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
}
Below is how I make ClientKey
and it works fine. 下面是我制作ClientKey
,它工作正常。
Map<String, String> testMap = new HashMap<String, String>();
testMap.put("hello", "world");
ClientKey keys = new ClientKey.Builder(12345L, 200).parameterMap(testMap).build();
Now when I try to clone the keys
object as shown below, it throws exception. 现在,当我尝试克隆如下所示的keys
对象时,它将引发异常。
ClientKey clonedKey = new ClientKey.Builder(keys).processName("hello").build();
It throws exception with error message as: java.lang.IllegalArgumentException: Multiple entries with same key: is_clientid=true and is_clientid=true
它将引发异常,并带有以下错误消息: 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();
How can I fix this problem? 我该如何解决这个问题? I want to make my map immutable but I also want to initialize with required fields as well and that I can only do it in the constructor of ClientKey
class. 我想使地图不可变,但我也想用必填字段进行初始化,而且只能在ClientKey
类的构造函数中进行ClientKey
。 And it throws exception while cloning the ClientKey
object. 并且在克隆ClientKey
对象时会引发异常。
When you construct a ClientKey
, the "is_clientid"
key is put in the map. 构造ClientKey
, ClientKey
"is_clientid"
键放入地图中。 Therefore, if you call your ClientKey.Builder(ClientKey)
constructor the putAll
call will copy it to your new ImmutableMap.Builder
instance. 因此,如果调用ClientKey.Builder(ClientKey)
构造函数,则putAll
调用会将其复制到新的ImmutableMap.Builder
实例。 When you then build your cloned ClientKey
, the ClientKey
constructor will again try to add the same key to the map, which causes the exception. 然后,当您构建克隆的ClientKey
, ClientKey
构造函数将再次尝试将相同的密钥添加到映射,这将导致异常。
The ImmutableMap.Builder
could have been written in a different way, but it wasn't. ImmutableMap.Builder
可以用不同的方式编写,但事实并非如此。 If you want to use it, you'll have to live with it. 如果要使用它,则必须使用它。
One solution is to not copy the entry with the "is_clientid"
key to the new ImmutableMap.Builder
in the constructor of your Builder
. 一种解决方案是不使用"is_clientid"
键将条目复制到Builder
的构造函数中的新ImmutableMap.Builder
。 Instead of this.parameterMap = ImmutableMap.<String, String>builder().putAll(key.parameterMap);
代替this.parameterMap = ImmutableMap.<String, String>builder().putAll(key.parameterMap);
you write: 你写:
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());
}
}
Another solution is to not use Guava's ImmutableMap.Builder
, but a normal Java HashMap
(it does not throw exception when you try to put a duplicate key in it, the old entry is simply overwritten). 另一个解决方案是不使用Guava的ImmutableMap.Builder
,而是使用普通的Java HashMap
(当您尝试在其中放置重复键时它不会引发异常,只是简单地覆盖了旧条目)。 Then in your ClientKey
constructor you write: 然后在ClientKey
构造函数中编写:
this.parameterMap = Collections.unmodifiableMap(builder.parameterMap);
You could also write: 您还可以编写:
this.parameterMap = ImmutableMap.copyOf(builder.parameterMap);
but this makes an entire copy of the map, which may take some time for very large maps. 但这会制作完整的地图副本,对于非常大的地图可能要花一些时间。
A concluding remark: if all you want to do is copy a ClientKey
, you do not need a builder; 结束语:如果您只想复制ClientKey
,则不需要构建器; idiomatic Java would use a copy constructor or the clone()
method (although the latter is discouraged by some). 惯用的Java将使用复制构造函数或clone()
方法(尽管有些人不建议使用后者)。
You are getting an exception because you're trying to set a value for the key is_clientid
in the same ImmutableMap.Builder
used by your single ClientKey.Builder
class: 因为要在单个ClientKey.Builder
类使用的同一ImmutableMap.Builder
尝试为键is_clientid
设置值, is_clientid
出现了ClientKey.Builder
:
builder.parameterMap.put("is_clientid", (clientId == 0) ? "false" : "true");
As seen in the documentation : 如文档所示 :
Associates key with value in the built map. 将键与已构建映射中的值相关联。 Duplicate keys are not allowed, and will cause
build()
to fail. 不允许使用重复的密钥,这将导致build()
失败。
Don't re-use the same instance of ImmutableMap.Builder
. 不要重复使用ImmutableMap.Builder
的相同实例。
You can clone an object sort of like this instead: 您可以改为克隆这样的对象:
public ClientKey(ClientKey copyee) {
// Copy fields here
this.parameterMap = ImmutableMap.copyOf(copyee.parameterMap);
}
If you want to use some sort of builder object, you could do something like this: 如果要使用某种构建器对象,则可以执行以下操作:
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;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.