newPropertiesFile.keySet().parallelStream()
.filter(value -> oldPropertiesFile.keySet().parallelStream()
.filter(entry -> oldPropertiesFile.get(entry).toString().equals(newPropertiesFile.get(value).toString()))
.filter(values -> !values.equals(value)).count() > 0)
.collect(Collectors.toMap(entryKey -> (String) entryKey, entryKey -> newPropertiesFile.get(entryKey).toString()));
例如,
我有mapA = {(1,'a'),(2,'b'),(3,'c')}
和mapB = {(5,'a'),(6,'d'),(7,'c')}
比较两个映射的valueList,'a'
中的值'c'
和mapA
出现在mapB
中,它们的键分别是{{1}}和5
因此是我需要的o / p:
7
,5
我已完成上述操作,并获得了所需的输出。但是O(n ^ 2)的复杂度太高了。有优化的方法吗?
更简化的示例:
7
答案 0 :(得分:4)
让我们总结一下这方面的一些变化,因为最好的解决方案只有在阅读了其他答案和评论后才会出现。
这个问题的简化问题是这样的。给定两个地图:
Map<Integer, String> mapA = Map.of(1, "a", 2, "b", 3, "c")
Map<Integer, String> mapB = Map.of(5, "a", 6, "d", 7, "c")
找到mapB
的键,它们对应于两个映射中出现的值。这个问题首先是一个解决方案,它是这样的(为清晰起见进行了编辑):
Set<Integer> result = mapB.keySet().stream()
.filter(keyB -> mapA.keySet().stream()
.filter(keyA -> mapA.get(keyA).equals(mapB.get(keyB)))
.count() > 0)
.collect(toSet());
从本质上讲,这就像两个嵌套的循环,在每个映射的键上循环。内部循环获取每个键的对应值并计算匹配次数。如果至少有一个匹配项,则密钥将通过过滤器传递到结果。
OP对此并不满意,因此要求进行改进,尤其是在算法复杂性方面。如评论中所述,实际问题可能有15,000个地图条目。该算法为O(n ^ 2),并且在此数量的映射的情况下确实开始明显退化。有一些较小的方法可以改进,例如使用anyMatch
代替filter
和count > 0
,但是鉴于answer from Eritrean中提出的替代方法,这些方法不是必需的:
Set<Integer> result = mapB.entrySet().stream()
.filter(entry -> mapA.values().contains(entry.getValue()))
.map(Map.Entry::getKey)
.collect(toSet());
这更好,因为它在mapA的contains
视图上使用了values()
操作,代替了先前解决方案的内部流。但是,不会为地图的值建立索引,因此contains()
对地图的值起作用的唯一方法是(可能)搜索每个条目。这比以前要好一些,因为如果找到匹配项,contains()
可以立即返回;但是如果找不到匹配项,则必须搜索地图的所有值。因此,平均而言,这种变化仍然需要O(n ^ 2)的时间。
减轻这种情况的一种方法是将mapA的值提取到HashSet
中。这会将contains()
检查从线性时间减少到恒定时间,从而将总体复杂度从O(n ^ 2)减少到O(n)。看起来像这样:
Set<String> aValues = new HashSet<>(mapA.values());
Set<Integer> result = mapB.entrySet().stream()
.filter(entry -> aValues.contains(entry.getValue()))
.map(Map.Entry::getKey)
.collect(toSet());
这是一个很大的改进,但是事实证明,根本不需要使用流。回到问题陈述,它具有子句“ ...在两个映射中都出现的值”。实质上,这是在值集合上进行设置的交集。在Java中进行交叉的方法是使用retainAll
方法。也就是说,给定两个集合x
和y
,做x.retainAll(y)
只会在x
中保留那些也出现在y
中的元素,而它将删除其他元素。这本质上是一个设定的交集。为此,retainAll
通常必须在contains
上反复调用y
,因此,最好确保操作快速-就像使用HashSet
一样。>
好吧,如果我们与值集合相交,那就给了我们值-但是我们想要键。特别是,我们需要mapB的键。我们该怎么做?
事实证明,地图的values()
视图支持删除(retainAll
可以这样做),并且如果从其中删除了值,则会从基础地图中删除相应的条目。在这种情况下,我们可以从mapB(或副本)开始,获取其values()
视图,使用先前已加载到retainAll
中的mapA的值调用HashSet
。这样在mapB中仅保留具有与mapA相同的值的条目。由于我们对键感兴趣,而不是对条目感兴趣,因此我们只获得keySet()
视图。该代码如下所示:
Set<String> aValues = new HashSet<>(mapA.values());
Set<Integer> mapBcopy = new HashMap<>(mapB);
mapBcopy.values().retainAll(aVals);
Set<Integer> result = mapBcopy.keySet();
这说明了与在使用流的情况下相比,如何更可能在集合视图上使用集合批量操作来完成某些任务。
答案 1 :(得分:0)
如果我说对了:
比较两个地图的valueList,mapA中的值'a'和'c'出现在mapB中,其键分别为5和7。因此,我需要的o / p:5、7
仅用list#contains过滤第二张地图还不够:
Map<Integer,String> mapA = new HashMap<>();
mapA.put(1, "a");
mapA.put(2, "b");
mapA.put(3, "c");
Map<Integer,String> mapB = new HashMap<>();
mapB.put(5, "a");
mapB.put(6, "d");
mapB.put(7, "c");
List<Integer> list = mapB.entrySet().stream()
.filter(e->mapA.containsValue(e.getValue()))
.map(e -> e.getKey())
.collect(Collectors.toList());
System.out.println(list);