I find myself often writing such code:
Map < String, Set < String > > map = new TreeMap < String, Set < String > >();
String key;
String element;
/* ... */
Set < String > value = map.get(key);
if (value == null) {
value = new TreeSet < String >();
map.put(key, value);
}
value.add(element);
I hate the if statement above -- how can I get rid of it in standard Java? If you can confirm that there is no standard Java solution, it would then be nice if you could suggest a non-standard library that addresses this need.
Apache Commons Collections has a MultiMap:
MultiMap mhm = new MultiHashMap();
mhm.put(key, "A");
mhm.put(key, "B");
mhm.put(key, "C");
Collection coll = (Collection) mhm.get(key);
Guava (used to be Google Collections) has one too, which supports generics, and has both a tree and hash version:
Multimap<String,String> mhm = new TreeMultimap<String,String>();
// etc.
To clarify, Multimap<T, S> is basically the same as a Map<T, Collection<S>>, and put automatically creates the collection if it needs to.
EDIT: Updated to link to Guava, since apparently Google Collections is deprecated.
Hmm... I like Brendan's multimap answer, but you could also use a static utility method and keep to standard java.util classes/interfaces:
public static <K,V> Set<V> getTreeSet(Map<K,Set<V>> map, K key) {
Set<V> set = map.get(key);
if (set == null) {
set = new TreeSet<V>();
map.put(key, set);
}
return set;
}
Then your code with the null checks could be written:
Map < String, Set < String > > map = new TreeMap < String, Set < String > >();
String key;
String element;
/* ... */
MapUtils.getTreeSet(map, key).add(element);
You'd need static method for each collection type, but that seems better than writing the same code over and over again.
Related
I am making a method, which takes a provided TreeMap, removes entries where the key is a multiple of keyFilter and the value contains the valueFilter character, and then returns the resulting TreeMap.
This is what I have so far:
public static TreeMap<Integer, String> filterTreeMap(
TreeMap<Integer, String> map, int keyFilter, char valueFilter) {
for (Map.Entry<Integer, String> entry : map.entrySet()) {
int mapKey = entry.getKey();
String mapValue = entry.getValue();
if (mapKey%keyFilter == 0 && mapValue.indexOf(valueFilter) != -1) {
map.remove(mapKey);
}
}
return map;
}
However, under the if condition where I want to delete the entries, I don't know how to delete entries in tree map. As far as I know, there is no existing method that I can use?
Use an Iterator. As the Iterator.remove() Javadoc notes
The behavior of an iterator is unspecified if the underlying collection is modified while the iteration is in progress in any way other than by calling this method.
Something like
public static TreeMap<Integer, String> filterTreeMap(TreeMap<Integer, String> map,
int keyFilter, char valueFilter) {
Iterator<Map.Entry<Integer, String>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Integer, String> entry = iter.next();
int mapKey = entry.getKey();
String mapValue = entry.getValue();
if (mapKey % keyFilter == 0 && mapValue.indexOf(valueFilter) != -1) {
iter.remove();
}
}
return map;
}
It is possible to apply method removeIf to the entry set.
default boolean removeIf(Predicate<? super E> filter)
Removes all of the elements of this collection that satisfy the given predicate. Errors or runtime exceptions thrown during iteration or by the predicate are relayed to the caller.
Implementation Requirements:
The default implementation traverses all elements of the collection using its iterator(). Each matching element is removed using Iterator.remove(). If the collection's iterator does not support removal then an UnsupportedOperationException will be thrown on the first matching element.
Then the method filterTreeMap may have void return type because the input map is modified and this change will be "visible" outside this method.
public static void filterTreeMap(
TreeMap<Integer, String> map, int keyFilter, char valueFilter) {
map.entrySet().removeIf(e ->
e.getKey() % keyFilter == 0
&& e.getValue().indexOf(valueFilter) != -1
);
}
Keys on the map are unique. So, find that keys, and then remove them form the map.
public static TreeMap<Integer, String> filterTreeMap(TreeMap<Integer, String> map,
int keyFilter, char valueFilter) {
Set<Integer> keysToRemove = map.entrySet().stream()
.filter(kv -> kv.getKey() % keyFilter == 0 && kv.getValue().indexOf(valueFilter) != -1) // can be Predicate parameter
.map(Map.Entry::getKey)
.collect(Collectors.toSet());
keysToRemove.forEach(map::remove);
return map; // keep in mind, map is modified here. You might want to return a new map instead
}
Iterate over a copy and you can add/remove just fine:
for (Map.Entry<Integer, String> entry : new LinkedHashMap<Integer,String> (map).entrySet()) {
int mapKey = entry.getKey();
String mapValue = entry.getValue();
if (mapKey%keyFilter == 0 && mapValue.indexOf(valueFilter) != -1) {
map.remove(mapKey);
}
}
It's not even any more lines of code, because the copy is made in-line via the copy constructor. LinkedHashMap was chosen to preserve iteration order (if that matters).
I used LinkedHashMap<String, Double> . I want to take separate values from it. If it is Array we can use .get[2] ,.get[5] etc. for take 2nd and 5th value. But for LinkedHashMap<String, Double> how to do it. I used following code. But it print all the values contained in LinkedHashMap<String, Double>. I need to take separately.
Set set = mylist.entrySet();
Iterator i = set.iterator();
while(i.hasNext()) {
Map.Entry me1 = (Map.Entry)i.next();
System.out.print(me1.getKey());
System.out.println(me1.getValue());
You may use the LinkedHashMap#get(Object key) method
Which will return the value corresponding to the key parameter. Since your keys are String, you can not use an int to retrieve them.
Example
If your LinkedHashMap contains ["key", 2.5], calling
System.out.println(lnkHashMap.get("key"));
will print
2.5
Addition
If you're using java-8, there is a workaround using a Stream object.
Double result = hashmap.values()
.stream()
.skip(2)
.findFirst()
.get();
This will skip the two first values and get to the third one directly and return it.
If not, here is a solution
public <T> T getValueByIndex (Map<? extends Object, T> map, int index){
Iterator<T> it = map.values().iterator();
T temp = null;
for (int i = 0 ; i < index ; i++){
if (it.hasNext()){
temp = it.next();
} else {
throw new IndexOutOfBoundsException();
}
}
return temp;
}
It could be the case that you are using the wrong data structure for your purpose.
If you look closely to the LinkedHashMap API you will notice that it is indeed a Map and the only way to access a previously stored value is by providing its key.
But if you really think you need to access the ith value of the LinkedHashMap according to its insertion-order (or access-order) you can use a simple utility method like the following:
Java 8 Solution
private static <K, V> Optional<V> getByInsertionOrder(LinkedHashMap<K, V> linkedHashMap, int index) {
return linkedHashMap.values().stream()
.skip(index)
.findFirst();
}
Java 7 Soution
private static <K, V> V getByInsertionOrder(LinkedHashMap<K, V> linkedHashMap, int index) {
if (index < 0 || index >= linkedHashMap.size()) {
throw new IndexOutOfBoundsException();
}
Iterator<Entry<K, V>> iterator = linkedHashMap.entrySet().iterator();
for (int i = 0; i < index; i++) {
iterator.next();
}
return iterator.next().getValue();
}
I understand that the Set returned from a Map's keySet() method does not guarantee any particular order.
My question is, does it guarantee the same order over multiple iterations. For example
Map<K,V> map = getMap();
for( K k : map.keySet() )
{
}
...
for( K k : map.keySet() )
{
}
In the above code, assuming that the map is not modified, will the iteration over the keySets be in the same order. Using Sun's jdk15 it does iterate in the same order, but before I depend on this behavior, I'd like to know if all JDKs will do the same.
EDIT
I see from the answers that I cannot depend on it. Too bad. I was hoping to get away with not having to build some new Collection to guarantee my ordering. My code needed to iterate through, do some logic, and then iterate through again with the same ordering. I'll just create a new ArrayList from the keySet which will guarantee order.
You can use a LinkedHashMap if you want a HashMap whose iteration order does not change.
Moreover you should always use it if you iterate through the collection. Iterating over HashMap's entrySet or keySet is much slower than over LinkedHashMap's.
If it is not stated to be guaranteed in the API documentation, then you shouldn't depend on it. The behavior might even change from one release of the JDK to the next, even from the same vendor's JDK.
You could easily get the set and then just sort it yourself, right?
Map is only an interface (rather than a class), which means that the underlying class that implements it (and there are many) could behave differently, and the contract for keySet() in the API does not indicate that consistent iteration is required.
If you are looking at a specific class that implements Map (HashMap, LinkedHashMap, TreeMap, etc) then you could see how it implements the keySet() function to determine what the behaviour would be by checking out the source, you'd have to really take a close look at the algorithm to see if the property you are looking for is preserved (that is, consistent iteration order when the map has not had any insertions/removals between iterations). The source for HashMap, for example, is here (open JDK 6): http://www.docjar.com/html/api/java/util/HashMap.java.html
It could vary widely from one JDK to the next, so i definitely wouldn't rely on it.
That being said, if consistent iteration order is something you really need, you might want to try a LinkedHashMap.
The API for Map does not guarantee any ordering whatsoever, even between multiple invocations of the method on the same object.
In practice I would be very surprised if the iteration order changed for multiple subsequent invocations (assuming the map itself did not change in between) - but you should not (and according to the API cannot) rely on this.
EDIT - if you want to rely on the iteration order being consistent, then you want a SortedMap which provides exactly these guarantees.
Just for fun, I decided to write some code that you can use to guarantee a random order each time. This is useful so that you can catch cases where you are depending on the order but you should not be. If you want to depend on the order, than as others have said, you should use a SortedMap. If you just use a Map and happen to rely on the order then using the following RandomIterator will catch that. I'd only use it in testing code since it makes use of more memory then not doing it would.
You could also wrap the Map (or the Set) to have them return the RandomeIterator which would then let you use the for-each loop.
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class Main
{
private Main()
{
}
public static void main(final String[] args)
{
final Map<String, String> items;
items = new HashMap<String, String>();
items.put("A", "1");
items.put("B", "2");
items.put("C", "3");
items.put("D", "4");
items.put("E", "5");
items.put("F", "6");
items.put("G", "7");
display(items.keySet().iterator());
System.out.println("---");
display(items.keySet().iterator());
System.out.println("---");
display(new RandomIterator<String>(items.keySet().iterator()));
System.out.println("---");
display(new RandomIterator<String>(items.keySet().iterator()));
System.out.println("---");
}
private static <T> void display(final Iterator<T> iterator)
{
while(iterator.hasNext())
{
final T item;
item = iterator.next();
System.out.println(item);
}
}
}
class RandomIterator<T>
implements Iterator<T>
{
private final Iterator<T> iterator;
public RandomIterator(final Iterator<T> i)
{
final List<T> items;
items = new ArrayList<T>();
while(i.hasNext())
{
final T item;
item = i.next();
items.add(item);
}
Collections.shuffle(items);
iterator = items.iterator();
}
public boolean hasNext()
{
return (iterator.hasNext());
}
public T next()
{
return (iterator.next());
}
public void remove()
{
iterator.remove();
}
}
I agree with LinkedHashMap thing. Just putting my findings and experience while I was facing the problem when I was trying to sort HashMap by keys.
My code to create HashMap:
HashMap<Integer, String> map;
#Before
public void initData() {
map = new HashMap<>();
map.put(55, "John");
map.put(22, "Apple");
map.put(66, "Earl");
map.put(77, "Pearl");
map.put(12, "George");
map.put(6, "Rocky");
}
I have a function showMap which prints entries of map:
public void showMap (Map<Integer, String> map1) {
for (Map.Entry<Integer, String> entry: map1.entrySet()) {
System.out.println("[Key: "+entry.getKey()+ " , "+"Value: "+entry.getValue() +"] ");
}
}
Now when I print the map before sorting, it prints following sequence:
Map before sorting :
[Key: 66 , Value: Earl]
[Key: 22 , Value: Apple]
[Key: 6 , Value: Rocky]
[Key: 55 , Value: John]
[Key: 12 , Value: George]
[Key: 77 , Value: Pearl]
Which is basically different than the order in which map keys were put.
Now When I sort it with map keys:
List<Map.Entry<Integer, String>> entries = new ArrayList<>(map.entrySet());
Collections.sort(entries, new Comparator<Entry<Integer, String>>() {
#Override
public int compare(Entry<Integer, String> o1, Entry<Integer, String> o2) {
return o1.getKey().compareTo(o2.getKey());
}
});
HashMap<Integer, String> sortedMap = new LinkedHashMap<>();
for (Map.Entry<Integer, String> entry : entries) {
System.out.println("Putting key:"+entry.getKey());
sortedMap.put(entry.getKey(), entry.getValue());
}
System.out.println("Map after sorting:");
showMap(sortedMap);
the out put is:
Sorting by keys :
Putting key:6
Putting key:12
Putting key:22
Putting key:55
Putting key:66
Putting key:77
Map after sorting:
[Key: 66 , Value: Earl]
[Key: 6 , Value: Rocky]
[Key: 22 , Value: Apple]
[Key: 55 , Value: John]
[Key: 12 , Value: George]
[Key: 77 , Value: Pearl]
You can see the difference in order of keys. Sorted order of keys is fine but that of keys of copied map is again in the same order of the earlier map. I dont know if this is valid to say, but for two hashmap with same keys, order of keys is same. This implies to the statement that order of keys is not guaranteed but can be same for two maps with same keys because of inherent nature of key insertion algorithm if HashMap implementation of this JVM version.
Now when I use LinkedHashMap to copy sorted Entries to HashMap, I get desired result (which was natural, but that is not the point. Point is regarding order of keys of HashMap)
HashMap<Integer, String> sortedMap = new LinkedHashMap<>();
for (Map.Entry<Integer, String> entry : entries) {
System.out.println("Putting key:"+entry.getKey());
sortedMap.put(entry.getKey(), entry.getValue());
}
System.out.println("Map after sorting:");
showMap(sortedMap);
Output:
Sorting by keys :
Putting key:6
Putting key:12
Putting key:22
Putting key:55
Putting key:66
Putting key:77
Map after sorting:
[Key: 6 , Value: Rocky]
[Key: 12 , Value: George]
[Key: 22 , Value: Apple]
[Key: 55 , Value: John]
[Key: 66 , Value: Earl]
[Key: 77 , Value: Pearl]
Hashmap does not guarantee that the order of the map will remain constant over time.
It doesn't have to be. A map's keySet function returns a Set and the set's iterator method says this in its documentation:
"Returns an iterator over the elements in this set. The elements are returned in no particular order (unless this set is an instance of some class that provides a guarantee)."
So, unless you are using one of those classes with a guarantee, there is none.
Map is an interface and it does not define in the documentation that order should be the same. That means that you can't rely on the order. But if you control Map implementation returned by the getMap(), then you can use LinkedHashMap or TreeMap and get the same order of keys/values all the time you iterate through them.
tl;dr Yes.
I believe the iteration order for .keySet() and .values() is consistent (Java
8).
Proof 1: We load a HashMap with random keys and random values. We iterate on this HashMap using .keySet() and load the keys and it's corresponding values to a LinkedHashMap (it will preserve the order of the keys and values inserted). Then we compare the .keySet() of both the Maps and .values() of both the Maps. It always comes out to be the same, never fails.
public class Sample3 {
static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static SecureRandom rnd = new SecureRandom();
// from here: https://stackoverflow.com/a/157202/8430155
static String randomString(int len){
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++) {
sb.append(AB.charAt(rnd.nextInt(AB.length())));
}
return sb.toString();
}
public static void main(String[] args) throws Exception {
for (int j = 0; j < 10; j++) {
Map<String, String> map = new HashMap<>();
Map<String, String> linkedMap = new LinkedHashMap<>();
for (int i = 0; i < 1000; i++) {
String key = randomString(8);
String value = randomString(8);
map.put(key, value);
}
for (String k : map.keySet()) {
linkedMap.put(k, map.get(k));
}
if (!(map.keySet().toString().equals(linkedMap.keySet().toString()) &&
map.values().toString().equals(linkedMap.values().toString()))) {
// never fails
System.out.println("Failed");
break;
}
}
}
}
Proof 2: From here, the table is an array of Node<K,V> class. We know that iterating an array will give the same result every time.
/**
* The table, initialized on first use, and resized as
* necessary. When allocated, length is always a power of two.
* (We also tolerate length zero in some operations to allow
* bootstrapping mechanics that are currently not needed.)
*/
transient Node<K,V>[] table;
The class responsible for .values():
final class Values extends AbstractCollection<V> {
// more code here
public final void forEach(Consumer<? super V> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.value);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
The class responsible for .keySet():
final class KeySet extends AbstractSet<K> {
// more code here
public final void forEach(Consumer<? super K> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.key);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
Carefully look at both the inner classes. They are pretty much the same except:
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.key); <- from KeySet class
// action.accept(e.value); <- the only change from Values class
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
They iterate on the same array table to support .keySet() in KeySet class and .values() in Values class.
Proof 3: this answer also explicitly states - So, yes, keySet(), values(), and entrySet() return values in the order the internal linked list uses.
Therefore, the .keySet() and .values() are consistent.
Logically, if the contract says "no particular order is guaranteed", and since "the order it came out one time" is a particular order, then the answer is no, you can't depend on it coming out the same way twice.
You also can store the Set instance returned by the keySet() method and can use this instance whenever you need the same order.
This question already has answers here:
Map implementation with duplicate keys
(20 answers)
Closed 9 years ago.
Is there a native data structure in java that accepts key value pairs and allows duplicates? I am creating a checklist of characters in a string but some characters occur more than once.
ex
j -> false
a -> false
v -> false
a -> false
You can simulate multiple key-value (KV) pairs by saving a list of values for each in a map. This is a standard implementation approach for "multivalue" maps.
So, if the key is a Character object and the value is Boolean, you can do
Map<Character, List<Boolean>> multimap = new HashMap<Character, List<Boolean>>();
and every time you want to add a new value to an existing KV pair in the map just call
multimap.get(key).add(value);
where key is the Character and value its corresponding Boolean value.
The Guava library by Google (free download) has a Multimap interface implemented in various ways, so essentially you can instantiate a MultiMap<Character, Boolean> map and use it accordingly. Similarly, you can get the Apache Commons Collections library and use its MultiValueMap class. You may also want to check the answers to a similar StackOverflow question, or another one.
If you only want to store one of each value per key, then a Set should be used in the place of the List.
Use a List of Pairs:
public class Pair<T, U> {
public final T key;
public final U value;
public Pair(T key, U value) {
this.key = key;
this.value = value;
}
}
public class YourApp {
public static void main(String[] args) {
List<Pair<Character, Boolean>> charList = new ArrayList<Pair<Character, Boolean>>();
charList.add(new Pair('j', false));
charList.add(new Pair('a', false));
charList.add(new Pair('v', false));
charList.add(new Pair('a', false));
for (Pair<Character, Boolean> pair : charList) {
System.out.println(pair.key + " -> " + pair.value);
}
}
}
With the selfwritten generic Pair class you can hold a key and a value of any type you want. If you're adding pairs to a List, you can even hold duplicates of pair entries.
You can use MultiMap<Character,Boolean> bcoz it allows duplicate key which exist in org.apache.commons.collections package.
or
You can use ArrayList and add the objects of the Class that contain attribute as char & boolean pair.
I do not know of a build in solution.
A quick alternative would be to use a simple ArrayList, and create an object that is a char/boolean pair that you can add to it.
commons.apache.org have MultiHashMap class. Try this one...!!!
MultiHashMap mp = new MultiHashMap();
mp.put("a", "1");
mp.put("b", "4");
mp.put("c", "2");
mp.put("a", "6");
List list = null;
Set set = mp.entrySet();
Iterator i = set.iterator();
while (i.hasNext()) {
Map.Entry<String, List<String>> me = (Map.Entry) i.next();
for(int j = 0 ; j< me.getValue().size(); j++ ){
System.out.println(me.getKey() +" : " +me.getValue().get(j));
}
}
}
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
}