When the count field of Java Hashtable initialized? - java

When reading the Java Hashtable source code I noticed that the count field of Hashtable is not initialized when declare I see that in the readObject method there is this code:
count = 0;
When is the count field initialized?

readObject also calls reconstitutionPut(table, key, value) for each key-value pair, and that method increments count.
Here's the relevant code with the relevant lines marked:
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
...
count = 0;
// Read the number of elements and then all the key/value objects
for (; elements > 0; elements--) {
#SuppressWarnings("unchecked")
K key = (K)s.readObject();
#SuppressWarnings("unchecked")
V value = (V)s.readObject();
// synch could be eliminated for performance
reconstitutionPut(table, key, value); // <---------------
}
}
private void reconstitutionPut(Entry<?,?>[] tab, K key, V value)
throws StreamCorruptedException
{
if (value == null) {
throw new java.io.StreamCorruptedException();
}
// Makes sure the key is not already in the hashtable.
// This should not happen in deserialized version.
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
throw new java.io.StreamCorruptedException();
}
}
// Creates the new entry.
#SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>)tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++; // <---------------
}

Related

HashMap implementation how do it faster and better in java

I have HashMap implementation, but operations are too slowly for me it must be faster, like normal hashmap.
This is the code:
package Map;
public class HashMap<K, V> {
private Entry<K, V>[] table; // Array of Entry.
private int capacity = 4; // Initial capacity of HashMap
static class Entry<K, V> {
K key;
V value;
Entry<K, V> next;
public Entry(K key, V value, Entry<K, V> next) {
this.key = key;
this.value = value;
this.next = next;
}
}
#SuppressWarnings("unchecked")
public HashMap() {
table = new Entry[capacity];
}
/**
* Method allows you put key-value pair in HashMapCustom. If the map already
* contains a mapping for the key, the old value is replaced. Note: method
* does not allows you to put null key though it allows null values.
* Implementation allows you to put custom objects as a key as well. Key
* Features: implementation provides you with following features:- >provide
* complete functionality how to override equals method. >provide complete
* functionality how to override hashCode method.
*
* #param newKey
* #param data
*/
public void put(K newKey, V data) {
if (newKey == null)
return; // does not allow to store null.
// calculate hash of key.
int hash = hash(newKey);
// create new entry.
Entry<K, V> newEntry = new Entry<K, V>(newKey, data, null);
// if table location does not contain any entry, store entry there.
if (table[hash] == null) {
table[hash] = newEntry;
} else {
Entry<K, V> previous = null;
Entry<K, V> current = table[hash];
while (current != null) { // we have reached last entry of bucket.
if (current.key.equals(newKey)) {
if (previous == null) { // node has to be insert on first of
// bucket.
newEntry.next = current.next;
table[hash] = newEntry;
return;
} else {
newEntry.next = current.next;
previous.next = newEntry;
return;
}
}
previous = current;
current = current.next;
}
previous.next = newEntry;
}
}
/**
* Method returns value corresponding to key.
*
* #param key
*/
public V get(K key) {
int hash = hash(key);
if (table[hash] == null) {
return null;
} else {
Entry<K, V> temp = table[hash];
while (temp != null) {
if (temp.key.equals(key))
return temp.value;
temp = temp.next; // return value corresponding to key.
}
return null; // returns null if key is not found.
}
}
public boolean containsKey(K key) {
int hash = hash(key);
if (table[hash] == null) {
return false;
} else {
Entry<K, V> temp = table[hash];
while (temp != null) {
if (temp.key.equals(key))
return true;
temp = temp.next; // return value corresponding to key.
}
}
return false;
}
/**
* Method removes key-value pair from HashMapCustom.
*
* #param key
*/
public boolean remove(K deleteKey) {
int hash = hash(deleteKey);
if (table[hash] == null) {
return false;
} else {
Entry<K, V> previous = null;
Entry<K, V> current = table[hash];
while (current != null) { // we have reached last entry node of
// bucket.
if (current.key.equals(deleteKey)) {
if (previous == null) { // delete first entry node.
table[hash] = table[hash].next;
return true;
} else {
previous.next = current.next;
return true;
}
}
previous = current;
current = current.next;
}
return false;
}
}
/**
* Method displays all key-value pairs present in HashMapCustom., insertion
* order is not guaranteed, for maintaining insertion order refer
* LinkedHashMapCustom.
*
* #param key
*/
public void display() {
for (int i = 0; i < capacity; i++) {
if (table[i] != null) {
Entry<K, V> entry = table[i];
while (entry != null) {
System.out.print("{" + entry.key + "=" + entry.value + "}" + " ");
entry = entry.next;
}
}
}
}
/**
* Method implements hashing functionality, which helps in finding the
* appropriate bucket location to store our data. This is very important
* method, as performance of HashMapCustom is very much dependent on this
* method's implementation.
*
* #param key
*/
private int hash(K key) {
return Math.abs(key.hashCode()) % capacity;
}
}
And when i try put big data, like:
HashMap<Integer,String> map = new HashMap();
long startTime = System.currentTimeMillis();
for(int i = 0 ; i < 200000000; i++){
map.put(i, "kotek"+i);
}
System.out.println(System.currentTimeMillis() - startTime);
It take too long time. I must implement it without another collections like: Set etc. I must put, remove, get and containsKey faster, like normal hashMap, but i don't know how implement that fast map.
The reason why your map is slow is because you have many collisions. Your capacity is 4 and you never expand it. So effectively the put() operation becomes approximately O(N) after the 4 first put() calls. Also as William mentioned, you add your new entries on the end of the bucket. So changing that to adding as the first element will boost the performance . But still it's not a good practice to keep your map in constant size of 4 - since your put() will be fine but get() will still be O(N)
EDIT
You cannot prepend the entry. Since you must go over all the entries on the bucket to make sure you dont already have an equal key

