clear all values of hashmap except two key/value pair - java

I have a HashMap with hundred of key/value pairs.
Now I have to delete all key/values except 2 key/value.
I have use this way :
if(map!=null){
String search = map.get(Constants.search);
String context = map.get(Constants.context);
map = new HashMap<>();
map.put(Constants.search,search);
map.put(Constants.context,context);
}
But java 8 introduced removeIf() for these kind of condition. How can I solve this problem with removeIf() method ?

You'll need to it like this :
map.keySet().removeIf(k -> !(k.equals(Constants.search) || k.equals(Constants.context)));
It will iterate over the keys and remove the ones for those the key is not one of or two required keys

yet shorter (since Java 2):
map.keySet().retainAll(myKeys);
Since keySet() still wraps the original HashMap, its #retainAll() affects the Map.
myKeys is a collection of keys, e.g.: myKeys = List.of("key1", "key2")

Related

Java - multiple hashmaps pointing to the same key

I have multiple files which contains key=value string pairs. The keys are the same between the files, but the values differs. Each file can have 1000 plus of such pairs.
I want to store each file in a separate hashmap, ie map<KeyString, ValueString>, so if there are five files, then there will be five hashmaps.
To avoid duplicating the keys across each hashmap, is it possible to have each map reference the same key? Note that once the keys are added to the map, it will not be deleted.
I considered making the first file the 'base' as in the flyweight pattern, this base would be the intrinsic set of keys/values. The other remaining files would be the extrinsic set of values, but I don't know how to relate the values back to the base (intrinsic) keys without duplicating the keys?
I am open to a simpler/better approach.
I can think about a simpler approach. Instead of having Map<String, String> think of Map<String, List<String> or directly MultiMap<String, String> from guava.
If each key is in each file and all have values, you could store values from first file at 0th index, from the second at 1st index etc.
If it wouldn't work, I recommend a Collection<Map<String, String>, so you're able to iterate through your Maps. Then when you want to add value to one of the Maps, go through all keySets and if one of them contains that key, just put with object returned from this keySet.
Other solution would be to have a HashSet of keys that have already been put. This would be more efficient.
After reading in the keys, you can use String.intern().
When called, what it does is either:
add the String to the internal pool if it didn't exist already;
return the equivalent String from the pool if it already existed.
String#intern Javadoc
First of all, I don't see the problem with storing multiple instances of your String keys. 5 HashMaps * 1000 keys is a very small number, and you shouldn't have memory issues.
That said, if you still want to avoid duplicating the Strings, you can create the first HashMap, and then you the exact same keys for the other HashMaps.
For example, suppose map1 is the first HashMap and it is already populated with the contents of the first file.
You can write something like this to populate the 2nd HashMap:
for (String key : map1.keySet()) {
map2.put (key, someValue);
}
Of course you will have to find for each key of the first map the corresponding value of the second map. If the keys are not stored in the same order in the input files, this may require some preliminary sorting step.
Perhaps you could hold a static Map<> to map your keys to unique Integers and use those Integers for the keys to your map?
Something like:
class KeySharedMap<K,V> {
// The next key to use. Using Atomics for the auto-increment.
static final AtomicInteger next = new AtomicInteger(0);
// Static mapping of keys to unique Integers.
static final ConcurrentMap<Object,Integer> keys = new ConcurrentHashMap<>();
// The map indexed by Integer from the `keys`.
Map<Integer, V> map = new HashMap<>();
public V get(Object key) {
return map.get(keys.get(key));
}
public V put(Object key, V value) {
// Associate a unique integer for each unique key.
keys.computeIfAbsent(key,x -> next.getAndIncrement());
// Put it in my map.
return map.put(keys.get(key),value);
}
}
Yes, I realise that K is not used here but I suspect it would be necessary if you wish to implement Map<K,V>.

Is there an efficient way of checking if HashMap contains keys that map to the same value?

