Follwing is my java class TestEntry.java
private void initializemapTest()
{
eventMap = new TreeMap<String,String>();
//Put some value into eventMap
mapTest = new TreeMap<String, String>( new Comparator<String>()
{
public int compare( String key1, String key2 )
{
if( key1 == null )
{
if( key2 == null )
{
return 0;
}
else
{
return 1;
}
}
else
{
if( key2 == null )
{
return -1;
}
else
{
return key1.compareTo( key2 );
}
}
}
} );
for( String s : eventMap.keySet() )
{
mapTest.put( eventMap.get( s ), s ); //Error at this line
}
}
As per my knowledge eventMap doesnot allow null values, hence keyset of eventMap does not have any null values,
if value of any key in eventMap is null, while i try to put it in mapTest, it shoukd not throw any null pointer exception, because its respective comparator allows null values
But why am i getting this exception
java.lang.NullPointerException
at java.util.TreeMap.cmp(TreeMap.java:1911)
at java.util.TreeMap.get(TreeMap.java:1835)
at kidiho.sa.client.reports.ReportEntry.initializemapTest(TestEntry.java:22)
It will throw NullPointerException because in TreeMap api get() method is throwing NullPointerException deliberately if that is null.
final Entry<K,V> getEntry(Object key) {
// Offload comparator-based version for sake of performance
if (comparator != null)
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
Comparable<? super K> k = (Comparable<? super K>) key;
Entry<K,V> p = root;
while (p != null) {
int cmp = k.compareTo(p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
}
From TreeMap:
final Entry<K,V> getEntry(Object key) {
// Offload comparator-based version for sake of performance
if (comparator != null)
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
Comparable<? super K> k = (Comparable<? super K>) key;
Entry<K,V> p = root;
while (p != null) {
int cmp = k.compareTo(p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
}
That is: TreeMap doesn't allow null keys, so you cannot do:
tm.put(null, something)
And subsequently, you cannot do
tm.get(null)
As according to the TreeMap behaviour, those operations actually don't make sense
As other said, you can't use a null value as a TreeMap key, it will throw a NullPointerException.
You're not getting the NullPointerException from the same place probably because your first map has a registered comparator and the second has none.
Related
Could anyone say when TeeMap makes sorting - on adding entries via put method or, for example, before we iterate the map? I tried to find in javadoc but with no luck.
It's done during the altering operations.
For exemple, here is the jdk8 implementation of the method put :
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
#SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
I read this excellent article about the ConcurrentHashMap by Brian Goetz. But when I was looking at the code of ConcurrentHashMap in more recent java version (1.8), I noticed a couple of differences
The next pointer in the MapEntry is not final, but rather volatile so it is possible to modify the list in the middle and not just beginning.
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
Also I don't see where exactly the get method is obtaining the lock when it fails to lookup the key in the initial iteration
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
int h = spread(key.hashCode());
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
if ((eh = e.hash) == h) {
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val;
}
else if (eh < 0)
return (p = e.find(h, key)) != null ? p.val : null;
while ((e = e.next) != null) {
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val;
}
}
return null;
}
Can someone please explain how and where is the lock obtained in the get method, if at all?
This method is supposed to find the smallest value in a Collection that is greater than the key. I keep getting a java.lang.UnsupportedOperationException, and I can't figure out how to fix it. Thanks in advance for any help.
public static <T> T ceiling(Collection<T> c, T key, Comparator<T> comp) {
T ceiling = null;
if (c == null || comp == null) {
throw new IllegalArgumentException();
}
else if (c.size() == 0) {
throw new NoSuchElementException();
}
else {
Iterator<T> itr = c.iterator();
while (itr.hasNext()) {
if (comp.compare(itr.next(), key) < 0) {
itr.remove();
}
}
}
if (c.size() == 0) {
throw new NoSuchElementException();
}
else {
Iterator<T> itr2 = c.iterator();
ceiling = itr2.next();
while (itr2.hasNext()) {
T temp2 = itr2.next();
if (comp.compare(temp2, ceiling) < 0) {
ceiling = temp2;
}
}
}
return ceiling;
}
Most likely you are trying to modify a collection which is unmodifiable.
I suggest you change the method to not modify the collection. (Also I suggest you read the stack trace to understand what it means)
Something like this
public static <T> T ceiling(Collection<T> c, T key, Comparator<T> comp) {
if (c == null || comp == null)
throw new NullPointerException();
T ret = null;
for (T t : c)
if (comp.compare(t, key)>=0 && (ret==null || comp.compare(t, ret)<0))
ret = t;
return ret;
}
I found that some code is changed for null keys in class HashMap in JDK 1.6 or above version compared to the previous JDK version, like 1.5.
In JDK1.5, a static final Object named NULL_KEY is defined: static final Object NULL_KEY = new Object();
Methods, including maskNull, unmaskNull, get and put etc, will use this object.
See
static final Object NULL_KEY = new Object();
static <T> T maskNull(T key) {
return key == null ? (T)NULL_KEY : key;
}
static <T> T unmaskNull(T key) {
return (key == NULL_KEY ? null : key);
}
public V get(Object key) {
Object k = maskNull(key);
int hash = hash(k);
int i = indexFor(hash, table.length);
Entry<K,V> e = table[i];
while (true) {
if (e == null)
return null;
if (e.hash == hash && eq(k, e.key))
return e.value;
e = e.next;
}
}
public V put(K key, V value) {
K k = maskNull(key);
int hash = hash(k);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
if (e.hash == hash && eq(k, e.key)) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, k, value, i);
return null;
}
However, such Object (NULL_KEY) is not used in JDK 1.6 or above version.
Instead, two new methods named getForNullKey() and putForNullKey(value) is added, which are applied in get and put method as well.
See the source code as follows:
public V get(Object key) {
if (key == null)
return getForNullKey();
Entry<K,V> entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
private V getForNullKey() {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null)
return e.value;
}
return null;
}
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
/**
* Offloaded version of put for null keys
*/
private V putForNullKey(V value) {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(0, null, value, 0);
return null;
}
Change always has its reason for changing, such as improving the performance etc. Please help me out with the following 2 question
Q#1 ==> Why this change is made, is there some scenario that the null keys of HashMap implemented in JDK 1.5 encouneters issue ?
Q#2 ==> What is the advantage of null keys mechanism change of HashMap in JDK 1.6 or above version?
Documentation for private V getForNullKey() says
Offloaded version of get() to look up null keys. Null keys map to
index 0. This null case is split out into separate methods for the
sake of performance in the two most commonly used operations (get and
put), but incorporated with conditionals in others.
I have used the below method to Sort a Map first on Object.property1 and then for each Object.property1, sort by Object.property2.
for example,
property1 = TaxIdNumber and
property2 = ProviderName
I was just wondering this can be done in a more shorter and precise manner. Any help or suggestion would be appreciated.
private List<TestObject> sortByValue(final Map m) {
List<TestObject> values = new ArrayList<TestObject>();
values.addAll(m.values());
// First sort the list by Tax ID.
Collections.sort(values, new Comparator<TestObject>() {
public int compare(TestObject r1, TestObject r2) {
Long taxId1 = (r1 == null ? null : r1.getTaxIdNumber());
Long taxId2 = (r2 == null ? null : r2.getTaxIdNumber());
if (taxId1 == null || taxId2 == null) {
return 0;
}
return taxId1.compareTo(taxId2);
}
});
// Then sort the list by Provider name.
Collections.sort(values, new Comparator<TestObject>() {
public int compare(TestObject r1, TestObject r2) {
String name1 = (r1 == null ? null : r1.getProviderName());
String name2 = (r2 == null ? null : r2.getProviderName());
if (name1 == null || name2 == null) {
return 0;
}
if (r1.getTaxIdNumber() == r2.getTaxIdNumber()) {
return name1.compareTo(name2);
} else {
return 0;
}
}
});
return values;
}
You only need one comparator. first compare the taxids. If they are unequal return -1 or 1 as appropriate. If they are equals, then compare the provider name.
something like:
Collections.sort(values, new Comparator<TestObject>() {
public int compare(TestObject r1, TestObject r2) {
Long taxId1 = (r1 == null ? null : r1.getTaxIdNumber());
Long taxId2 = (r2 == null ? null : r2.getTaxIdNumber());
if (taxId1 == null || taxId2 == null) {
return 0;
}
int cmp = taxId1.compareTo(taxId2);
if (cmp != 0)
return cmp;
String name1 = (r1 == null ? null : r1.getProviderName());
String name2 = (r2 == null ? null : r2.getProviderName());
if (name1 == null || name2 == null) {
return 0;
}
return name1.compareTo(name2);
}
});
Your null-handling violates the contract of compare, as you deem null equal to any other value, while the JavaDoc writes:
Compares its two arguments for order. Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
and in particular:
Finally, the implementor must ensure that compare(x, y)==0 implies that sgn(compare(x, z))==sgn(compare(y, z)) for all z.
which your code fails to accomplish for x = null, y = "a", z = "b".
Therefore, if any objects or properties in the list are null, the list may not be sorted correctly.
That being said, I wonder if the list may really contain null values or properties? If not, I'd remove all null checks and end up with
Collections.sort(list, new Comparator<TestObject>() {
#Override public int compare(TestObject o1, TestObject o2) {
int c = o1.getTaxIdNumber().compareTo(o2.getTaxIdNumber);
if (c != 0) {
return c;
}
return o1.getProviderName().compareTo(o2.getProviderName());
}
}
If the list may contain null objects or properties, you must define whether the null values come first or last, and extend the comparator accordingly:
Collections.sort(list, new Comparator<TestObject>() {
#Override public int compare(TestObject o1, TestObject o2) {
// insert null-checks for o1, o2 here
int c = cmp(getTaxIdNumber(), o2.getTaxIdNumber());
if (c != 0) {
return c;
}
return cmp(o1.getProviderName(), o2.getProviderName());
}
private <T extends Comparable<? super T>> cmp(T o1, T o2) {
if (o1 == o2) {
return 0;
else if (o1 == null) {
return -1;
} else if (o2 == null) {
return 1;
} else {
return o1.compareTo(o2);
}
}
}
Now this is quite a bit of repetitive and tricky code, which is why the folks over at Apache wrote the CompareToBuilder. With that API, you can simply write:
#Override int compare(TestObject r1, TestObject r2) {
// insert null checks for r1 and r2 here - if you really need them
return new CompareToBuilder()
.append(r1.getTaxIdNumber(), r2.getTaxIdNumber())
.append(r1.getProviderName(), r2.getProviderName())
.toComparison();
}
}