Replace/Changing HashMap items during iteration - java

I get Concurrent Modification Exception when i try to remove an item from HashMap. I know that removing items during iteration through HashMap will trigger this exception , but i need to replace older item with new one. How can i do this ? Maybe to create a copy of countNumberOfEachCharacter HashMap , and whe iterate through original HashMap to remove item from copy HashMap ?
countNumberOfEachCharacter = new HashMap<Character,Character>();
if (countNumberOfEachCharacter.containsKey(word.charAt(i))) {
System.out.println("This character already exists");
for (Iterator it = countNumberOfEachCharacter.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
Object key = entry.getKey();
Object value = entry.getValue();
if (key.equals(word.charAt(i))) {
int toIncrease = Integer.parseInt(value.toString());
toIncrease++;
System.out.println("key "+key);
System.out.println("increased "+toIncrease);
countNumberOfEachCharacter.remove(word.charAt(i));
char c = Character.forDigit(toIncrease, 10);
countNumberOfEachCharacter.put(word.charAt(i),c);
}
}
}
else {
System.out.println("First time found this character");
char c = Character.forDigit(1, 10);
countNumberOfEachCharacter.put(word.charAt(i),c);
System.out.println("Stored "+word.charAt(i)+" with count "+c);
}

While iterating over a Collection, you can only remove elements by using the Iterator#remove method. This is also documented in the class javadoc of HashMap
The iterators returned by all of this class's "collection view
methods" are fail-fast: if the map is structurally modified at any
time after the iterator is created, in any way except through the
iterator's own remove method, the iterator will throw a
ConcurrentModificationException. Thus, in the face of concurrent
modification, the iterator fails quickly and cleanly, rather than
risking arbitrary, non-deterministic behavior at an undetermined time
in the future
Furthermore for what you are trying to do (=update a value) you do not have to remove it. Just call put with that key and an updated value, which will update the value, as documented in the javadoc of the HashMap#put method
Associates the specified value with the specified key in this map. If
the map previously contained a mapping for the key, the old value is
replaced.