I basically need to know if my HashMap has different keys that map to the same value. I was wondering if there is a way other than checking each keys value against all other values in the map.
Update:
Just some more information that will hopefully clarify what I'm trying to accomplish. Consider a String "azza". Say that I'm iterating over this String and storing each character as a key, and it's corresponding value is some other String. Let's say I eventually get to the last occurrence of 'a' and the value is already be in the map.This would be fine if the key corresponding with the value that is already in the map is also 'a'. My issue occurs when 'a' and 'z' both map to the same value. Only if different keys map to the same value.
Sure, the fastest to both code and execute is:
boolean hasDupeValues = new HashSet<>(map.values()).size() != map.size();
which executes in O(n) time.
Sets don't allow duplicates, so the set will be smaller than the values list if there are dupes.
Very similar to EJP's and Bohemian's answer above but with streams:
boolean hasDupeValues = map.values().stream().distinct().count() != map.size();
You could create a HashMap that maps values to lists of keys. This would take more space and require (slightly) more complex code, but with the benefit of greatly higher efficiency (amortized O(1) vs. O(n) for the method of just looping all values).
For example, say you currently have HashMap<Key, Value> map1, and you want to know which keys have the same value. You create another map, HashMap<Value, List<Key>> map2.
Then you just modify map1 and map2 together.
map1.put(key, value);
if(!map2.containsKey(value)) {
map2.put(value, new ArrayList<Key>);
}
map2.get(value).add(key);
Then to get all keys that map to value, you just do map2.get(value).
If you need to put/remove in many different places, to make sure that you don't forget to use map2 you could create your own data structure (i.e. a separate class) that contains 2 maps and implement put/remove/get/etc. for that.
Edit: I may have misunderstood the question. If you don't need an actual list of keys, just a simple "yes/no" answer to "does the map already contain this value?", and you want something better than O(n), you could keep a separate HashMap<Value, Integer> that simply counts up how many times the value occurs in the map. This would take considerably less space than a map of lists.
You can check whether a map contains a value already by calling map.values().contains(value). This is not as efficient as looking up a key in the map, but still, it's O(n), and you don't need to create a new set just in order to count its elements.
However, what you seem to need is a BiMap. There is no such thing in the Java standard library, but you can build one relatively easily by using two HashMaps: one which maps keys to values and one which maps values to keys. Every time you map a key to a value, you can then check in amortized O(1) whether the value already is mapped to, and if it isn't, map the key to the value in the one map and the value to the key in the other.
If it is an option to create a new dependency for your project, some third-party libraries contain ready-made bimaps, such as Guava (BiMap) and Apache Commons (BidiMap).
You could iterate over the keys and save the current value in the Set.
But, before inserting that value in a Set, check if the Set already contains that value.
If this is true, it means that a previous key already contains the same value.
Map<Integer, String> map = new HashMap<>();
Set<String> values = new HashSet<>();
Set<Integter> keysWithSameValue = new HashSet<>();
for(Integer key : map.keySet()) {
if(values.contains(map.get(key))) {
keysWithSameValue.add(key);
}
values.add(map.get(key));
}

How Trim String keys in Hashmap

