Given a Java Map in which both the key and the value are serializable, I want to be able to serialize the key-value pair. Is there any efficient way that given the key I could retrieve the key-value pair as an object and serialize that? I've seen the entrySet() method for the map class but I don't like to search for the pair twice.
map does not provides such method. But what Still you can do you can, by extending the Map implementation as example - HashMap<K,V> and implement such method like -
Map<K,V> map = new HashMap<K,V>(){
public Entry<K,V> get(Object key) { // overloading get method in subclass
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e;
}
return null;
}
private Entry<K,V> getForNullKey() {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null)
return e;
}
return null;
}};
...
Map.Entry<K,V> entry1 = map.get(key);// invoking Entry<K,V> get(Object key)
You can serialize it as an array:
Object obj = new Object[] {key, value}
obj is Serializable as soon as key and value are Serializable
Related
I can see static Entry class in Hashmap in java have Equals and Hashcode methods.What is the purpose of these methods.put,get methods we use object hashcode and equals methods....
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
final int hash;
/**
* Creates new entry.
*/
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
public final boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o;
Object k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
public final int hashCode() {
return (key==null ? 0 : key.hashCode()) ^
(value==null ? 0 : value.hashCode());
}
public final String toString() {
return getKey() + "=" + getValue();
}
Map.Entry is a representation of a key-value pair, in a map. All of this methods can be used for comparing key-value pairs, not for comparing key-objects.
Entryes are objects too, it's entirely possible to compare them for equality or to use them as keys in a map.
In addition, they are sometimes used as a poor man's Pair class: Didn't Java once have a Pair class?, https://www.baeldung.com/java-pairs, https://www.techiedelight.com/five-alternatives-pair-class-java/ and for this use you really want these implementations of equals and hashCode.
As we know, null is not allowed in Hashtable.
But when I checked the source code of Hashtable (jdk 1.8).
I only saw the check of value and couldn't find the key check.
Here is the source code below of the put method:
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
#SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}
The key check is here:
int hash = key.hashCode();
This will throw a NullPointerException if the key is null.
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
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;
}
I am trying to understand the HashMap implemintation. I understood everything except this line - Object k;
Please, explain how this Object k appears??
In that implementation of a HashMap, the data structure was backed by an array of linked lists of entries. These entries have a key and a value.
That variable k is used to store the key for each entry while iterating over the linked list bucket. If it's found to be equal (reference and then value equality) to the key with which you're trying to insert a value, then that value replaces the old.
I am trying to put a map into a properties using putAll() and get a NullPointerException even when my map is not null
Map<String,Object> map = item.getProperties();
Properties props = new Properties();
if(map!=null) {
props.putAll(map); //NPE here
}
The item.getProperties() returns Map<String,Object> and I want to store those properties into a properties file.
I also tried to instantiate the map first
Map<String,Object> map = new HashMap<String, Object>()
map = item.getProperties();
Properties props = new Properties();
if(map!=null) {
props.putAll(map); //NPE here
}
I know that the map is not null, since I can see the map values in the log.
The Properties class extends Hashtable which does not accept null values for its entries.
Any non-null object can be used as a key or as a value.
If you try to put a null value, the Hashtable#put(Object, Object) method throws a NullPointerException. It's possible your
map = item.getProperties();
contains null values.
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
V old = e.value;
e.value = value;
return old;
}
}
modCount++;
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
Entry<K,V> e = tab[index];
tab[index] = new Entry<K,V>(hash, key, value, e);
count++;
return null;
}
Maybe your map has null key or value.
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.