Java 从数组中收集键值以进行映射,无重复项
我的应用程序从web服务获取一些字符串。看起来是这样的:Java 从数组中收集键值以进行映射,无重复项,java,Java,我的应用程序从web服务获取一些字符串。看起来是这样的: name=Raul&city=Paris&id=167136 name=Raul&city=Paris&id=167136&city=Oslo 我想从以下字符串获取地图: {name=Raul, city=Paris, id=167136} 代码: 这在大多数情况下都可以正常工作,但应用程序可以获得具有重复键的字符串,如下所示: name=Raul&city=Paris&i
name=Raul&city=Paris&id=167136
name=Raul&city=Paris&id=167136&city=Oslo
我想从以下字符串获取地图:
{name=Raul, city=Paris, id=167136}
代码:
这在大多数情况下都可以正常工作,但应用程序可以获得具有重复键的字符串,如下所示:
name=Raul&city=Paris&id=167136
name=Raul&city=Paris&id=167136&city=Oslo
应用程序将崩溃,出现以下未捕获的异常:
Exception in thread "main" java.lang.IllegalStateException: Duplicate key city (attempted merging values Paris and Oslo)
我尝试更改收集方法:
但编者说没有:
Cannot resolve method 'collect(java.util.stream.Collector<T,capture<?>,java.util.Map<K,U>>, <lambda expression>)'
无法解析方法“collect(java.util.stream.Collector)”您误解了toMap
(合并运算符)的最后一个参数。当它找到一个重复的键时,它会将映射中的当前值和具有相同键的新值交给合并运算符,该运算符生成要存储的单个值
例如,如果您只想存储找到的第一个值,然后使用(s1,s2)->s1
。如果您想用逗号分隔它们,请使用(s1,s2)->s1+,“+s2
,使用kotlin集合可以获得相同的结果
val res = message
.split("&")
.map {
val entry = it.split("=")
Pair(entry[0], entry[1])
}
println(res)
println(res.toMap()) //distinct by key
结果是
[(姓名,劳尔),(巴黎市),(id,167136),(奥斯陆市)]
{name=Raul,city=Oslo,id=167136}如果您想将重复键的值添加到一起并按键对它们进行分组(因为应用程序可以获得包含重复键的字符串),而不是使用collector.toMap()
您可以将收集器.groupingBy
与自定义收集器(collector.of(…)
):
为什么不使用库来解析查询字符串呢?手工解析是一个非常糟糕的主意,因为有上百种情况下,您的代码会丢失!例如,标志(没有值的属性)呢或者URL编码的数据?在特定情况下,只需为收集器使用自定义合并策略即可。TL;DR:不,在大多数情况下,它不起作用。正在修复此特定解析问题,但会留下大量其他问题。解析查询的库是什么?你能提供一个好的示例吗?这是否回答了你的问题?(请注意,逗号分隔建议会受到字符串concat in-loop反模式的影响),而字符串concat in-loop反模式会受到置换优化反模式的影响:-)在这一点上我不同意。我认为引用经常被误用——我确信这里就是这样。特别是因为有一个加入收集器专门解决这个常见问题。我同意在这种情况下,使用一个groupingBy
和一个下游加入收集器将更有意义。但是我不同意string-concat-in-loop通常是一种反模式。多年来,我看到很多愚蠢的代码使用StringBuilder
来提高理论性能,但没有证据表明在这种特定情况下需要它——但有很多证据表明它使代码的可读性大大降低。很明显,我支持你,这是正确的答案。我们在争论抽象理论——我完全同意人们可以用代码做愚蠢的事情,不管工具有多大的帮助。这个问题被标记为Java。为什么不使用带有重复键函数的映射收集器?其目的不是更明显吗?@Boristespider我更喜欢Set
,而不是c一个字符串,如s1+,”+s2
,毕竟这取决于问题作者的需要!是的,但在任何情况下都不需要您建议的自定义收集器。作者特别要求只取一个值-这可以通过自定义合并函数轻松解决。我没有说这是必需的,或者这是唯一的解决方案,它可以为其他人服务r愿意有这种行为而不是有单一价值观的人
String input = "name=Raul&city=Paris&city=Berlin&id=167136&id=03&id=505";
Map<String, Set<Object>> result = Arrays.stream(input.split("&"))
.map(splitedString -> splitedString.split("="))
.filter(keyValuePair -> keyValuePair.length() == 2)
.collect(
Collectors.groupingBy(array -> array[0], Collector.of(
() -> new HashSet<>(), (set, array) -> set.add(array[1]),
(left, right) -> {
if (left.size() < right.size()) {
right.addAll(left);
return right;
} else {
left.addAll(right);
return left;
}
}, Collector.Characteristics.UNORDERED)
)
);
result => size = 3
"city" -> size = 2 ["Berlin", "Paris"]
"name" -> size = 1 ["Raul"]
"id" -> size = 3 ["167136","03","505"]