2017-05-26 36 views
0

JDK中的equals implementatin的java.util.concurrent.ConcurrentSkipListSet是如下JDK java.util.concurrent.ConcurrentSkipListSet.equals(對象o)執行效率

public boolean equals(Object o) { 
    // Override AbstractSet version to avoid calling size() 
    if (o == this) 
     return true; 
    if (!(o instanceof Set)) 
     return false; 
    Collection<?> c = (Collection<?>) o; 
    try { 
     return containsAll(c) && c.containsAll(this); 
    } catch (ClassCastException unused) { 
     return false; 
    } catch (NullPointerException unused) { 
     return false; 
    } 
} 

但我認爲下面的代碼似乎是更有效的

public boolean myEquals(Object o) { 
    if (o == this) 
     return true; 
    if (!(o instanceof Set)) 
     return false; 
    Collection<?> c = (Collection<?>) o; 
    if (c.size() != this.size()) { 
     return false; 
    } 

    Iterator ic = c.iterator(); 
    Iterator id = iterator(); 

    while (ic.hasNext() && id.hasNext()) { 
     if (!ic.next().equals(id.next())) { 
      return false; 
     } 
    } 

    return true; 
} 

和一個簡單的測試也可能支撐第二equals

public class Test { 
    public static void main(String[] args) { 
     ConcurrentSkipListSet<Integer> set1 = new ConcurrentSkipListSet<Integer>(); 
     ConcurrentSkipListSet<Integer> set2 = new ConcurrentSkipListSet<Integer>(); 

     for (int i = 0; i < 10000000; i++) { 
      set1.add(i); 
      set2.add(i); 
     } 

     long ts = System.currentTimeMillis(); 
     System.out.println(set1.equals(set2)); 
     System.out.println(System.currentTimeMillis() - ts); 

     ts = System.currentTimeMillis(); 
     System.out.println(myset1.myEquals(myset2)); 
     System.out.println(System.currentTimeMillis() - ts); 
    } 
} 

輸出結果

true 
2713 
true 
589 

在JDK評論,它說,This definition ensures that the equals method works properly across different implementations of the set interface.可能有人善意解釋一下嗎?

+3

您的代碼假定這兩個集合都是有序的。他們沒有。 – EJP

+0

@EJP實際上'ConcurrentSkipListSet'擴展了'NavigableSet',它擴展了'SortedSet' –

+2

請在[email protected]郵件列表中討論你的問題。 – Fairoz

回答

0

僅供參考,OpenJDK thread導致創建JDK-8181146 ConcurrentSkipListSet.equals efficiency

在JDK的評論中說,This definition ensures that the equals method works properly across different implementations of the set interface.任何人都可以好心解釋一下嗎?

它來自Set.equals(Object)。每文檔:

如果指定的對象也是一組

返回true,兩套具有相同的大小,並且指定set的所有成員都包含在此set中(或者,這組的每一個成員包含在指定的集合中)。該定義確保equals方法在設置接口的不同實現之間正常工作。

據暗示Set.equals實現應該通過的Set.contains(Object)行爲來定義。然後導致你這個空話從java.util.SortedSet

注意,如果有序set要正確實現了由有序集合(無論是否提供了明確的比較器)保持的順序必須與equals一致設置界面。 (請參閱Comparable接口或Comparator接口以獲得與equals一致的精確定義。)這是因爲Set接口是根據equals操作定義的,但有序集合使用其compareTo(或compare)方法執行所有元素比較,所以從排序集的角度來看,被這種方法視爲相等的兩個元素是相等的。即使排序與等號不一致,排序集的行爲也是很好定義的;它只是不服從Set接口的總體合同。

那麼,爲什麼'這包含這個,幷包含這個'在ConcurrentSkipListSet?首先,您希望避免撥打ConcurrentSkipListSet.size(),因爲:

請注意,與大多數收集不同,此方法不是常規操作。由於這些集合的異步性質,確定元素的當前數量需要遍歷它們以對它們進行計數。此外,在執行此方法期間可能會更改大小,在這種情況下返回的結果將不準確。因此,這種方法在併發應用程序中通常不是很有用。

