2015-01-08 76 views
5

我想創建可能包含重複值的集合,順序不特定。什麼Java收集認爲排列是平等的?

換句話說:

{ 1, 1, 2 } == { 2, 1, 1 } == { 1, 2, 1 } 

其實,我想有一組這些集合的,所以如果我嘗試添加這兩種{ 1, 1, 2 }{ 2, 1, 1 },第二.add()實際上不會做任何事情。

是否有一個標準集合已經表現這種方式?

如果我理解正確:

  • ArrayList中允許重複值,但有一個固定的順序
  • 的HashSet允許以任意,但沒有重複的值
  • TreeSet的保證訂單常量,但不允許有重複值

是否有一個我忽略的集合,允許重複值以及任意值或固定值爲了使兩個具有相同元素的集合被認爲是相等的?


@asteri問我的用例。在一場比賽中,我有不同長度的塊,可以首尾相連,填滿一定的距離。例如,如果距離爲10,則可以填充2-3-5或5-2-3或3-3-4或3-4-3,或其他任何數量的排列。根據可用的塊,我想列出所有可能的解決方案以填補空白。


定製的解決方案
@sprinter建議設立的ArrayList的子類。 @dasblinkenlight和@Dici建議使用Map來存儲{ Element : Count }條目。我選擇結合這兩個建議。以下是TreeMap的一個子類。密鑰總是以相同的順序存儲,以確保hashCode()方法生成相同的值,例如使用相同的鍵和值。

我已經使用了一個increment方法,以方便添加特定整數「值」的新發生。

package com.example.treematch; 

import java.util.Map; 
import java.util.TreeMap; 

public class TreeMatch<K> extends TreeMap<K, Integer> { 

    @Override 
    public boolean equals(Object other) { 
    if (this == other) { 
     return true; 
    } 

    if (!(other instanceof TreeMatch)) { 
     return false; 
    } 

    TreeMatch otherMatch = (TreeMatch) other; 
    if (size() != otherMatch.size()) { 
     return false; 
    } 

    for (Object key : this.keySet()) { 
     if (!otherMatch.containsKey(key)) { 
      return false; 
     } 
    } 

    for (Object key : otherMatch.keySet()) { 
     if (!this.containsKey(key)) { 
     return false; 
     } 

     if (this.get(key) != otherMatch.get(key)) { 
     return false; 
     } 
    } 

    return true; 
    } 

    public void increment(K key) { 
    Integer value; 

    if (this.containsKey(key)) { 
     value = (this.get(key)) + 1; 
    } else { 
     value = 1; 
    } 

    this.put(key, value); 
    } 


    @Override 
    public int hashCode() { 
    int hashCode = 0; 

    for (Map.Entry entry : this.entrySet()) { 
     hashCode += entry.getKey().hashCode(); 
     hashCode = hashCode << 1; 
     hashCode += entry.getValue().hashCode(); 
     hashCode = hashCode << 1; 
    } 

    return hashCode; 
    } 
} 
+0

嗯......有趣的問題。不是我能想到的,儘管在Apache Commons或Guava中可能會有所幫助。出於好奇,我可以問你的用例嗎? – asteri

+0

不是您的問題的答案,但一個簡單的解決方法是將它們作爲列表,然後對它們進行排序並進行比較。 – asteri

+0

有趣:有沒有辦法檢查兩個類別包含相同的元素,獨立的順序?](http://stackoverflow.com/a/1565262/1762224)' - >''HashMultiset.create(C1)。等於(HashMultiset.create(C2));' –

回答

2

我覺得你更好的選擇是換一個Map這樣:

  • 鍵是你的價值觀
  • 值是基於Map.keySet()
  • 等方法按鍵的出現的次數如果發生事件的數量很重要,則等於Map.equals()
+0

我認爲OP會更喜歡只是基於'Map.equals'而不是'keySet'的equals實現,因爲它們似乎希望每個元素的出現次數很重要? –

+0

@LouisWasserman其實,再次重新發布問題,我認爲自從{1,1,2} == {2,1,1} == {1,2,1}'' – Dici

+0

以後我就對了。與keySet比較會使{ 1,1,2} = {1,2},我認爲這不正確? –

6

Java內置庫沒有任何內容,但Guava的Multiset可以做到這一點。

+0

巴姆。 Apache或Guava始終提供。 – asteri

2

HashMap<Integer,Integer>將能夠如果要存儲的值作爲鍵和值出現在地圖中值的次數來表示你的收藏。例如,你的收藏{1, 1, 2}會這樣表示:

{{ 1 : 2 }, { 2 : 1 }} 

這意味着有兩個項目與1值(第一對整數),和一個項目與2值(第二對整數)。

3

這種類型的集合通常被稱爲一個多集。標準庫中沒有包含多重集的實現,但外部庫Guava確實包含多重集。

1

我建議建立的ArrayList的子類,覆蓋equals方法:

class MultiMemberList extends ArrayList { 
    public boolean equals(Object other) { 
     if (this == other) 
      return true; 
     if (!(other instanceof MultiMemberList)) 
      return false; 
     MultiMemberList otherList = (MultiMemberList)other; 
     if (size() != otherList.size()) 
      return false; 
     return stream().distinct().allMatch(element -> countElement(element) == otherList.countElement(element)); 
    } 

    private int countElement(Object element) { 
     return stream().filter(element::equals).count(); 
    } 
} 

我並未提出這個通用保持它的簡單,但很明顯,你應該這樣做。

你也應該重寫哈希函數:做最簡單的事就是調用父類的hash之前列表排序。

一旦你實現了這個然後設置增加了將工作像您期望的MultiMemberList對象:它不會增加,根據這個定義等於第一第二列表。

+0

是不是你的等號方法'O(n^2)'? – Dici

+0

有很多方法可以提高性能。我儘量避免使答案複雜化。 – sprinter

3

您可以使用Eclipse CollectionsBag,也稱爲Multiset

MutableBag<Integer> bag1 = Bags.mutable.with(1, 1, 2); 
MutableBag<Integer> bag2 = Bags.mutable.with(1, 2, 1); 
Assert.assertEquals(bag1, bag2); 

由於您的收藏將只包含整數,所以最好使用原始包。 IntHashBagint[]陣列支持,而不是Object[]陣列,使用更少的內存,並可能更快。

MutableIntBag intBag1 = IntBags.mutable.with(1, 1, 2); 
MutableIntBag intBag2 = IntBags.mutable.with(1, 2, 1); 
Assert.assertEquals(intBag1, intBag2); 

注:我的Eclipse藏品的參與者。

相關問題