how to handle null pointer in iteration with null key - java

In a recent interview, question I faced was
How can you handle null pointer when iterator encounters a null key
while iteration over a hashmap. assume my developer has inserted a
null key by mistake
My answer was: Just we need to check null!=entry.getKey().
he was not satisfied and said before that how will you handle.
How should I answer that question. When I was back.
I tried this
public class Main {
public static void main(String[] args) {
Map<String,String> map = new HashMap<String,String>();
map.put(null, "null");
map.put("null",null);
map.put("test", "test");
Iterator<Entry<String, String>> it = map.entrySet().iterator();
while(it.hasNext()){
System.out.println(it.next().getKey());
}
}
}
output:
null
test
null
there is no exception. What was he trying to ask actually. or is that I am missing some concepts? Please guide me

map.remove(null)
From JavaDocs:
Returns the value to which this map previously associated the key, or null if the map contained no mapping for the key.
If this map permits null values, then a return value of null does not necessarily indicate that the map contained no mapping for the key; it's also possible that the map explicitly mapped the key to null.
The map will not contain a mapping for the specified key once the call returns.
Call it before iteration, and it will remove explicit null keys

I guess the key point that he want to test by this question is What happend when called the method next() on the object Iterator.
He maybe want to know whether or not you will programming like the following way when you want to iterate a map:
Map map = new HashMap();
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
if (null != iter.next().key()) {
System.out.println(iter.next());
}
}
If you programming like above example, the next method will called twice, and you will not get the correct object that you really want to check. Instead, you should write code like this:
Map map = new HashMap();
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
// Key point: define a refreence point to the object returned by the
// method `next()`
Entry entry = iter.next();
if (null != entry.key()) {
System.out.println(entry);
}
}

Related

How to check if an element has been removed from a Map

How can I check that an element has been properly removed from a Map in java?
Given:
ConcurrentHashMap<Integer,Object> myMap = new ConcurrentHashMap<>();
myMap.put(1, new Object());
myMap.remove(1);
Is the below code the only way to check it?
myMap.contains(1);
remove() returns the removed object, or null if it is not present in the map:
Returns:
the previous value associated with key, or null if there was no mapping for key.
Object removedObject = myMap.remove(1);
boolean wasRemoved = removedObject != null;
In fact by using, contains(), the object might have been removed by another thread between the calls to remove() and contains(), if the map is shared between threads. That your question uses a ConcurrentHashMap hints that concurrency might be a case here.

Why does this throw Concurrent Modification Exception

The following code works fine when there are more than one modification in a particular map. But when there is only one modification it throws concurrent modification exception
for(Map.Entry<String, List<String>> mapEntry : beanMap.entrySet()) {
for(String dateSet : dateList) {
String mName = mapEntry.getKey();
boolean dateFound = false;
if(beanMap.containsKey(dateSet)) {
dateFound = true;
System.out.println(" Found : "+mapEntry.getKey());
}
if(!dateFound)
{
Map<String, List<String>> modifiedMap = beanMap;
List<String> newBeanList = new ArrayList<String>();
dBean beanData = new Bean(dateSet+"NA","NA","NA",0,0,0);
newBeanList.add(beanData);
System.out.println(" Adding : "+dateSet+" "+"NA");
modifiedMap.put(mName, newBeanList);
}
}
}
In the above code it throws ConcurrentModificationException when modifying the "modifiedMap" only once. May be there is more to it but couldn't find out why.
When you use an enhanced for loop, there is an implicit Iterator working behind the scenes. You attempt to make a copy of beanMap with this line:
Map<String, List<String>> modifiedMap = beanMap;
However, this only creates another reference variable that also refers to the same map object. There is still only one map, and you are modifying it:
modifiedMap.put(mName, newBeanList);
The Iterator then detects that the map is modified when it attempts to iterate to the next entry, resulting in the ConcurrentModificationException.
You can create another Map with new, and put all your modifications into that map while you're iterating the original map.
After you're done iterating the original map, you can call the putAll method on it, passing your new map, to apply all of the modifications you want.
You are not allowed to change the underlying collection while iterating over it using this syntax. The collections are implemented in a fail-fast way, so even a single change will raise the exception.
If you need to change the collection while visiting the elements, use an Iterator.
modifiedMap is the reference to same Map beanMap on which you are iterating. You are modifying the Collection modifiedMap while iteration hence the Exception.

Java HashMap add new entry while iterating

In a HashMap
map = new HashMap<String,String>();
it = map.entrySet().iterator();
while (it.hasNext())
{
entry = it.next();
it.remove(); //safely remove a entry
entry.setValue("new value"); //safely update current value
//how to put new entry set inside this map
//map.put(s1,s2); it throws a concurrent access exception
}
When i trying to add a new entry to map it throws ConcurrentModificationException. For remove and update iterator has safely removing methods. How to add new entry while iterating?
You need to consider what it means to put a value to a Map whilst iterating. HashMap defines no order over which its entries will be iterated over. So when you put a new entry, should the entry be returned by the iterator later or not. Consistency of behaviour is important. However, whichever way you decide you'll get inconsistent behaviour when you put a new value to a preexisting key. If the key has already been iterated over then the change won't appear and will appear if the key has yet to be produced by the iterator.
A simple way to overcome this problem is to create a temporary Map of the new key-value pairs and add the temporary Map to the main Map at the end of your iteration.
Map<String,String> values = ...
Map<String,String> temp = new HashMap<>();
for (Entry<String,String> entry : values.entrySet()) {
if ("some value".equals(entry.getValue()) {
temp.put(entry.getValue(), "another value");
}
}
values.putAll(temp);
You need to use ConcurrentHashMap to add elements while iterating the collection. HashMap uses fail-fast iterator, which throws ConcurrentModificationException when the collection is updated while iterating. Whereas ConcurrentHashMap uses fail-safe iterator, which basically works on the clone of the underlying collection and hence allows modification while iterating.
How about:
map = new HashMap<String,String>();
it = map.entrySet().iterator();
while (it.hasNext())
{
entry = it.next();
entry.setValue("new value"); // update current value
}
I checked the HashMap implementation, it does not change its modification count when updating an entry like this. I also see no reason why this shouldn't be allowed. Nothing is removed, nothing is added and no keys are changed.
What I did was to create an array with the current elements and then iterate through the array:
Feature[] initialFeatures = featureMap.values().toArray(new Feature[featureMap.values().size()]);
for (Feature feature : initialFeatures)
{/* Code that can safely add to the featureMap */}

Java - Iteration over HashMap replacing ArrayList

Previously I set up an ArrayList to contain objects of records. I have since replaced the ArrayList with a HashMap where the objects are stored and use the username string of an individual as the key.
The class of this directory implemented Iterable through implements Iterable<Object> (just a one off question, but why is <Object> required?).
The previous code used to iterate over the ArrayList was:
public Iterator iterator() {
return records.iterator();
}
I then used this iterator for all objects in that class as follows:
for (Object o : directory) {
TelephoneRecords temp = (TelephoneRecords) o;
System.out.println(temp.toString());
}
Unfortunately, the HashMapName.iterable(); seems to raise issues, so how do I go about this behaviour with a HashMap?
If you're only interested in the keys, you can iterate through the keySet() of the map:
Map<String, Object> map = ...;
for (String key : map.keySet()) {
// ...
}
If you only need the values, use values():
for (Object value : map.values()) {
// ...
}
Finally, if you want both the key and value, use entrySet():
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
// ...
}
Hope this helps you.
You can iterate over the entrySet of the HashMap. The entrySet contains the sets of keys and values. The keys and values are then accessible through getKey() and getValue(). This can be done by the following code:
for (Map.Entry<String, Object> entry : map.entrySet()) {
String username = entry.getKey();
TelephoneRecord record = (TelephoneRecord)entry.getValue();
// Do something with username and record ...
}
Your off-question:
Object is a type parameter for the HashMap, which says that the Iterable contains Objects. If your HashMap is only supposed to contain TelephoneRecords objects, replace
implements Iterable<Object>
with
implements Iterable<TelephoneRecords>
That way you're saying that your Iterable contains TelephoneRecords, which in turn lets you avoid casting and get compile errors instead of runtime errors if you're doing something wrong (which is prefered!). That would improve the above code to:
for (Map.Entry<String, TelephoneRecord> entry : map.entrySet()) {
String username = entry.getKey();
TelephoneRecord record = entry.getValue();
// Do something with username and record ...
}
What does "seems to raise issues" mean? In a HashMap, you can iterate over keys (map.keySet()), values (map.values()) or key-value pairs (map.entrySet()).
I solved my issue with replacing records.iterator(); (which didn't work) with records.values().iterator();. It seems you cannot iterate directly over a HashMap but you can iterate over the values (objects) within it.
Furthermore, the issue of getting and printing the contents of the HashMap, I solved through the following code. It iterates over the TelephoneRecords objects within the directory, as specified by TelephoneRecords o : directory and the Iterator method within the directory class. Then a temporary object is assigned to the TelephoneRecords object being iterated over, and the [custom] toString() method used to print out the string of that specific TelephoneRecords object.
for (TelephoneRecords o : directory) {
TelephoneRecords temp = (TelephoneRecords) o;
System.out.println(temp.toString());
}
And I ended up solving my little side question by following the help provided, using Iterator<TelephoneRecords> (rather than simply using Object) to iterate over the TelephoneRecords objects contained within the directory object.
You can use the entryset from the hashmap, then iterate over that in the same manner as you do already e.g.
for (Object o : Directory.getEntrySet()) {
}
Also, if you type your hashmap it will remove the need for the cast -
Map<String, TelephoneRecords>

Java concurrent modification exception

I have written following code which is resulting in concurrent modification exception. How can I prevent it ? The idea is to escape all values of the Map and reconstruct the object (dO) back with new param map.
try {
Map<String,String[]> paramMap = dO.getParameterMap();
Set<Map.Entry<String, String[]>> entries = paramMap.entrySet();
Iterator<Map.Entry<String, String[]>> it = entries.iterator();
while (it.hasNext()) {
Map.Entry<String, String[]> entry = it.next();
String[] values = entry.getValue();
List<String> valList = new ArrayList<String>();
if (values != null) {
for (String value : values) {
valList.add(escapeHTML(value));
}
dO.removeParameter(entry.getKey());
//Please note that Parameter is a hashMap so , Is it required to remove the entry first before inserting or it will replace the new value associated with key . How it works in Java ?
dO.addParameter(entry.getKey(),valList.toArray(new String[valList.size()]));
}
}
}
the exception is thrown because you are adding/removing things from the map while you are iterating it:
dO.removeParameter(entry.getKey());
dO.addParameter(entry.getKey(),valList.toArray(new String[valList.size()]
you should use iterator.remove() instead.
Not sure you need to alter the keys of Map, it appears all you want to do is alter the values in the arrays.
for(String[] values: dO.getParameterMap().values())
for (int i = 0; i < values.length; i++)
values[i] = escapeHTML(values[i]);
I would make sure the Map does have null values stored. But if you can't change this you will need to add if(values != null)
You should remove/add only if you are changing a key in a map. As I see in the code, you are changing only value. Hence you could use entry.setValue(...) instead.
ConcurrentModificationException is thrown when you use fail-fast iterators (eg: entries.iterator() is fail-fast iterator). What they do is they iterate over original collection object.
To be able to modify and iterate over a collection object you can use fail-safe iterator (eg: List<Book> books = new CopyOnWriteArrayList<>()) This will take a copy inside memory while iterating over the elements and even you will be able to modify it.

Categories