第二個原因是你想要'等於等於'。

讓我們以一個殘酷的例子關你的代碼:

private static boolean myEquals(Set o1, Set o2) { 
    if (o1.size() == 1 && o2.size() == 1) { 
     Iterator ic = o2.iterator(); 
     Iterator id = o1.iterator(); 

     while (ic.hasNext() && id.hasNext()) { 
      if (!ic.next().equals(id.next())) { 
       return false; 
      } 
     } 
     return true; 
    } 
    return o1.equals(o2); 
} 

public static void main(String[] args) { 
    print(skiplist(new BigDecimal("1.0")), tree(new BigDecimal("1.00"))); 
    print(skiplist(new BigDecimal("1.0")), hash(new BigDecimal("1.00"))); 
    print(skiplist(new BigDecimal("1.0")), identity(new BigDecimal("1.00"))); 
    print(skiplist(BigDecimal.ONE), identity(new BigDecimal(BigInteger.ONE, 0))); 
} 

private static Collection<BigDecimal> e() { 
    return Arrays.asList(new BigDecimal("1.0")); 
} 

private static <E> Set<E> hash(E... e) { 
    return new HashSet<>(Arrays.asList(e)); 
} 

private static <E> Set<E> skiplist(E... e) { 
    return new ConcurrentSkipListSet<>(Arrays.asList(e)); 
} 

private static <E> Set<E> tree(E... e) { 
    return new TreeSet<>(Arrays.asList(e)); 
} 

private static <E> Set<E> identity(E... e) { 
    Set<E> s = Collections.newSetFromMap(new IdentityHashMap<E, Boolean>()); 
    Collections.addAll(s, e); 
    return s; 
} 

private static void print(Set o1, Set o2) { 
    System.out.println(o1.getClass().getName() 
      + "==" + o2.getClass().getName() + ": " 
      + o1.equals(o2) + ": " + myEquals(o1, o2)); 
    System.out.println(o2.getClass().getName() 
      + "==" + o1.getClass().getName() + ": " + o2.equals(o1) 
      + ": " + myEquals(o2, o1)); 
} 

,輸出:

java.util.concurrent.ConcurrentSkipListSet==java.util.TreeSet: true: false 
java.util.TreeSet==java.util.concurrent.ConcurrentSkipListSet: true: false 
java.util.concurrent.ConcurrentSkipListSet==java.util.HashSet: false: false 
java.util.HashSet==java.util.concurrent.ConcurrentSkipListSet: false: false 
java.util.concurrent.ConcurrentSkipListSet==java.util.Collections$SetFromMap: false: false 
java.util.Collections$SetFromMap==java.util.concurrent.ConcurrentSkipListSet: false: false 
java.util.concurrent.ConcurrentSkipListSet==java.util.Collections$SetFromMap: false: true 
java.util.Collections$SetFromMap==java.util.concurrent.ConcurrentSkipListSet: false: true 

即輸出顯示新的實現不會consistent with equals

的對於C類的自然排序被認爲與等於一致當且僅當e1.compareTo(e2)== 0具有t對於類C的每個e1和e2,他都有與e1.equals(e2)相同的布爾值。請注意,null不是任何類的實例,並且e.compareTo(null)應該拋出NullPointerException,即使e.equals(null)返回false。

現在我們可以通過與 ((Comparable) e1).compareTo((Comparable) e2) != 0Comparator.compare(e1, e2) != 0更換元件檢查解決這個問題,並添加檢查,以試圖確定這兩組使用相同的排序,但請記住,集合可以包裹並沒有什麼能夠阻止來自hiding the fact that a set is backed by sorted set的來電者。現在你回到'這包含了那個',它包含了'可以處理集合包裝器的'equals'的實現。

另一個'包含這個並且包含這個'實現的好的屬性是equals實現不會爲給定的集合創建一個迭代器對象,在最壞的情況下,這個對象可能有一個像Arrays.asList(s.toArray()).iterator()這樣的實現。

如果不放寬規範,放寬現有行爲或添加一個返回BiPredicate來捕獲集合的「等價關係」的集合方法,我認爲很難在JDK中添加這樣的優化。