簡體   English   中英

允許重復的 TreeSet 或 TreeMap

[英]A TreeSet or TreeMap that allow duplicates

我需要一個Collection進行排序的元素,但不會刪除重復項。

我選擇了TreeSet ,因為TreeSet實際上將值添加到支持的TreeMap

public boolean add(E e) {
    return m.put(e, PRESENT)==null;
}

並且 TreeMap 使用Comparators compare邏輯刪除重復項

我編寫了一個Comparator ,在元素相等的情況下返回 1 而不是 0。 因此,在元素相等的情況下,帶有此ComparatorTreeSet不會覆蓋重復項,而只會對其進行排序。

我已經針對簡單的String對象對其進行了測試,但我需要一組自定義對象。

public static void main(String[] args)
{       
        List<String> strList = Arrays.asList( new String[]{"d","b","c","z","s","b","d","a"} );      
        Set<String> strSet = new TreeSet<String>(new StringComparator());       
        strSet.addAll(strList);     
        System.out.println(strSet); 
}

class StringComparator implements Comparator<String>
{
    @Override
    public int compare(String s1, String s2)
    {
        if(s1.compareTo(s2) == 0){
            return 1;
        }
        else{
            return s1.compareTo(s2);
        }
    }
}

這種方法很好還是有更好的方法來實現這一目標?

編輯

實際上,我有以下類的 ArrayList:

class Fund 
{
    String fundCode;
    BigDecimal fundValue;
    .....

    public boolean equals(Object obj) {
    // uses fundCode for equality
    }
}

我需要所有的fundCode最高fundValue

您可以使用 PriorityQueue。

PriorityQueue<Integer> pQueue = new PriorityQueue<Integer>(); 

PriorityQueue() :創建一個具有默認初始容量 (11) 的 PriorityQueue,它根據元素的自然順序對其進行排序。

這是文檔的鏈接: https : //docs.oracle.com/javase/8/docs/api/java/util/PriorityQueue.html

我需要所有具有最高基金價值的基金代碼

如果這是您想要排序的唯一原因,我建議您根本不要進行排序。 排序的復雜性主要是O(n log(n)) 查找最大值只有O(n)的復雜度,並通過對列表的簡單迭代實現:

List<Fund> maxFunds = new ArrayList<Fund>();
int max = 0;
for (Fund fund : funds) {
    if (fund.getFundValue() > max) {
        maxFunds.clear();
        max = fund.getFundValue();

    }
    if (fund.getFundValue() == max) {
        maxFunds.add(fund);

    }
}

您可以通過使用像Guava這樣的第三級庫來避免該代碼。 請參閱: 如何從 Guava 中的 List 中獲取 max() 元素

您可以使用Collections.sort對列表進行排序。

鑒於您的Fund

List<Fund> sortMe = new ArrayList(...);
Collections.sort(sortMe, new Comparator<Fund>() {
  @Override
  public int compare(Fund left, Fund right) {
    return left.fundValue.compareTo(right.fundValue);
  }
});
// sortMe is now sorted

在 TreeSet 的情況下,Comparator 或 Comparable 用於比較和存儲對象。 不調用等於,這就是為什么它不識別重復的

我們可以使用 List 代替 TreeSet 並實現 Comparable 接口。

public class Fund implements Comparable<Fund> {

    String fundCode;
    int fundValue;

    public Fund(String fundCode, int fundValue) {
        super();
        this.fundCode = fundCode;
        this.fundValue = fundValue;
    }

    public String getFundCode() {
        return fundCode;
    }

    public void setFundCode(String fundCode) {
        this.fundCode = fundCode;
    }

    public int getFundValue() {
        return fundValue;
    }

    public void setFundValue(int fundValue) {
        this.fundValue = fundValue;
    }

    public int compareTo(Fund compareFund) {

        int compare = ((Fund) compareFund).getFundValue();
        return compare - this.fundValue;
    }

