A TreeSet or TreeMap that allow duplicates

前端 未结 7 1698
走了就别回头了
走了就别回头了 2021-01-17 23:12

I need a Collection that sorts the element, but does not removes the duplicates.

I have gone for a TreeSet, since TreeSet actu

7条回答
  •  臣服心动
    2021-01-18 00:08

    I found a way to get TreeSet to store duplicate keys.

    The problem originated when I wrote some code in python using SortedContainers. I have a spatial index of objects where I want to find all objects between a start/end longitude.

    The longitudes could be duplicates but I still need the ability to efficiently add/remove specific objects from the index. Unfortunately I could not find the Java equivalent of the Python SortedKeyList that separates the sort key from the type being stored.

    To illustrate this consider that we have a large list of retail purchases and we want to get all purchases where the cost is in a specific range.

    // We are using TreeSet as a SortedList
    TreeSet _index = new TreeSet()
    
    // 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 _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);
    

    There are 3 classes created for the list

    • PriceBase: Base class that returns the sort key (the price).
    • Purchase: subclass that contains transaction data.
    • PriceKey: subclass used for the range search.

    When I initially implemented this with TreeSet it worked except in the case where the prices are the same. The trick is to define the compareTo() so that it is polymorphic:

    1. If we are comparing Purchase to PriceKey then only compare the price.
    2. If we are comparing Purchase to Purchase then compare the price and the name if the prices are the same.

    For example here are the compareTo() functions for the PriceBase and Purchase classes.

    // 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());
    }
    

    This trick allows the TreeSet to sort the purchases by price but still do a real comparison when one needs to be uniquely identified.

    In summary I needed an object index to support a range scan where the key is a continuous value like double and add/remove is efficient.

    I understand there are many other ways to solve this problem but I wanted to avoid writing my own tree class. My solution seems like a hack and I am surprised that I can't find anything else. if you know of a better way then please comment.

提交回复
热议问题