How and when check whether key is null in a hashtable

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.

How does the return type of the put method works in Hashtable or HashMap?

In the documentations, the return type for the HashMap or Hashtable is the value itself as following,
public V put(K key, V value)
and, it tells about the return type is the previous value associated with key, or null if there was no mapping for key. (A null return can also indicate that the map previously associated null with key.)
Say, if I write a Hashtable as following,
Hashtable<Integer, Character> hash = new Hashtable<Integer, Character>();
System.out.println(hash.put(12, 'c'));
Now, the above code returns null. I'm implementing a Hashtable and I'm slightly confused when to return null or V where V will be the previous value associated with key. My code is as following,
// for dealing w/ the table data
class HashEntry<K, V> {
private final K key;
private V value;
public HashEntry(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
}
class MyHashTable <K, V> {
private int size;
private int DEFAULT_CAPACITY = 16;
private HashEntry<K, V>[] values = new HashEntry[DEFAULT_CAPACITY];
public V put(K key, V value) {
boolean insert = true;
for (int i = 0; i < size; i++) {
if (values[i].getKey().equals(key)) {
values[i].setValue(value);
insert = false;
}
}
if (insert) {
// method for check or increase capacity
increaseCapacity();
values[size++] = new HashEntry<K, V>(key, value);
}
return null;
// return value;
}
When should I return null or value ( type V) in the implementation ?
In your example this is your first time assigning a value to the key 12 so as stated in the documentation null is returned since there was no previous value stored for that key.
If you then did:
System.out.println(hash.put(12, 'd'));
'c' would be printed because it is returned as the previous value for the key 12.
Edit based on additional info in question:
Here is a possible implementation of your put method to return the previous value correctly.
public V put(K key, V value) {
boolean insert = true;
V prevValue = null;
for (int i = 0; i < size; i++) {
if (values[i].getKey().equals(key)) {
prevValue = values[i].getValue();
values[i].setValue(value);
insert = false;
}
}
if (insert) {
// method for check or increase capacity
increaseCapacity();
values[size++] = new HashEntry<K, V>(key, value);
}
return prevValue;
}

HashMap Duplicate Values - Identify the duplicates

I understand that HashMap doesn't allow insertion of duplicate values and it replaces the last duplicate value with the latest entry.
Is there a way to print the duplicates which were found during the put method?
I have the following code snippet:
for( int i = 0; i <= elements.length - 1; i++) {
nodeDBList = (NodeList) xPath.compile(elements[i]).evaluate(dbDocument, XPathConstants.NODESET);
for (int j = 0; j < nodeDBList.getLength(); j++) {
if(nodeDBList.item(j).getFirstChild() != null)
dbList.put(nodeDBList.item(j).getFirstChild().getNodeValue().toLowerCase().trim(),
nodeDBList.item(j).getNodeName().toLowerCase().trim());
}
}
Wrong. HashMap does not support duplicate keys, which are hashed.
Duplicate values are totally acceptable for different keys.
You can search for existing values by iterating them through the values() method and using the equals method.
Edit
There seems to be a confusion between keys and values here.
According to the HashMap implementation of Map's public V put(K key, V value);, the method put will return the original value for a given key if any, or null.
Quote from the API
#return the previous value associated with key, or null if there was
no mapping for key. (A null return can also indicate that the map
previously associated null with key.)
Well, the answer can be found in the API description of HashMap: The put method returns the value that was previously associated with the key.
Returns:
the previous value associated with key, or null if there was no mapping for key. (A null return can also indicate that the map
previously associated null with key.)
The old value of the key is returned by the put method, so you can output it.
Assuming the value of your HashMap is of type String :
for( int i = 0; i <= elements.length - 1; i++)
{
nodeDBList = (NodeList) xPath.compile(elements[i]).evaluate(dbDocument, XPathConstants.NODESET);
for (int j = 0; j < nodeDBList.getLength(); j++) {
if(nodeDBList.item(j).getFirstChild() != null) {
String oldVal = dbList.put(nodeDBList.item(j).getFirstChild().getNodeValue().toLowerCase().trim(), nodeDBList.item(j).getNodeName().toLowerCase().trim());
if (oldVal != null) {
System.out.println(oldVal);
}
}
}
}
Override the HashMap
this is an example
public class MyMap<K, V> extends HashMap<K,V> {
private static final long serialVersionUID = -1006394139781809796L;
#SuppressWarnings({ "unchecked" })
#Override
public V put(K key, V value) {
if (value == null) {
return super.put(key, value);
}
if (value.getClass() == Timestamp.class) {
DateFormat dateTimeFormatter;
dateTimeFormatter = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, getLocale());
super.put((K) (key + "_f"), (V) dateTimeFormatter.format(new Date(((Timestamp) value).getTime())));
DateFormat dateFormatter;
dateFormatter = DateFormat.getDateInstance(DateFormat.SHORT, getLocale());
super.put((K) (key + "_f_date"), (V) dateFormatter.format(new Date(((Timestamp) value).getTime())));
}
if (value.getClass() == java.sql.Date.class) {
DateFormat dateFormatter;
dateFormatter = DateFormat.getDateInstance(DateFormat.SHORT, getLocale());
super.put((K) (key + "_f"), (V) dateFormatter.format(new Date(((java.sql.Date) value).getTime())));
}
return super.put(key, value);
}
}

NullPointerException when using putAll(map) to put map into properties

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.

Categories