    public static void main(String args[]){

        List<Fund> funds = new ArrayList<Fund>();

        Fund fund1 = new Fund("a",100);
        Fund fund2 = new Fund("b",20);
        Fund fund3 = new Fund("c",70);
        Fund fund4 = new Fund("a",100);
        funds.add(fund1);
        funds.add(fund2);
        funds.add(fund3);
        funds.add(fund4);

        Collections.sort(funds);

        for(Fund fund : funds){
            System.out.println("Fund code: " + fund.getFundCode() +  "  Fund value : " + fund.getFundValue());
        }
    }
}

將元素添加到數組列表中,然后使用實用程序 Collections.sort 對元素進行排序。 然后根據您的密鑰實現可比較並編寫您自己的 compareTo 方法。

也不會刪除重復項,也可以排序:

List<Integer> list = new ArrayList<>();

Collections.sort(list,new Comparator<Integer>() 
{

  @Override


  public int compare(Objectleft, Object right) {


**your logic**

     return '';

  }

}

)
;

我找到了一種讓TreeSet存儲重復鍵的方法。

問題起源於我使用SortedContainers在 python 中編寫一些代碼。 我有一個對象的空間索引,我想在其中找到開始/結束經度之間的所有對象。

經度可能是重復的,但我仍然需要能夠有效地從索引中添加/刪除特定對象。 不幸的是,我找不到將排序鍵與存儲的類型分開的 Python SortedKeyList的 Java 等效項。

為了說明這一點,請考慮我們有大量的零售采購清單,我們希望獲取成本在特定范圍內的所有采購。

// We are using TreeSet as a SortedList
TreeSet _index = new TreeSet<PriceBase>()

// populate the index with the purchases. 
// Note that 2 of these have the same cost
_index.add(new Purchase("candy", 1.03));
Purchase _bananas = new Purchase("bananas", 1.45);
_index.add(new Purchase(_bananas);
_index.add(new Purchase("celery", 1.45));
_index.add(new Purchase("chicken", 4.99));

// Range scan. This iterator should return "candy", "bananas", "celery"
NavigableSet<PriceBase> _iterator = _index.subset(
    new PriceKey(0.99), new PriceKey(3.99));

// we can also remove specific items from the list and
// it finds the specific object even through the sort
// key is the same
_index.remove(_bananas);

為列表創建了 3 個類

  • PriceBase:返回排序鍵(價格)的基類。
  • 購買:包含交易數據的子類。
  • PriceKey:用於范圍搜索的子類。

當我最初用 TreeSet 實現它時,它工作正常,除非價格相同。 訣竅是定義 compareTo() 以便它是多態的:

  1. 如果我們將購買與 PriceKey 進行比較,則只比較價格。
  2. 如果我們比較購買與購買,那么如果價格相同,則比較價格和名稱。

例如,這里是 PriceBase 和 Purchase 類的 compareTo() 函數。

// in PriceBase
@Override
public int compareTo(PriceBase _other) {
    return Double.compare(this.getPrice(), _other.getPrice());
}

// in Purchase
@Override
public int compareTo(PriceBase _other) {

    // compare by price
    int _compare = super.compareTo(_other);

    if(_compare != 0) {
        // prices are not equal
        return _compare;
    }

    if(_other instanceof Purchase == false) {
        throw new RuntimeException("Right compare must be a Purchase");
    }

    // compare by item name
    Purchase _otherPurchase = (Purchase)_other;
    return this.getName().compareTo(_otherChild.getName());
}

這個技巧允許 TreeSet 按價格對購買進行排序,但在需要唯一標識時仍會進行真正的比較。

總之,我需要一個對象索引來支持范圍掃描,其中鍵是一個連續的值,比如 double 並且添加/刪除是有效的。

我知道有很多其他方法可以解決這個問題,但我想避免編寫自己的樹類。 我的解決方案似乎是一個黑客,我很驚訝我找不到其他任何東西。 如果您知道更好的方法,請發表評論。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM