I have a Hashtable in Java and want to iterate over all the values in the table and delete a particular key-value pair while iterating.
How may this be done?
You need to use an explicit java.util.Iterator to iterate over the Map's entry set rather than being able to use the enhanced For-loop syntax available in Java 6. The following example iterates over a Map of Integer, String pairs, removing any entry whose Integer key is null or equals 0.
Map<Integer, String> map = ...
Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, String> entry = it.next();
// Remove entry if key is null or equals 0.
if (entry.getKey() == null || entry.getKey() == 0) {
it.remove();
}
}
You can use Enumeration:
Hashtable<Integer, String> table = ...
Enumeration<Integer> enumKey = table.keys();
while(enumKey.hasMoreElements()) {
Integer key = enumKey.nextElement();
String val = table.get(key);
if(key==0 && val.equals("0"))
table.remove(key);
}
You can use a temporary deletion list:
List<String> keyList = new ArrayList<String>;
for(Map.Entry<String,String> entry : hashTable){
if(entry.getValue().equals("delete")) // replace with your own check
keyList.add(entry.getKey());
}
for(String key : keyList){
hashTable.remove(key);
}
You can find more information about Hashtable methods in the Java API
So you know the key, value pair that you want to delete in advance? It's just much clearer to do this, then:
table.delete(key);
for (K key: table.keySet()) {
// do whatever you need to do with the rest of the keys
}
Related
Let's say I have the LinkedHashMap with some unknown data inside.
//==================
Map< Integer, String > map = new LinkedHashMap<>();
map.put(10, "C");
map.put(20, "C++");
map.put(50, "JAVA");
map.put(40, "PHP");
map.put(30, "Kotlin");
//=============
And I know just the key = 50;
I am wondering what is the best way to get the next element to the element that I have by this key (50)? This is not a multi-threaded application. I don't worry about thread-safety.
I don't like the way to iterate all keys through entrySet from the beginning.
It would be great to somehow get access to the next() of LinkedHashMaps Entry.
This is LinkedHashMap so it remembers the order of elements insertion.
public static Map.Entry<Integer, String> getNextEntry(LinkedHashMap<Integer, String> map, Integer key) {
List<Integer> keys = new ArrayList<>(map.keySet());
int index = keys.indexOf(key);
if (index < 0 || index >= keys.size() - 1)
return null;
int k = keys.get(index + 1);
return Map.entry(k, map.get(k));
}
Or you can use Iterator:
public static Map.Entry<Integer, String> getNextEntry(LinkedHashMap<Integer, String> map, Integer key) {
boolean found = false;
for (Map.Entry<Integer, String> entry : map.entrySet()) {
if (found)
return Map.entry(entry.getKey(), entry.getValue());
if (entry.getKey().intValue() == key)
found = true;
}
return null;
}
LinkedHashMap doesn't offer a functionality which would allow finding the next key or entry.
In case if you simply don't want to bother with managing iteration yourself manually, then sure you can alternate this process, but keep in mind that the iteration should happen somewhere anyway.
Stream API
Alternatively you can make use of the Stream API if you don't want to bother with loops.
public static Optional<Map.Entry<Integer, String>> getNextEntry(Map<Integer, String> map,
int previous) {
return map.entrySet().stream()
.dropWhile(entry -> entry.getKey() != previous) // discard the entries, until the target key has been encountered
.skip(1) // skip the entry with the target key
.findFirst(); // grab the next entry and return it as an Optional (because the next entry might not exist)
}
TreeMap
However, you would be able to navigate through the keys of the map if you were using a TreeMap.
TreeMap maintains a red-black tree under the hood, and it keep the entries in sorted order based on keys. And it offers various method like higherEntry(), higherKey().
NavigableMap<Integer, String> map = new TreeMap<>();
// populating the map
int key = 50;
Map.Entry<Integer, String> next = map.higherEntry(key);
This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 5 years ago.
I was doing:
for (Object key : map.keySet())
if (something)
map.remove(key);
which threw a ConcurrentModificationException, so i changed it to:
for (Object key : new ArrayList<Object>(map.keySet()))
if (something)
map.remove(key);
this, and any other procedures that modify the map are in synchronized blocks.
is there a better solution?
Here is a code sample to use the iterator in a for loop to remove the entry.
Map<String, String> map = new HashMap<String, String>() {
{
put("test", "test123");
put("test2", "test456");
}
};
for(Iterator<Map.Entry<String, String>> it = map.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, String> entry = it.next();
if(entry.getKey().equals("test")) {
it.remove();
}
}
As of Java 8 you could do this as follows:
map.entrySet().removeIf(e -> <boolean expression>);
Oracle Docs: entrySet()
The set is backed by the map, so changes to the map are reflected in the set, and vice-versa
Use a real iterator.
Iterator<Object> it = map.keySet().iterator();
while (it.hasNext())
{
it.next();
if (something)
it.remove();
}
Actually, you might need to iterate over the entrySet() instead of the keySet() to make that work.
is there a better solution?
Well, there is, definitely, a better way to do so in a single statement, but that depends on the condition based on which elements are removed.
For eg: remove all those elements where value is test, then use below:
map.values().removeAll(Collections.singleton("test"));
UPDATE
It can be done in a single line using Lambda expression in Java 8.
map.entrySet().removeIf(e-> <boolean expression> );
I know this question is way too old, but there isn't any harm in updating the better way to do the things :)
ConcurrentHashMap
You can use java.util.concurrent.ConcurrentHashMap.
It implements ConcurrentMap (which extends the Map interface).
E.g.:
Map<Object, Content> map = new ConcurrentHashMap<Object, Content>();
for (Object key : map.keySet()) {
if (something) {
map.remove(key);
}
}
This approach leaves your code untouched. Only the map type differs.
Java 8 support a more declarative approach to iteration, in that we specify the result we want rather than how to compute it. Benefits of the new approach are that it can be more readable, less error prone.
public static void mapRemove() {
Map<Integer, String> map = new HashMap<Integer, String>() {
{
put(1, "one");
put(2, "two");
put(3, "three");
}
};
map.forEach( (key, value) -> {
System.out.println( "Key: " + key + "\t" + " Value: " + value );
});
map.keySet().removeIf(e->(e>2)); // <-- remove here
System.out.println("After removing element");
map.forEach( (key, value) -> {
System.out.println( "Key: " + key + "\t" + " Value: " + value );
});
}
And result is as follows:
Key: 1 Value: one
Key: 2 Value: two
Key: 3 Value: three
After removing element
Key: 1 Value: one
Key: 2 Value: two
You have to use Iterator to safely remove element while traversing a map.
I agree with Paul Tomblin. I usually use the keyset's iterator, and then base my condition off the value for that key:
Iterator<Integer> it = map.keySet().iterator();
while(it.hasNext()) {
Integer key = it.next();
Object val = map.get(key);
if (val.shouldBeRemoved()) {
it.remove();
}
}
An alternative, more verbose way
List<SomeObject> toRemove = new ArrayList<SomeObject>();
for (SomeObject key: map.keySet()) {
if (something) {
toRemove.add(key);
}
}
for (SomeObject key: toRemove) {
map.remove(key);
}
And this should work as well..
ConcurrentMap<Integer, String> running = ... create and populate map
Set<Entry<Integer, String>> set = running.entrySet();
for (Entry<Integer, String> entry : set)
{
if (entry.getKey()>600000)
{
set.remove(entry.getKey());
}
}
Maybe you can iterate over the map looking for the keys to remove and storing them in a separate collection. Then remove the collection of keys from the map. Modifying the map while iterating is usually frowned upon. This idea may be suspect if the map is very large.
Set s=map.entrySet();
Iterator iter = s.iterator();
while (iter.hasNext()) {
Map.Entry entry =(Map.Entry)iter.next();
if("value you need to remove".equals(entry.getKey())) {
map.remove();
}
}
What's the best way to iterate over the below two maps together? I want to compare two maps values which are strings and have to get the keys and values.
HashMap<String, String> map1;
HashMap<String, String> map2;
There really isn't a better option than
for (Map.Entry<String, String> entry1 : map1.entrySet() {
String key = entry1.getKey();
String value1 = entry1.getValue();
String value2 = map2.get(key);
// do whatever with value1 and value2
}
Depending on what exactly you're trying to do, there are several reasonable options:
Just compare the contents of two maps
Guava provides a Maps.difference() utility that gives you a MapDifference instance letting you inspect exactly what is the same or different between two maps.
Iterate over their entries simultaneously
If you just want to iterate over the entries in two maps simultaneously, it's no different than iterating over any other Collection. This question goes into more detail, but a basic solution would look like this:
Preconditions.checkState(map1.size() == map2.size());
Iterator<Entry<String, String>> iter1 = map1.entrySet().iterator();
Iterator<Entry<String, String>> iter2 = map2.entrySet().iterator();
while(iter1.hasNext() || iter2.hasNext()) {
Entry<String, String> e1 = iter1.next();
Entry<String, String> e2 = iter2.next();
...
}
Note there is no guarantee these entries will be in the same order (and therefore e1.getKey().equals(e2.getKey()) may well be false).
Iterate over their keys to pair up their values
If you need the keys to line up, iterate over the union of both maps' keys:
for(String key : Sets.union(map1.keySet(), map2.keySet()) {
// these could be null, if the maps don't share the same keys
String value1 = map1.get(key);
String value2 = map2.get(key);
...
}
My case if maps are the same sizes
IntStream.range(0, map1.size()).forEach(i -> map1.get(i).equals(map2.get(i));
You can do something like:
for (String key : map1.keySet()) {
if (map2.containsKey(key)) {
// do whatever
} else {
// map2 doesn't have entry with map1 key
}
}
I have the following maps :
Map <String,String> m; // contains part details
Map <String,String> n; // contains part details
Map <String,String> o; // the new map that contains both m and n.
I want copy the values from m into o first.
I then want to loop though n and compare keys against o. If key from n, does not exist in o then put key/value it to o.
I tried the below, but the the the second step is not working (values are not copying)
for (Map.Entry<String, String> entry : m.entrySet())
{
String key = entry.getKey();
String value =entry.getValue();
o.put(key, value);
}
for (Map.Entry<String, String> entry : n.entrySet())
{
String key = entry.getKey();
String value =entry.getValue();
for (Map.Entry<String, String> entry1 : o.entrySet())
{
String key1 = entry.getKey();
if(key1 != key)
{
o.put(key,value);
}
}
}
Consider using Map#containsKey(). You can also iterate over a keyset, not over the entryset.
for (String key: n.keySet())
{
if (!o.containsKey(key))
o.put(key,n.get(key));
}
This should work.
I think the problem was you were using direct comparing of Strings, which is not sufficient in your case (almost never), use String#compareTo() method instead.
Why you don't use ready methods? You do not have to write all manually ;).
1) Method putAll() copies all values from one map to the second.
http://docs.oracle.com/javase/7/docs/api/java/util/Map.html#putAll(java.util.Map)
2) Method containsKey() and only one loop.
You don't have to loop through o.
Just use o.containsKey() method
In second inner for loop "String key1 = entry.getKey();" it should be "String key1 = entry1.getKey();"
mistaken between entry and entry1
o.putAll(m); // put all m into o
n.keySet().removeAll(o.keySet()); // Remove all duplicates from n
o.putAll(n); // Now add all filtered n to o
I have following LinkedHashMap declaration.
LinkedHashMap<String, ArrayList<String>> test1
my point is how can i iterate through this hash map.
I want to do this following, for each key get the corresponding arraylist and print the values of the arraylist one by one against the key.
I tried this but get only returns string,
String key = iterator.next().toString();
ArrayList<String> value = (ArrayList<String> )test1.get(key)
for (Map.Entry<String, ArrayList<String>> entry : test1.entrySet()) {
String key = entry.getKey();
ArrayList<String> value = entry.getValue();
// now work with key and value...
}
By the way, you should really declare your variables as the interface type instead, such as Map<String, List<String>>.
I'm assuming you have a typo in your get statement and that it should be test1.get(key). If so, I'm not sure why it is not returning an ArrayList unless you are not putting in the correct type in the map in the first place.
This should work:
// populate the map
Map<String, List<String>> test1 = new LinkedHashMap<String, List<String>>();
test1.put("key1", new ArrayList<String>());
test1.put("key2", new ArrayList<String>());
// loop over the set using an entry set
for( Map.Entry<String,List<String>> entry : test1.entrySet()){
String key = entry.getKey();
List<String>value = entry.getValue();
// ...
}
or you can use
// second alternative - loop over the keys and get the value per key
for( String key : test1.keySet() ){
List<String>value = test1.get(key);
// ...
}
You should use the interface names when declaring your vars (and in your generic params) unless you have a very specific reason why you are defining using the implementation.
In Java 8:
Map<String, List<String>> test1 = new LinkedHashMap<String, List<String>>();
test1.forEach((key,value) -> {
System.out.println(key + " -> " + value);
});
You can use the entry set and iterate over the entries which allows you to access both, key and value, directly.
for (Entry<String, ArrayList<String>> entry : test1.entrySet()) {
System.out.println(entry.getKey() + "/" + entry.getValue());
}
I tried this but get only returns string
Why do you think so? The method get returns the type E for which the generic type parameter was chosen, in your case ArrayList<String>.
// iterate over the map
for(Entry<String, ArrayList<String>> entry : test1.entrySet()){
// iterate over each entry
for(String item : entry.getValue()){
// print the map's key with each value in the ArrayList
System.out.println(entry.getKey() + ": " + item);
}
}