My app will receive a Hashmap<String,Object> from another application.
Is there any way to trim the string keys without iterating the hashmap which leads to performance downgrade becoz Hashmap may contain a lot of real data entries.
Thanks
You have two options:
trim before adding
iterate through MapEntries ad update all keys
In your example you have no choice, and you need to iterate over those keys.
Althought in java 8 you could use pararell streams to boost up such operation. But I would not recommend it in multithread enviroment.
One thing to note beforehand:
The map might contain 2 keys where the first is the trimmed version of the second. By doing what you want, it would overwrite/remove one of them from the map! E.g. the map might contain the keys "a " and "a", and by trimming the keys one of them will disappear!
HashMap does not provide any way to manipulate keys without iterating over them.
You can either "copy" the entries to a new map with keys trimmed (as with #RuchiraGayanRanaweera's solution), or you can do it in the same map like this:
Solution #1: Duplicate entry set and replace the different keys
So what you may do is iterate over the entries, and trim the keys. This also means that if the trimmed key is not equal to the original, you have to remove the entry with the old key and put it again with the new one. You only need to replace the entry if the trimmed version is different:
Map<String, Object> map = new HashMap<>();
for (Entry<String, Object> entry : new HashSet<>(map.entrySet())) {
String trimmed = entry.getKey().trim();
if (!trimmed.equals(entry.getKey())) {
map.remove(entry.getKey());
map.put(trimmed, entry.getValue());
}
}
Note that it is necessary to create a new Set of the entry set because quoting from the javadoc of HashMap.entrySet():
If the map is modified while an iteration over the set is in progress (except through the iterator's own remove operation, or through the setValue operation on a map entry returned by the iterator) the results of the iteration are undefined.
Solution #2: Collect first then replace the different keys
Another option is to collect the keys where the trimmed key is different, and change only those after the first iteration. This solution has the advantage of not having to "duplicate" the entry set to iterate over it. If there are relatively few keys whose trimmed variant is different, probably this is the fastest solution:
Map<String, Object> map = new HashMap<>();
// Set to store the modified keys,
// Also store the trimmed String for performance reasons
Set<String[]> modifiedSet = new HashSet<>();
for (Entry<String, Object> entry : map.entrySet()) {
String trimmed = entry.getKey().trim();
if (!trimmed.equals(entry.getKey()))
modifiedSet.add(new String[]{entry.getKey(), trimmed});
}
// Changing a key can be done in one step:
// Removing the old entry (which returns the old value) and put the new
for (String[] modified : modifiedSet)
map.put(modified[1], map.remove(modified[0]));
If there is no way to trim() keys before adding to HashMap then you have to do something like following:
HashMap<String,Object> map=new HashMap<>();
HashMap<String,Object> newMap=new HashMap<>();
for(Map.Entry<String,Object> entry:map.entrySet()){
newMap.put(entry.getKey().trim(),entry.getValue());
}

How to use hashmap.put without overriding the previous data? (java)

I have something along the lines of this:
public HashMap<Boolean, String> map = new HashMap();
map.put(this.inverted, "Inverted");
map.put(this.active, "Loading");
System.out.println(map.size());
after seeing that the size was always 1, I realised that using map.put was overriding the previous data. I am currently trying to iterate over the hashmap. Is there a way to add mappings to it without overriding previous ones?
You have declared your HashMap as: -
public HashMap<Boolean, String> map = new HashMap();
Now, just think how many maximum mapping can you have in your map? The answer you can get by thinking of, what all values can your Boolean type take. This is because, you cannot have duplicate keys in a HashMap.
So, probably you got it now, that you can at max have only 2 mappings in your map, one for true and other for false(In fact you can have a 3rd one too, as you can have a mapping for a null key too in your HashMap).
So, in your case, if both this.inverted and this.active are either true or false. Then only one of them can be there, and that would be the later value inserted.
Is there a way to add mappings to it without overriding previous ones?
Probably you have build your HashMap wrongly. You should declare your map as: -
private Map<String, Boolean> map = new HashMap();
And now you can put two mappings as: -
map.put("Inverted", this.inverted);
map.put("Loading", this.active);
It's because this.inverted.equals(this.active) and this.inverted.hashcode()==this.active.hashcode()
Maybe you need redefine the equals method for the key.
In MAP
An object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value. ---> from Map Api
from your implementation, may be this.inverted and this.active both have same value.
Check the input once. print the keySet, then check.
or change the input to Map<String, Boolean>
As #Frank suggest you should invert your Map.
public final Map<String, Boolean> map = new HashMap<>();
map.put("Inverted", this.inverted);
map.put("Loading", this.active);
System.out.println(map);
If the keys are the same than the previous value is overwritten in a standard Java Map. If you don't want this, you can have a look at a multimap which is implemented for example in commons-collections. It can hold different values for one key.
Hashmap is based on key/value pairs. If your keys are equal (they have the same hashcode), it will behave as you described.
For your use case, reversing your key/value pairs will help you.
public HashMap<String, Boolean> map = new HashMap();
map.put("Inverted", this.inverted);
map.put("Loading", this.active);
System.out.println(map.size());
Get object of innermap ,by passing outer map key .. Then check if key of innermap exists then update values with previous data. else create new object of inner map.

Return all close match values in a Map

I have map where each key is a String.
To access a value I can use the .get method of Map. If I want to return anything that matches the key for example : "one, onetwo, onetwothree" , get all values that contain the String "two" so in this case return "onetwo, onetwothree". Is this possible using a Map ?
Im currently using a List and iterating over each String and checking if the String contains the value I am searching for.
There is no such method on any of the Map classes (afaik). You can iterate the keys then check the containment of the fragment, or use a completely different data structure. A trie-map would do it I guess.
Edit:
What you currently doing should be just fine for 99% of all cases. If you are processing extreme amounts of data, use full text indexing. (Which can be done with Suffix trees)
Using a HashMap you will have to iterate over all keys and as soon as a key matches your pattern, then collect that value and keep going until you are done.
Using a TreeMap you can get the keys in sorted order so you could use perhaps this property for a more efficient search.
But I think you should switch to a different data structure. A trie as #zeller also points out seems to do what you want
as others said there are no methods in the java collections API to acheive this.
this is how you do it by iterating over keys of a map
HashMap<String, String> map = new HashMap<String, String>();
map.put("onesample", "1");
map.put("onetwo", "2");
map.put("onetwothree", "3");
for(Entry<String, String> en: map.entrySet()) {
if(en.getKey().contains("two")){
System.out.println(en.getKey());
}
}
}

Categories

Resources