繁体   English   中英

检查两个集合是否包含至少一个相同元素的快速方法

[英]fast way to check if two sets contain at least one same element

我有两个TreeMap,我想检查它们是否至少包含一个相同的键(这些键是字符串)。 所以我使用两个循环进行比较:

boolean found = false;
for(String key1 : map1.keySet()){
    for(String key2 : map2.keySet()){
        if(key1.equals(key2)){
            found = true;
            break;
        }
    }
    if(found){
        break;
    }
}
if(found){
    someFunction(map1, map2);
}

因为我有500,000个TreeMap(每个约有1000个键),并且我想对照每个地图检查每个地图,所以需要很长时间。 有谁知道更快的解决方案?

*编辑:我想调用“someFunction()” - 方法,每次我找到两个地图与leat一个相同的键。 我认为> 90%的案例found == false

您可以尝试的一种方法是创建key-> maps的多重映射,即迭代所有500k映射并为它们包含的每个键添加它们。

然后再次遍历键,如果键有两个或更多映射,则这些映射共享它。

采用这种方法,复杂度应从O(n² * m)下降到O(n * m)n是映射的数量, m是键的数量)。

粗略轮廓:

Multimap<Key, Map<Key, Value>> mapsContainingKey = ... ;//could be a Guava Multimap
//O(n * m) complexity
for(Map<Key, Value> m : largeSetOfTreeMaps ) {
  for(Key k : m.keySet() ) {
    mapsContainingKey.put( k, m );
  }
}

//O(m)
for( Entry<Key, Map<Key, Value>> entry : mapsContainingKey.entries() ) {
  Key key = entry.getKey();
  Collection<Map<Key, Value>> mapsWithSameKey = entry.getValue();
  if( mapsWithSameKey.size() > 1 ) {
    //all maps in that collection share this key
  }
}

更新:我运行了一个快速的基准测试,虽然没有优化,但有一个明显的趋势:

“天真”的方法是循环遍历所有地图并检查所有后续地图,以便每对只检查一次。 此外,我应用了Holger建议用于比较两张地图的内容。

我在这里发布的是“地图”方法。

我的机器上有1000张地图的结果,每张地图都有100个长度为10的随机String键:

naive: 11656 ms
map:     235 ms

更新2:一些不同大小的结果:

1000张不同长度的100张地图(按键越长,碰撞越少)

key length   1        2         3         4         5        10        20
naive      417 ms  3221 ms  10937 ms  11273 ms  11357 ms  11383 ms  11706 ms
map         16 ms    43 ms     86 ms    224 ms    245 ms    210 ms    154 ms

1000个地图,每个密钥的密钥数量不同,密钥长度为10(密钥越多,冲突越多)

key count    50       100       500
naive      4865 ms  11368 ms  81280 ms 
map          64 ms    206 ms    913 ms

数量不一的地图(每个地图有1000个键,键长为10)(地图越多,碰撞越多)

map count    500     1000      2000
naive      6323 ms  12766 ms  47798 ms 
map         139 ms    206 ms    333 ms

如您所见,地图数量对此影响最大,其次是密钥数量。

你没有说明排序,但我假设所有TreeMap都有相同的顺序。 在这种情况下,您可以通过使用第二张地图的边界来减小外部迭代范围。 您可以简单地询问地图是否包含密钥,因此内部迭代完全过时了。

for(String s: map1.navigableKeySet().subSet(map2.firstKey(), true, map2.lastKey(), true)) {
    if(map2.containsKey(s)) {
        someFunction(map1, map2);
        break;
    }
}

说明:

假设您有以下映射键:

map2:    D, E, F, G, H
         |           |
       first        last
map1: A,    E,    G,   I
            |<--->|
          subset("D", true, "H", true)

这里, map2的第一个元素是"D" ,而最后一个元素是"H" 当将这些元素作为包含边界传递给map1的navigableKeySet().subSet(…)方法时,我们将得到最接近的内部集["E", "G"]作为搜索范围,因此我们排除了"A""I"在我们开始线性搜索之前(请记住,这些只是示例占位符,它们可能代表大量的键)。


通过考虑更多,您可以在比较时跳过两个地图中的任意范围:

public static boolean haveCommonKeys(TreeMap<String,?> map1, TreeMap<String,?> map2) {
    if(map1.isEmpty()) return false;
    for(String s=map1.firstKey(); s!=null; ) {
        String s2=map2.ceilingKey(s);
        if(s2==null) break;
        if(s2.equals(s)) return true;
        s=map1.ceilingKey(s2);
        if(s2.equals(s)) return true;
    }
    return false;
}

在此解决方案中,我们从地图的第一个(最小)键开始,并向每个地图询问一个与我们在另一个地图中找到的值相同或更大的键。 这样,我们将跳过地图的所有连续键,其他地图不包含中间键。

创建自己的地图,其中包含一组对象的每个键。 如果你在一个键上调用getter,你将得到一组对象。 如果在此集合上调用size(),则将知道是否有多个对象映射到此键。 但是您不应该将所有数据都放在一张地图中,因为这会使核心速度变慢。 如果可以的话,最好对密钥进行排序。 就像在一张地图中所有由数字组成的键,在一张地图中全部由字母组成,其余在第三张地图中一样。 然后你可以检查密钥,获取属于它的地图并使用它。 像这样:

public class MyMap{

private Map<String key, Set<Object>> stuff;

 public MyMap(){
  stuff = new HashMap<String key, Set<Object>>(); // Or any other map instead of HashMap
 }

 public void put(final String pKey, final Object pObject){
  Set<Object> objects = stuff.get(pKey);
  if(objects!=null)
   objects.add(pObject);
  else{
   Set<Object> objects = new HashSet<Object>();
   objects.add(pObject);
   stuff.put(pKey, objects);
  }
 }

 public Set<Object> get(String pKey){
  return stuff.get(pKey);
 }

 public void remove(String pKey){
  stuff.remove(pKey);
 }

}

但是,如果你有这么多的地图,那么这个可能会破坏你的表现。 您必须拆分键以使其更快:)也可以使用任何其他映射/集。 我使用HashSet因为我认为如果你想像你告诉我们那样进行检查,你不想将同一个对象添加到同一个密钥两次。

希望我能帮忙:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM