简体   繁体   中英

Treemap's headmap method in Java

I was checking headMap method of TreeMap which returns a portion of Map whose keys are strictly less than toKey. So I was expecting output to be B, C but it returns only B. Here is a thing I did weird I changed compareTo method like this return this.priority > o.priority ? 1 : -1; return this.priority > o.priority ? 1 : -1; then it started returning C, B which is I was expecting. I am sure this is not correct but how can I get both B, C which has lower priority than A. Where I am getting it wrong. Thanks.

NavigableMap<PolicyTypePriorityWrapper, String> treeMap = new TreeMap();
PolicyTypePriorityWrapper a = new PolicyTypePriorityWrapper("A", 2);
PolicyTypePriorityWrapper b = new PolicyTypePriorityWrapper("B", 1);
PolicyTypePriorityWrapper c = new PolicyTypePriorityWrapper("C", 1);

treeMap.put(a, "A");
treeMap.put(b, "B");
treeMap.put(c, "C");

NavigableMap<PolicyTypePriorityWrapper, String> map = treeMap.headMap(a, false);
Set<PolicyTypePriorityWrapper> policyTypePriorityWrappers = map.keySet();

for (PolicyTypePriorityWrapper pol: policyTypePriorityWrappers) {
    System.out.println(pol.getPolicyType());
}

PolicyTypePriorityWrapper.java

class PolicyTypePriorityWrapper implements Comparable<PolicyTypePriorityWrapper> {

    private String policyType;
    private int priority;

    public PolicyTypePriorityWrapper(final String policyType, final int priority) {
        this.policyType = policyType;
        this.priority = priority;
    }

    public String getPolicyType() {
        return this.policyType;
    }

    public int getPriority() {
        return this.priority;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        PolicyTypePriorityWrapper that = (PolicyTypePriorityWrapper) o;

        if (priority != that.priority) return false;
        return policyType.equals(that.policyType);
    }

    @Override
    public int hashCode() {
        int result = policyType.hashCode();
        result = 31 * result + priority;
        return result;
    }

    @Override
    public int compareTo(final PolicyTypePriorityWrapper o) {
        return Integer.compare(this.priority, o.priority);
    }
}

That's because you are not following JDK documentation guidelines, from Comprarable :

It is strongly recommended (though not required) that natural orderings be consistent with equals. This is so because sorted sets (and sorted maps) without explicit comparators behave "strangely" when they are used with elements (or keys) whose natural ordering is inconsistent with equals. In particular, such a sorted set (or sorted map) violates the general contract for set (or map), which is defined in terms of the equals method.

As you can see you have circumstances in which a.compareTo(b) == 0 but !a.equals(b) . Both "B", 1 and "C", 1 are considered equal for the TreeMap :

Note that the ordering maintained by a tree map, like any sorted map, and whether or not an explicit comparator is provided, must be consistent with equals if this sorted map is to correctly implement the Map interface. (See Comparable or Comparator for a precise definition of consistent with equals.) This is so because the Map interface is defined in terms of the equals operation, but a sorted map performs all key comparisons using its compareTo (or compare) method, so two keys that are deemed equal by this method are, from the standpoint of the sorted map, equal . The behavior of a sorted map is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Map interface.

For example, if one adds two keys a and b such that (!a.equals(b) && a.compareTo(b) == 0) to a sorted set that does not use an explicit comparator, the second add operation returns false (and the size of the sorted set does not increase) because a and b are equivalent from the sorted set's perspective .

So what happens is that you compareTo is not able to distinguish two elements with same priority but different type, but since a TreeMap is using ONLY that method to decide if two elments are equal then you are not adding them both to the map in the first place.

Did you try if treeMap.size() == 3 ? My guess is that it's 2 in the first place.

The new PolicyTypePriorityWrapper("B", 1) is not qualified because it does it even not make it into the treeMap .

Why? Because the keys are the PolicyTypePriorityWrapper objects which are compared according to their integer priority value. Since b and c have the same priority, only the last one is saved to the treeMap . Compared a , b and c has a lower priority than a and equal to b . The key remains and the value is replaced. So in the map appears an entry PolicyTypePriorityWrapper b with the newly replaced value C .

It's the behavior of Map::put(K key, V value) method.

If the map previously contained a mapping for the key, the old value is replaced by the specified value.

Now the NavigableMap::headMap(K toKey, boolean inclusive) which returns a view of the portion of this map whose keys are less than (or equal to, if inclusive is true) toKey (taken from the documentation). The result is obvious. Only a and b stay in the treeMap , so the a is filtered out since it has smaller priority to b and only b is qualified to be returned.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM