Java 如何比较两个映射并检索两个映射中出现的值的键?

Java 如何比较两个映射并检索两个映射中出现的值的键?,java,java-8,java-stream,Java,Java 8,Java Stream,比如说,, 我有mapA={(1,'a'),(2,'b'),(3,'c')}和mapB={(5,'a'),(6,'d'),(7,'c')} 比较两个地图的值列表,mapA中的值'a'和'c'出现在mapB中,它们的键分别为5和7 因此,我需要o/p: 5,7 我已完成上述操作,并获得了所需的输出。但是O(n^2)的复杂性太高了。有优化的方法吗 一个更简单的例子: newPropertiesFile.keySet().parallelStream() .filter(value ->

比如说,, 我有
mapA={(1,'a'),(2,'b'),(3,'c')}
mapB={(5,'a'),(6,'d'),(7,'c')}
比较两个地图的值列表,
mapA
中的值
'a'
'c'
出现在
mapB
中,它们的键分别为
5
7

因此,我需要o/p:
5
7

我已完成上述操作,并获得了所需的输出。但是O(n^2)的复杂性太高了。有优化的方法吗

一个更简单的例子:

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.keySet().parallelStream()
    .filter(v->mapB.keySet().parallelStream()
            .filter(e->mapB.get(v).equals(mapA.get(v)))
            .filter(v->!v.equals(v)).count()>0)
    .forEach(System.out::println);
本质上,这就像两个嵌套的循环,在每个贴图的关键点上循环。内部循环获取每个键的对应值,并统计匹配数。如果至少有一个匹配项,则键将通过过滤器传递到结果

OP对此并不满意,并要求改进,特别是在算法复杂性方面。正如评论中提到的,真正的问题可能有15000个映射条目。这个算法是O(n^2),它确实开始随着映射数量的增加而显著降低。有一些小方法可以改进这一点,例如使用
anyMatch
而不是
filter
count>0
,但考虑到以下建议的替代方案,这些方法是不必要的:

这是一个很大的改进,但事实证明,使用流是完全没有必要的。返回到问题语句,它有一个子句“…两个映射中出现的值”。这实际上是在值集合上进行集合交集。Java中进行交叉的方法是使用
retainal
方法。也就是说,给定两个集合
x
y
,执行
x.retainal(y)
将只在
x
中保留那些也出现在
y
中的元素,并删除其他元素。这本质上是一个集合交点。为此,
retainal
通常必须在
y
上重复调用
contains
,因此最好确保操作快速,就像使用
哈希集一样

好的,如果我们与值集合相交,这会给我们值——但我们需要键。特别是,我们需要mapB的键。我们怎么做

事实证明,映射的
values()
视图支持删除,而
retainal
支持删除,如果从映射中删除了值,则从基础映射中删除相应的条目。在这种情况下,我们可以从mapB(或一个副本)开始,获取它的
values()
视图,调用
retainal
,使用之前加载到
哈希集中的mapA值。这将在mapB中只保留与mapA具有相同值的条目。因为我们感兴趣的是键,而不是条目,所以我们只得到
keySet()
视图。该代码如下所示:

Set<Integer> result = mapB.entrySet().stream()
    .filter(entry -> mapA.values().contains(entry.getValue()))
    .map(Map.Entry::getKey)
    .collect(toSet());
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());
Set aValues=newhashset(mapA.values());
Map mapBcopy=新HashMap(mapB);
mapBcopy.values().retainal(aValues);
Set result=mapBcopy.keySet();

这演示了如何在集合视图上使用集合批量操作来比使用流更简单地完成某些任务。

让我们总结一下这方面的一些变化,因为最好的解决方案只有在阅读其他答案和注释后才会出现

这个问题的简化问题是这样的。给出两张地图:

mapA.keySet().parallelStream()
    .filter(v->mapB.keySet().parallelStream()
            .filter(e->mapB.get(v).equals(mapA.get(v)))
            .filter(v->!v.equals(v)).count()>0)
    .forEach(System.out::println);
本质上,这就像两个嵌套的循环,在每个贴图的关键点上循环。内部循环获取每个键的对应值,并统计匹配数。如果至少有一个匹配项,则键将通过过滤器传递到结果

OP对此并不满意,并要求改进,特别是在算法复杂性方面。正如评论中提到的,真正的问题可能有15000个映射条目。这个算法是O(n^2),它确实开始随着映射数量的增加而显著降低。有一些小方法可以改进这一点,例如使用
anyMatch
而不是
filter
count>0
,但考虑到以下建议的替代方案,这些方法是不必要的:

这是一个很大的改进,但事实证明,使用流是完全没有必要的。返回到问题语句,它有一个子句“…两个映射中出现的值”。这实际上是在值集合上进行集合交集。Java中进行交叉的方法是使用
retainal
方法。也就是说,给定两个集合
x
y
,执行
x.retainal(y)
将只在
x
中保留那些也出现在
y
中的元素,并删除其他元素。这本质上是一个集合交点。为此,
retainal
通常必须在
y
上重复调用
contains
,因此最好确保操作快速,就像使用
哈希集一样

好的,如果我们与值集合相交,这会给我们值——但我们需要键。特别是,我们需要mapB的键。我们怎么做

事实证明,映射的
values()
视图支持删除,而
retainal
支持删除,如果从映射中删除了值,则从基础映射中删除相应的条目。在这种情况下,我们可以从mapB(或一个副本)开始,获取它的
values()
视图,调用
retainal
,使用之前加载到
哈希集中的mapA值。这将在mapB中只保留与mapA具有相同值的条目。因为我们感兴趣的是键,而不是条目,所以我们只得到
keySet()
视图。Th
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());
Set<String> aValues = new HashSet<>(mapA.values());
Map<Integer, String> mapBcopy = new HashMap<>(mapB);
mapBcopy.values().retainAll(aValues);
Set<Integer> result = mapBcopy.keySet();