簡體   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