...but i need to replace older item with new one
I take it from "replace" (and from the code you've quoted) that the key remains the same, it's just the value that differs. If so, I don't believe calling setValue on Map.Entry objects causes a ConcurrentModificationException, so you could do that.
Update: Just tested it, and indeed, it works:
import java.util.*;
public class ReplaceMapEntryValue {
public static final void main(String[] args) {
Map m;
Iterator<Map.Entry> it;
Map.Entry entry;
// Create
m = new HashMap();
m.put("a", "alpha");
m.put("b", "beta");
// Update
it = m.entrySet().iterator();
while (it.hasNext()) {
entry = it.next();
if (entry.getKey() == "b") {
entry.setValue("bravo");
}
}
// Show
it = m.entrySet().iterator();
while (it.hasNext()) {
entry = it.next();
System.out.println("key = " + entry.getKey() + ", value = " + entry.getValue());
}
// Done
System.exit(0);
}
}

The whole point of a map is that you can lookup by key, you don't have to examine every entry.
Map<Character, AtomicInteger> countNumberOfEachCharacter = new TreeMap<Character, AtomicInteger>();
String word = "the quick brown fox jumps over the lazy dog";
for (int i = 0; i < word.length(); i++) {
AtomicInteger count = countNumberOfEachCharacter.get(word.charAt(i));
if (count == null)
countNumberOfEachCharacter.put(word.charAt(i), new AtomicInteger(1));
else
count.incrementAndGet();
}
System.out.println("Character count: " + countNumberOfEachCharacter);
prints
Character count: { =8, a=1, b=1, c=1, d=1, e=3, f=1, g=1, h=2, i=1, j=1, k=1, l=1, m=1, n=1, o=4, p=1, q=1, r=2, s=1, t=2, u=2, v=1, w=1, x=1, y=1, z=1}
However since you have a small, fixed number of possible characters you don't even need to use a Map
int[] countNumberOfEachCharacter = new int[Character.MAX_VALUE + 1];
String word = "the quick brown fox jumps over the lazy dog";
for (int i = 0; i < word.length(); i++)
countNumberOfEachCharacter[word.charAt(i)]++;
System.out.print("Character count: ");
for (int i = 0; i < countNumberOfEachCharacter.length; i++)
if (countNumberOfEachCharacter[i] > 0)
System.out.print(" " + (char) i + "=" + countNumberOfEachCharacter[i]);
System.out.println();
prints
Character count: =8 a=1 b=1 c=1 d=1 e=3 f=1 g=1 h=2 i=1 j=1 k=1 l=1 m=1 n=1 o=4 p=1 q=1 r=2 s=1 t=2 u=2 v=1 w=1 x=1 y=1 z=1

Related

It puts wrong null value from String array in HashMap

i am a newbie in Java (coming from JavaScript developing on Adobe LiveCycle) and facing the following problem:
I have a String array with several items. I want to put only the items with the value "a" to a HashMap. But instead of 3 "a" values in the HashMap i get 1 null value there. Why is that?
String[] s = {"a", "a", "b", "a"};
Map m = new HashMap();
for (int i = 0; i < s.length; i++) {
if (s[i].equals("a")) {
m.put(i, s[i]);
}
}
for (int i = 0; i < m.size(); i++) {
System.out.println(m.get(i));
}
// Prints
//a
//a
//null
You are putting the items in the map with key 0, 1 and 3.
You are taking them out with key 0, 1, an 2.
Use:
for (Object o : m.keySet()) {
System.out.println(m.get(o));
}
or - better:
Map<Integer, String> m = new HashMap<>();
...
for (Integer i : m.keySet()) {
System.out.println(i + " -> " + m.get(i));
}
You put the items with their corresponding index in array s in the Map, i.e. you have a Map with content {0=a, 1=a, 3=a}. Therefore if you try to access the map with key 2 (m.get(2)), you get a null since key 2 is not found in m.
Instead of using a for-loop over m's size, I recommend iteration over m's keySet() via a foreach-loop:
for (Object key : m.keySet()) {
System.out.println("key: " + key + ", value: " + m.get(key));
}
On a sidenote: you are using raw types. You should bind the types of the Map and HashMap properly (see the Javadoc of Map for details): Map<Integer, String> m = new HashMap<Integer, String>();. With properly bound types, key in the for-loop can be of type int or Integer. I recommend type Integer to avoid unnecessary Auto(un)boxing.
Your code is working correctly, but you are accessing it not correctly.
String[] s = {"a", "a", "b", "a"};
for (int i = 0; i < s.length; i++) {
if (s[i].equals("a")) {
m.put(i, s[i]);
}
}
This puts it like this
First iteration : m.put(0, "a");
Second iteration : m.put(1, "a");
Third iteration : "b" doest not equal "a" but still counts the index i up
Fourth iteration: m.put(3, "a");
Apart from the other answers you can still use your range based loop and access it with an Iterator
Iterator<String> it = m.values().iterator();
while (it.hasNext()) {
System.out.println(it.next());
}

How To Find 2nd or any other order,say 3rd, 4th, non repeating character in a string?

Suppose i have a string named as " statistic ".
I want to print "c" as the 2nd non repeating character as the first character is a.
I did printed the first character but couldn't find way to traverse another keys through that?
Also, If it is given "statistic non" , can i get " c and o" too as all non repeating if, specified, leaving first and also, if specified, " a,c and o" taking all??
How can I achieve that?
Simply iterate over the string and put the occurrence of each character in a map with the key is the character, and the value is an object which has two attributes the number of occurence and the first occurence position.
Then you simple sort the keySet() of this map depending on the position and iterate over the sorted set till u find an object with only one occurence
for(int i=0;i<str.length();i++) {
if(map.get(str.chatAt(i))==null) {
map.put(str.charAt(i),new Wrapper(i,1)) ;
} else {
map.get(str.charAt(i)).occurence++;
}
}
class Wrapper {
public int occurrence;
public int position;
public Wrapper(int position,int occurrence) {
this.position = position;
this.occurrence = occurrence;
}
}
Use LinkedHashMap
Hash table and linked list implementation of the Map interface, with predictable iteration order. ... This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map (insertion-order). Note that insertion order is not affected if a key is re-inserted into the map.
With this, it becomes easy:
// We keep only a map of between tha char and the number of
// occurrences. We don't store the positions for the next (after first)
// occurrences, but the LinkedHashMap guarantees that
// the iteration order is the same as insertion order
// (so first inserted letters will appear first when iterating)
LinkedHashMap<Character, Integer> charOccurs=new LinkedHashMap<>();
for(char c : str) {
Integer occ=charOccurs.get(c);
if(null==occ) {
occ=0;
}
else {
occ=new Integer(occ.intValue()+1);
}
// "Note that insertion order is not affected
// if a key is re-inserted into the map."
// Niiice!
charOccurs.put(c, occ);
}
int order=1;
for(Map.Entry<Character, Integer> entry : charOccurs.entrySet()) {
// this works because ...
// "This linked list defines the iteration ordering,
// which is normally the order in which keys were inserted"
// Doubly-nice!!!
if(1==entry.getValue()) { // single occurrence
System.out.println("Char: "+entry.getKey()+" order: "+order);
order++;
}
}
I think you will like to go through old skool method
String intitialString = "statistic non";
int count = 0;
char[] ch = intitialString.toCharArray();
for (int i = 0; i < ch.length; i++) {
for (int j = 0; j < ch.length; j++) {
if (ch[i] == ch[j]) {
count++;
}
}
if (count > 1) {
count = 0;
} else
System.out.println(ch[i]);
}
output
a
c
o
Hope this helps!
The following code snippets help to identify the nonrepeating character in the string and will return the 2nd one on that.
public String printNonRepeatingChar(String data){
LinkedHashMap<String, Integer> counter = new LinkedHashMap<>();
if(data!=null) {
for(int i=0;i<data.length();i++) {
String key = data.charAt(i)+"";
if(counter.keySet().contains(key)) {
Integer value = counter.get(key);
value+=1;
counter.put(key, value);
}else {
counter.put(key, 1);
}
}
}
List<String> elements = new LinkedList<>();
for(String key:counter.keySet()) {
if(counter.get(key)==1) {
elements.add(key);
}
}
return elements.get(1);
}
Change the index position from 0 to length in the return statement for the nonrepeating character position. please handle the exception if index is not found.
Thanks for reading.

counting occurance of characters using hashtable in Java

I am trying to count occurrence of each character in a string.
So if I input aaabbbccc I am expecting o/p as {a=3, b=3, c=3} but instead I keep on getting it as {a=1, b=1, c=1} as my hashtable contains method fails and returns false. What is wrong in the code?
I also know that there is a HashMap collection quite similar to hashtable. but as I am learing java I want to try the same code with all datastructures just to get an idea of methods. The code with map is not much different than what I am doing here still this code fails. and I am not able to figure out where is the bug in the code.
I have following code:
Hashtable<Character, Integer> stringHash = new Hashtable<Character, Integer>();
This stringHash is a class level variable.
for(int i=0; i<s.length(); i++){
if(stringHash ==null || stringHash.size()==0){
stringHash.put(s.charAt(i), 1);
}
else{
if(! stringHash.contains(s.charAt(i)) ){
System.out.println(s.charAt(i));
stringHash.put(s.charAt(i), 1);
}
else{
int count = stringHash.get(s.charAt(i));
stringHash.put(s.charAt(i), count++);
}
}
System.out.println(stringHash + " " + s.charAt(i) + " "+ stringHash.contains(s.charAt(i)));
}
This code works for me:
String s = "aaabbbccc";
Map<Character, Integer> stringHash = new HashMap<Character, Integer>();
for (char ch : s.toCharArray())
stringHash.put(ch, stringHash.containsKey(ch) ? (stringHash.get(ch) + 1) : 1);
System.out.println(stringHash);
// output: "{a=3, b=3, c=3}"
I am using a Map<K, V> instead of HashTable<K, V>, but this is more common.
Try something like this....The reason your code is failing is that you are checking contains() on HashTable instead of its keySet. Hope that helps
public static void main(String[] args) {
// TODO Auto-generated method stub
String s = "aaaaabbcccc";
Hashtable<Character, Integer> counter = new Hashtable<Character, Integer>();
int count = 0;
for(int i=0;i<s.length();i++){
if(!counter.keySet().contains(s.charAt(i))){
counter.put(s.charAt(i), 1);
} else {
count = counter.get(s.charAt(i));
counter.put(s.charAt(i), ++count);
}
}
for(char c:counter.keySet()) {
System.out.println("Character : "+c+" - Occurences : "+counter.get(c));
}
}
o/p
Character : b - Occurences : 2
Character : c - Occurences : 4
Character : a - Occurences : 5
Your code
if(stringHash ==null || stringHash.size()==0){
stringHash.put(s.charAt(i), 1);
}
would throw NPE if somehow the hashmap is null. Luckily it seems that you have initialized it properly. The block rather should have been
if(stringHash ==null){
stringHash = new HashMap()
stringHash.put(s.charAt(i), 1);
}
Again, that would not have fixed your bug. You should use containsKey instead of contains that checks for value in HashTable. What you are looking to implement can be summarized in following pseudocode.
initialize hashmap
for each character c in inputString
count = 0
if hashmap has a key for c
count = get value for c from hashmap
end if
put in to hashmap c, count + 1
end for
In Java this would look like :
Map<Character, Integer> charCountMap = new HashMap<>();
for(char c : inputString.toCharArray()){
int count = 0;
if(charCountMap.containsKey(c)){
count = charCountMap.get(c);
}
charCountMap.put(c,count+1);
}
Or for the adventurous, here is Java8 version
Map<Character,Long> map = s.chars().mapToObj(i->(char)i)
.collect(Collectors
.groupingBy(e -> e,
Collectors.counting()));
System.out.println(map);
Finally, do not use HashTable its a legacy class, no one uses it now a days. Stick with HashMap or other flavors of Map implementations.
Debug my code questions are discouraged, but in the way of solving the general problem of counting characters in a string I can suggest a much simpler method:
public static int[] countCharacters( String s ){
int[] count = new int[ 256 ];
for( int xChar = 0; xChar < s.length(); xChar++ ) count[ s.charAt( xChar ) ]++;
return count;
}
This assumes you have single byte characters.
Why do you use hashMap for counting character occurance?
I would use integer array of size 255 like so:
int[] counter = new int[256];
String s = "aaabbbcccc";
for(int i = 0; i < s.length(); i++){
counter[s.charAt(i)]++;
}
for(int i = 0; i < counter.length; i++)
if(counter[i] > 0)
System.out.println(((char)i) + " occurs " + counter[i] + " times");
that coude would give an output:
a occurs 3 times
b occurs 3 times
c occurs 4 times
Don't use Hashtable, you can simplify that code a lot, something like this should work:
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
public class Main {
public static void main(String[] args) {
Map<Character, Long> countMap = count("aabbbcccc");
for (Map.Entry<Character, Long> entry : countMap.entrySet()) {
System.out
.println(MessageFormat.format("Char ''{0}'' with count ''{1}''", entry.getKey(), entry.getValue()));
}
}
private static Map<Character, Long> count(String value) {
Map<Character, Long> countMap = new HashMap<Character, Long>();
if (StringUtils.isNotBlank(value)) {
for (int i = 0; i < value.length(); i++) {
Long count = countMap.get(value.charAt(i));
count = count == null ? 1 : count + 1;
countMap.put(value.charAt(i), count);
}
}
return countMap;
}
}

Better way to loop through the List<String> and remove items

I have a List and I'm looping through it removing items in the List if there is a match.
I'm using i = -1 if the item in the List is removed. But it loops through from the beginning again. Is there a better way to do this?
private List<String> populateList(List<String> listVar) {
List<String> list = new ArrayList<String>();
list.add("2015-01-13 09:30:00");
list.add("2015-01-13 06:22:12");
list.add("2015-01-12 05:45:10");
list.add("2015-01-12 01:52:40");
list.add("2015-01-12 02:23:45");
return list;
}
private void removeItems() {
List<String> list = new ArrayList<String>();
list = populateList(list);
System.out.println("List before modification : "+list);
for (int i = 0; i < list.size(); i++) {
String dateNoTime = list.get(i).split(" ")[0];
System.out.println(" Going over : "+list.get(i));
if(!dateNoTime.equalsIgnoreCase("2015-01-13")) {
System.out.println(" Removing : "+list.get(i));
list.remove(i);
i = -1; //This is making the loop start from scratch. Is there a better way?
}
}
System.out.println("List after modification: "+list+"\n\n");
}
Java's List<T> provides a better way of removing items from it by using ListIterator<T>:
ListIterator<String> iter = list.listIterator();
while (iter.hasNext()) {
String s = iter.next();
String dateNoTime = s.split(" ")[0];
if(!dateNoTime.equalsIgnoreCase("2015-01-13")) {
iter.remove();
}
}
With Java-8, you can simplify the entire thing to:
List<String> filtered = list.stream().filter(item -> item.split(" ")[0]
.equalsIgnoreCase("2015-01-13"))
.collect(Collectors.toList());
I think this is the shortest way to remove items from the list by checking them one-by-one. (I mean 'shortest' in terms of code size, not complexity-wise).
When you're using an index to iterate through a list, and you're removing items from the list, you need to be careful about how you handle the index. (Doing the remove through the iterator as in #dasblinkenlight's answer is better in this case, but there are other similar situations where it's not possible.)
Suppose you just deleted the line that resets i:
for (int i = 0; i < list.size(); i++) {
String dateNoTime = list.get(i).split(" ")[0];
System.out.println(" Going over : "+list.get(i));
if(!dateNoTime.equalsIgnoreCase("2015-01-13")) {
System.out.println(" Removing : "+list.get(i));
list.remove(i);
//i = -1; //This is making the loop start from scratch. Is there a better way?
}
}
Now, when i==2, you decide you need to remove the element. When you do so, the item that was element 3 then becomes element 2, and the element that was element 4 then becomes element 3, and so on.
But then you go back up to the top and increment i. It is now 3. The result is that the element that was element 3, but is now element 2, is never examined at all. It gets skipped.
There are a couple ways to deal with this.
One is to make sure i doesn't get incremented. I've seen good programmers do things like this:
for (int i = 0; i < list.size(); i++) {
String dateNoTime = list.get(i).split(" ")[0];
System.out.println(" Going over : "+list.get(i));
if(!dateNoTime.equalsIgnoreCase("2015-01-13")) {
System.out.println(" Removing : "+list.get(i));
list.remove(i);
i--; // Move "i" backwards so that no elements are skipped
}
}
Personally, I dislike modifying an index inside a for loop like this, so I'd be happier with
int i = 0;
while (i < list.size()) {
String dateNoTime = list.get(i).split(" ")[0];
System.out.println(" Going over : "+list.get(i));
if(!dateNoTime.equalsIgnoreCase("2015-01-13")) {
System.out.println(" Removing : "+list.get(i));
list.remove(i);
} else {
i++;
}
}
[Note that in both cases, it's important to use list.size() in the termination condition, and not save the original value in a variable. The list size will change, and you want to use the new size when checking for termination.]
Another solution that may be appropriate in some cases is to go backward through the list:
for (int i = list.size() - 1; i >= 0; i--)
String dateNoTime = list.get(i).split(" ")[0];
System.out.println(" Going over : "+list.get(i));
if(!dateNoTime.equalsIgnoreCase("2015-01-13")) {
System.out.println(" Removing : "+list.get(i));
list.remove(i);
}
}
which doesn't have the problem when elements are shifted.
You can use Iterator interface which has remove method.
Iterator<String> iterator = list.iterator();
while (iterator.hasNext())
{
String next = iterator.next();
String dateNoTime = next.split(" ")[0];
if(!dateNoTime.equalsIgnoreCase("2015-01-13")) {
System.out.println(" Removing : "+next);
iterator.remove();
}
}
Use Iterators.
Javadoc for Iterator says the following
Iterators allow the caller to remove elements from the underlying collection during the iteration with well-defined semantics.
Or you can use guava API to achieve it.
FluentIterable
.from(list)
.transform(new Function<String, String>(){
#Override
public void apply(String input){
return input.split(" ")[0];
}
}).filter(new Predicate<String>(){
#Override
public boolean apply(String input){
return input.equalsIgnoreCase("2015-01-13");
}
}).toList();

Using HashMaps Java

I'm writing a method that allows me to count how many times an element of type String shows up in a LinkedList of type Strings. my code shown below does not work. I keep getting index out of bounds in the line i commented on down below. Can't seem to find the bug
public int findDuplicate (LinkedList<String> e) {
int j = 1;
LinkedList<String> test = e;
while (!test.isEmpty()){
test = e;
String value = test.pop();
//Screws up here when i = 6
for(int i =0; i<=test.size() && test.get(i)!=null; i++){
String value3 = test.get(i);
if(e.get(i).equals(value) && i<=test.size()){
String value2 = test.get(i);
j++;
String Duplicate = e.get(i);
e.remove(i);
}
}
System.out.println(value + " is listed " + j + " times");
}
return j;
}
using hashmaps.. still doesn't work
public void findDuplicate (LinkedList e) {
Map<String,Integer> counts = new HashMap<String,Integer>();
while(!e.isEmpty()){
String value = e.pop();
for(int i =0; i<e.size(); i++){
counts.put(value, i);
}
}
System.out.println(counts.toString());
}
My code should go through the linked list find out how many times an element within the list appears and deletes duplicates from the list at the same time. Then prints the element and the number of times it appears in the list. I posted about this last night but didn't get a response yet. Sorry for the repost.
You are running off the end of the list. Change
for(int i =0; i<=test.size() && test.get(i)!=null; i++){
to
for(int i =0; i< test.size() && test.get(i)!=null; i++){
Valid indexes for a List (or an array) are 0 through size() - 1.
Regarding your hashmap example to count the duplicates:
#Test
public void countOccurrences() {
LinkedList<String> strings = new LinkedList<String>(){{
add("Fred");
add("Fred");
add("Joe");
add("Mary");
add("Mary");
add("Mary");
}};
Map<String,Integer> count = count(strings,new HashMap<String,Integer>());
System.out.println("count = " + count);
}
private Map<String, Integer> count(List<String> strings, Map<String, Integer> runningCount) {
if(strings.isEmpty()) {
return runningCount;
}
String current = strings.get(0);
int startingSize = strings.size();
while(strings.contains(current)) {
strings.remove(current);
}
runningCount.put(current, startingSize - strings.size());
return count(strings,runningCount);
}
If you want the original strings list preserved you could do
Map<String,Integer> count = count(new LinkedList<String>(strings),new HashMap<String,Integer>());
System.out.println("strings = " + strings);
System.out.println("count = " + count);
Check out google's guava collections which has a perfect class for maintaining a map and getting a count:
https://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#BiMap
Multiset<String> wordsMultiset = HashMultiset.create();
wordsMultiset.addAll(words);
// now we can use wordsMultiset.count(String) to find the count of a word
I hope you realize what the test = e statement is doing. After this statement executes both test and e refer to the same object.
If anyone of them modifies the list, the other sees it as they both are looking at the same object.
If this is not intended you need to clone the list before assigning it to another list reference.
This doesn't affect your out of bounds issue, but you are removing elements from your list while still evaluating it. If you remove an element, you should call i-- afterwards, or you skip the next entity (which is re-indexed) for evaluation.
Also of note regarding your code, I see you are trying to make a copy of your list, but standard assignment means test and e both point to the same instance. You need to use Collections.copy() see this SO thread on how to use the class.

Categories

Resources