我有一张地图Map<String, List<String>>
。如果一个键是另一个键的功能,我想合并键,例如:
如果函数是“前缀”,我想在地图中给出这些值:
{"123", ["a"]]}
{"85", ["a","b"]]}
{"8591", ["c"]}
获取具有这些值的新地图:
{"123", ["a"]}
{"85", ["a","b","c"]}
此映射“减少”是用户请求的一部分,因此必须快速。我知道我可以做O(n^2)
,但是我正在寻找更好的方法,如果可能的话,应该平行进行。
以下是通过调用getMatchingKey
函数为每个键找到超级键的代码:
Map<String, Set<String>> result= new HashMap<>();
for (Map.Entry<String, List<String>> entry : input.entrySet()){
String x = getMatchingKey(entry.getKey(), input.keySet());
if (!resultt.containsKey(x)){
resultt.put(x, new HashSet<String>());
}
resultt.get(x).addAll((input.get(x)));
resultt.get(x).addAll((entry.getValue()));
}
编辑
我遇到的全部问题是这样的:
给定实体名称与其足迹Map<String, Footprint>
的映射,我想从包含在不同实体中的Subnet
中删除Footprint
。
Footprint
对象包含Subent
的列表。
所以我虽然将映射反转为Map<Subnet, List<String>>
,然后将所有子网映射到它们的实体名称,而不是合并所有子网,最后从原始Map中过滤出子网。像这样:
public Map<String, Footprint> clearOverlaps(Map<String, Footprint> footprintsMap) {
Map<Subnet, List<String>> subnetsToGroupNameMap =
footprintsMap.entrySet()
.parallelStream()
.flatMap(e -> e.getValue().getSubnets().stream().map(i -> new AbstractMap.SimpleEntry<>(i, e.getKey())))
.collect(groupingBy(e->e.getKey(), mapping(e->e.getValue(), toList())));
Map<Subnet, Set<String>> subnetsToGroupNameFiltered = new HashMap<>();
for (Map.Entry<Subnet, List<String>> entry : subnetsToGroupNameMap.entrySet()){
Subnet x = findSubnetBiggerOrEqualToMe(entry.getKey(), subnetsToGroupNameMap.keySet());
if (!subnetsToGroupNameFiltered .containsKey(x)){
subnetsToGroupNameFiltered .put(x, new HashSet<String>());
}
subnetsToGroupNameFiltered .get(x).addAll((subnetsToGroupNameMap.get(x)));
subnetsToGroupNameFiltered .get(x).addAll((entry.getValue()));
}
footprintsMap.entrySet().stream().forEach(entry->entry.getValue().getSubnets().stream().filter(x->!subnetsToGroupNameFiltered .containsKey(x)));
return footprintsMap;
}
函数findSubnetBiggerOrEqualToMe
在所有子网中找到包含子网实例的最大子网。
但是由于此功能应在用户请求下运行,并且Map包含数以万计的实体以及数以万计的子网,因此我需要快速的功能(内存是免费的:))
答案 0 :(得分:0)
我尝试了一种方法,该方法首先按字典顺序对子网进行排序。这样可以将您对findSubnetBiggerOrEqualToMe
的调用从n ^ 2减少到排序算法的复杂度(通常为〜nlog(n))。我将假设您可以订购子网,因为其逻辑应该与findSubnetBiggerOrEqualToMe
中的逻辑类似。
理想地,如果所有子网的超网都是相同集合的前缀,那么这将是线性时间的简单减少。示例[1, 2, 22, 222, 3]
:
for (int i = 0; i < sortedEntries.size() - 1; i++)
{
Entry<Subnet, Set<String>> subnet = sortedEntries.get(i);
Entry<Subnet, Set<String>> potentialSupernet = sortedEntries.get(i + 1);
if (subnet.getKey().isPrefix(potentialSupernet.getKey()))
{
potentialSupernet.getValue().addAll(subnet.getValue());
sortedEntries.remove(i);
i--;
}
}
但是一旦遇到[1, 2, 22, 23]
之类的情况(22和23不是同一网络的前缀),它就不再是简单的简化了,因为您不仅需要查找下一个条目,还需要进一步查找确保您找到了所有超级网(2个必须同时合并到22个和 23个中):
for (int i = 0; i < sortedEntries.size(); i++)
{
Entry<Subnet, Set<String>> subnet = sortedEntries.get(i);
for (int j = i + 1; j < sortedEntries.size(); j++)
{
Entry<Subnet, Set<String>> nextNet = sortedEntries.get(j);
if (!subnet.getKey().isPrefix(nextNet.getKey()))
{
break;
}
Entry<Subnet, Set<String>> nextNextNet = j < sortedEntries.size() - 1 ? sortedEntries.get(j + 1) : null;
if (nextNextNet == null || !subnet.getKey().isPrefix(nextNextNet.getKey()))
{
// biggest, and last superset found
nextNet.getValue().addAll(subnet.getValue());
sortedEntries.remove(i);
i--;
}
else if (!nextNet.getKey().isPrefix(nextNextNet.getKey()))
{
// biggest superset found, but not last
nextNet.getValue().addAll(subnet.getValue());
}
}
}
此方法减少n ^ 2的效果取决于独立网络的数量。具有相同前缀的集合越小,运行时的平方就应该越小。
最后,我认为这种方法的行为与前缀树方法非常相似。在那里,您将构建树,然后迭代叶子(即最大的超集),然后将其祖先的所有项目合并到它们的集合中。