Finding all keys in a HashMap with a character an replace it - java

I have a HashMap and now i need to find all the keys inside the HashMap which has a particular letter inside it and replace it with another letter

You could try this :
public void replaceKeysChar(char originalChar, char newChar, Map<String, ?> map) {
Map<String, Object> tempMap = new HashMap<String, Object>();
for (Map.Entry<String, ?> entry : map.entrySet()) {
String key = entry.getKey();
if(key != null){
key = key.replace(originalChar, newChar);
}
tempMap.put(key, entry.getValue());
}
map.clear();
map.putAll(tempMap);
}
This way you handle only char, and you don't change the implementation.
Plus when you iterate you don't add item in your map (it would be a bad idea).
If you don't care about the implementation simply return the tempMap and remove the clear/putAll part (it will consume less resources).
EDIT :
After #locka's answer I think I should specify that this method can't handle collisions.
If your Map contains the keys "toto" and "tata" and you do a `replaceKeysChar('a','o', map) only one of the values between "toto"'s value and "tata"'s value will be in the map, the other will simply be ignored.
EDIT bis:
To handle collisions with exceptions (à la #Stephen C) just replace the old for by this one :
for (Map.Entry<String, ?> entry : map.entrySet()) {
String key = entry.getKey();
if(key != null){
key = key.replace(originalChar, newChar);
}
if(tempMap.containsKey(key))
throw new CollisionException();
tempMap.put(key, entry.getValue());
}

Here's a solution that "deals with" collisions by throwing an exception.
public void replaceKeysChar(char originalChar, char newChar, Map<String, ?> map) {
Map<String, Object> tempMap = new HashMap<String, Object>();
Set<String> tempSet = new HashSet<String>();
for (Map.Entry<String, ?> entry : map.entrySet()) {
String originalKey = entry.getKey();
String newKey = originalKey .replace(originalChar, newChar);
if (!newKey.equals(originalKey)) {
if (map.containsKey(newKey) || tempMap.containsKey(newKey)) {
throw new CollisionException(newKey);
}
tempMap.put(newKey, entry.getValue());
tempSet.add(originalKey());
}
}
map.keySet().removeAll(tempSet);
map.putAll(tempMap);
}
EDIT
Fixed bugs ... in previous versions.

Construct a new hashmap
Copy all the
(key,value) pairs from the old
hashmap, except that you replace
OLDCHAR with NEWCHAR in the keys
Swap
the hashmaps over.

So you want to change the Key. If the Hashalgorithm isn't something strange you'll have to get all key-value pairs, delete them from the hashmap, change them and reinsert them. Or insert them in a new Hashmap.

I think people are concerned that you can't change a String key in situ because of immutability and the String's hashcode being tied the value of the string. Adding / removing keys could potentially work but then you run the risk of collisions, where you end up replacing an existing string by accident.
The most obvious way to solve this issue is to use a custom key, e.g.
public class MyKey {
static private Random r = new Random();
private String keyValue;
private final int hashCode = r.nextInt();
#Override
public int hashCode() {
return hashCode;
}
public void setKeyValue(String keyValue) {
this.keyValue = keyValue;
}
public String getKeyValue() {
return keyValue;
}
}
This key has a string key value which you can easily set / get and a hashCode which is just a random number. Once you add this key to the hash map you can change the string in situ without adding or removing the key. All you need do is iterate the key set and perform any processing you like on the key, all without affecting the hashcode or running the risk of collisions or anything else.

Related

Java HashMap - How to simultaneously get and then remove a random entry from a HashMap?

I was wondering if it is possible to get a random value from a HashMap and then straight after remove that key/value from the HashMap? I can't seem to find any method that works, would a different data structure be more appropriate for this?
Edit:
I should've been more clear, I generate a random number and then retrieve the value that corresponds with that random number. I need to return the value and then remove the entry from the map.
Maybe Map#computeIfPresent would work in your case. From its documentation:
If the value for the specified key is present and non-null, attempts to compute a new mapping given the key and its current mapped value.
If the remapping function returns null, the mapping is removed.
var map = new HashMap<Integer, String>();
map.put(1, "One");
map.put(2, "Two");
map.put(3, "Three");
map.computeIfPresent(2, (k, v) -> {
// `v` is equal to "Two"
return null; // Returning `null` removes the entry from the map.
});
System.out.println(map);
The above code outputs the following:
{1=One, 3=Three}
If you were to use a ConcurrentHashMap, then this would be an atomic operation.
The best way to both return and remove the key-value pair from a HashMap is by using the remove(key) method. This method removes the entry associated with the key and returns its corresponding value.
Integer randomNumber = new Random().nextInt(10);
Map<Integer, String> map = new HashMap<>();
String valueOfRandomNumberKey = map.remove(randomNumber);
The problem, as I understand it, is this: given a HashMap you want to
Choose a key at random from among the the keys currently associated in the Map;
Remove that association of that randomly chosen key from the map; and
Return the value that had, until recently, been associated with that key
Here's an example of how to do this, along with some a little test/demonstration routine:
public class Main
{
private static <K, V> V removeRandomEntry(Map<K, V> map){
Set<K> keySet = map.keySet();
List<K> keyList = new ArrayList<>(keySet);
K keyToRemove = keyList.get((int)(Math.random()*keyList.size()));
return map.remove(keyToRemove);
}
public static void main(String[] args){
Map<String, String> map = new HashMap<>();
for(int i = 0; i < 100; ++i)
map.put("Key" + i, "Value"+i);
int pass = 0;
while (!map.isEmpty())
System.out.println("Pass " + (++pass) + ": Removed: " + removeRandomEntry(map));
}
}
I would do it like this:
Hashmap<Integer, Object> example;
int randomNum = ThreadLocalRandom.current().nextInt(0, example.size());
example.getValue() //do something
example.remove(new Integer(randomNum));

in Java, how can I map String to a Set<String> if I do not know how many keys in total

I need a HashMap, which the key is String and value is Set, like:
Key: "a", Value: {"a","b","c"....}
Key: "b", Value: {"a,","d"....}
...
But I do not know how many keys in total, it depends on the result from other method.
So basically, here is the method looks like: (map could be field)
public void mapKeyValue(int numbersOfKey, HashMap map){
//some code
}
So if I write the code like this:
public void mapKeyValue(int numbersOfKey, HashMap map){
for (int i = 0; i < numbersOfKey; i++){
Set<String> set = new HashSet<String>();
set.add("some strings");// we can add some strings here
map.put("OneString", set);
}
}
After the method, I will get nothing because I will lose all the Set object created by the method, so I cannot get the Set by calling map.get("OneString").
So what should I do if I want to get that hashMap?
There are a number of issues with your code, but I suggest the following approach.
In your case, it looks like you have a Map<String, Set<String>> which is a map of String keys to a set of Strings.
If that's what you were after, I suggest that you
check if the key has a value. If not add an empty set for the key to the surrounding map.
Fetch the set from the map by it's key.
add or remove any desired values from the set
Note that your code as it is written, always replaces the Set stored with the key "OneString" meaning that regardless of value "numbersOfKey" you are really just rebuilding the set at the single key "OneString" numbersOfKey times.
You probably want to do something like
public void addToSet(String setName, String value) {
if (!sets.containsKey(setName)) {
sets.put(setName, new HashSet<String>());
}
Set<String> values = sets.get(setName);
values.add(value);
}
This block assumes you have somewhere in the class a member variable like
private Map<String, Set<String>> sets = new HashMap<>();
Note that this code is an idea, and not production code. In the real world, what you add probably should eventually be removed at some point in time. As such, you want to have a facility to remove specific values, or entire sets of values along with their keys at some future point of your program's execution.
you can not do that?
public HashMap<String, Set<String>> mapKeyValue(int numbersOfKey){
HashMap<String, Set<String>> map = new HashMap<>();
for (int i = 0; i < numbersOfKey; i++){
Set<String> set = new HashSet<String>();
set.add("some strings" + "" + i);// we can add some strings here
map.put("OneString", set);
}
return map;
}
I would suggest using the Apache Commons Collection MultiValueMap instead of creating a Set each time. Both work just fine, but there is a Map that does all of that for you and it's based on a HashMap, keeping your constant time access. Javadoc here:
https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/map/MultiValueMap.html
Something like this...
public void someOtherMethod() {
// Assuming the Map is created and used somewhere outside the mapKeyValue method. Otherwise it should be instantiated inside the mapKeyValue method
MultiValueMap<String, String> map = new MultiValueMap<>();
//2 is an arbitrary, made up number that you select somehow
mapKeyValue(2, map);
//Access the values of the map dynamically without knowing how many keys there are
for (String key : map.keySet()) {
System.out.print(key + " : ");
for (String value : map.getCollection(key)) {
System.out.print(value + ", ");
}
}
}
public MultiValueMap<String, String> mapKeyValue(int numbersOfKey, MultiValueMap<String, String> map){
for (int i = 0; i < numbersOfKey; i++){
//We need to create a unique key here, so let's use 'i'
//There are several ways to skin the cat and get the int to String
//Also want to create unique values, but that's up to you, they're not required to be unique
map.put(Integer.toString(i), Integer.toString(i) + "a");
map.put(Integer.toString(i), Integer.toString(i) + "b");
map.put(Integer.toString(i), Integer.toString(i) + "c");
}
//At this point, the map has in it the following key : value pairs
//"0" : ["0a", "0b", "0c"]
//"1" : ["1a", "1b", "1c"]
//"2" : ["2a", "2b", "2c"]
//Not technically required to return the map IFF the map is instantiated outside the method
return map;
}

remove similar (redundant) strings from Arraylist

I'm trying to remove similar strings from an ArrayList but I'm getting this error:
CurrentModificationException
and here is my method where I pass my original arrayList (old) and get a new list without redundant strings.
ArrayList<String> removeRed(ArrayList<String> old) throws IOException
{
ArrayList<String> newList = new ArrayList<String>();
for (int i=0; i< old.size(); i++)
{
if(newList.size() < 1)
{
newList.add(old.get(0));
} else{
for(Iterator<String> iterator = newList.iterator(); iterator.hasNext();) {
while(iterator.hasNext())
{
if(!ChopMD((String) iterator.next()).equals(ChopMD(old.get(i))))
{
newList.add(old.get(i));
Log.e("new algo", "" + old.get(i) );
}
}
}
}
}}
Note that my ChopMD() returns a particular string and it works fine.
It works fine for the first few strings, this it throws that exception. Any suggestion to resolve this issue would be appreciated it. Thanks.
If you have no problems with using the standard library (always preferable, why reinvent the wheel) try
List<String> uniques = new ArrayList<String>(new HashSet<String>(oldList));
The HashSet will only contain unique strings and the ArrayList constructor takes any Collection (including a HashSet) to build a list from.
Judging from your comments it seems like you are trying to implement an Associative Array with unique keys using an ArrayList. The better approach is to use a Map implementation like HashMap to pair IDs with their associated Strings.
Map<Integer, String> map = new HashMap<>();
map.put(1, "This string corresponds to ID=1");
map.put(3, "Donald Ducks Nephews");
map.put(7, "Is a Prime");
Then to get a value associated with an ID:
int key = someObject.getID();
String value = map.get(key);
All the Map implementations use unique keys so there is no need for you to check for redundant IDs, if you try to add a new (key,value) pair the value associated with the ID will be replaced if the map contains the key.
map.put(1, "New String");
String s = map.get(1); //s will no longer be "This string corresponds to ID=1"
If you don't want this behavior you have the choice of either subclassing one of the Map implementations to ignore .put(key, value) if the map contains key,value or delegating .put(key,value) to some other class.
Subclassing:
public class UniqueValueHashMap<K,V> extends HashMap<K, V>{
#Override
public V put(K key, V value) {
if (containsKey(key))
return null;
return super.put(key, value);
}
Delegating
public class SomeClass {
private Map<Integer, String> map = new HashMap<>();
// ...stuff this class does
public String put(int key, String value) {
if (map.containsKey(key))
return null;
return map.put(key, value);
}
// ...more stuff this class does
}
Delegation is the better approach, notice how you can change the map implementation (using maybe a TreeMap instead of HashMap) without introducing a new class where you override the .put(key,value) of TreeMap.
You can iterate much easier by this
for (String oldString : old){
for (String newString : newList){
}
}
Also you can use Set to have unique strings
Set<String> newList = new HashSet<String>();
Your error is because you are changing the list WHILE it is still iterated.

call to map.get inside For/in loop throws nullpointerexception while iterator doesn't

I broke my brain on it for a few hours, but need to carry on and found an ugly workaround, but I'd be happy to cleanup my code, here's the problem:
public static void function1(Map<String, Float> map)
{
for(String key : map.keySet()) {
Float val = map.get(key);
// val is null here, throws NPE as soon as we try to use it
}
}
public static void function2(Map<String, Float> map)
{
Iterator<Entry<String, Float>> it = map.entrySet().iterator();
while(it.hasNext()) {
Entry<String, Float> entry = it.next();
String key = entry.getKey();
Float val = entry.getValue();
// do something with key & val, works fine
}
}
the argument Map<String, Float> map of course is correctly initialized and doesn't contain any null value.
on a side note, function1 works fine if I change the argument to Map map and use string pairs only. My goal is to to have only 1 function with generics Map<? extends Object, ? extends Object> map which I could use for both type of maps.
any suggestion appreciated, thanks!
thomas
EDIT: I added some really basic introspection to make the function work with generics. I can confirm that I'm still getting null values when using the keyset, while I followed the suggestion below to use the entryset. here's my code below (the 1st function works fine, while the second returns null elements.
// yeah, it's aweful, but it works.
public static JsonNode map2JSON(Map<? extends Object, ? extends Object> map)
{
ObjectNode dummyObject = Json.newObject();
ArrayNode result = dummyObject.putArray("dummyKey");
for(Entry<?, ?> entry : map.entrySet()) {
ObjectNode mapElementNode = result.addObject();
if("java.lang.String".equalsIgnoreCase(entry.getKey().getClass().getName())) {
String key = (String)entry.getKey();
if("java.lang.Float".equalsIgnoreCase(entry.getValue().getClass().getName())) {
Float val = (Float)entry.getValue();
mapElementNode.put(key, val);
} else if("java.lang.String".equalsIgnoreCase(entry.getValue().getClass().getName())) {
String val = (String)entry.getValue();
mapElementNode.put(key, val);
}
}
}
return result;
}
// result here contains valid keys (the string part) and null values (the float part)
#Deprecated
public static JsonNode mapSF2JSON(Map<String, Float> map)
{
ObjectNode dummyObject = Json.newObject();
ArrayNode result = dummyObject.putArray("dummyKey");
for(String key : map.keySet()) {
ObjectNode mapElementNode = result.addObject();
mapElementNode.put(key, map.get(key));
}
return result;
}
You probably inserted NULL key Strings into the map. This is possible when using a HashMap.
Try to avoid adding NULL keys. Further you could use a TreeMap.
Bye the way it is not bad to iterate over the entry set, you could clean up your code
by using foreach in function2 like you have done in function1:
This will look something like:
for(Entry<String, Float> entry : map.entrySet()) {
Float val = entry.getValue();
}
Although it is not necessary to clean up function2.
But you should find the location where you inserted the NULL key into the map.
function1 uses a foreach loop, which will throw a NullPointerException when the map is null.
You should check the map to be not null before you iterate with foreach
Further You should rewrite the ugly
if("java.lang.String".equalsIgnoreCase(entry.getKey().getClass().getName())) {
to
if (entry.getKey().getClass() == String.class) {

Java HashMap: How to get a key and value by index?

I am trying to use a HashMap to map a unique string to a string ArrayList like this:
HashMap<String, ArrayList<String>>
Basically, I want to be able to access the keys by number, not by using the key's name. And I want to be able to access said key's value, to iterate over it. I'm imagining something like this:
for(all keys in my hashmap) {
for(int i=0; i < myhashmap.currentKey.getValue.size(); i++) {
// do things with the hashmaps elements
}
}
Is there an easy way to do this?
Here is the general solution if you really only want the first key's value
Object firstKey = myHashMap.keySet().toArray()[0];
Object valueForFirstKey = myHashMap.get(firstKey);
You can iterate over keys by calling map.keySet(), or iterate over the entries by calling map.entrySet(). Iterating over entries will probably be faster.
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
List<String> list = entry.getValue();
// Do things with the list
}
If you want to ensure that you iterate over the keys in the same order you inserted them then use a LinkedHashMap.
By the way, I'd recommend changing the declared type of the map to <String, List<String>>. Always best to declare types in terms of the interface rather than the implementation.
HashMaps are not ordered, unless you use a LinkedHashMap or SortedMap. In this case, you may want a LinkedHashMap. This will iterate in order of insertion (or in order of last access if you prefer). In this case, it would be
int index = 0;
for ( Map.Entry<String,ArrayList<String>> e : myHashMap.iterator().entrySet() ) {
String key = e.getKey();
ArrayList<String> val = e.getValue();
index++;
}
There is no direct get(index) in a map because it is an unordered list of key/value pairs. LinkedHashMap is a special case that keeps the order.
Kotlin HashMap Answer
You can get key by index. Then get value by key.
val item = HashMap<String, String>() // Dummy HashMap.
val keyByIndex = item.keys.elementAt(0) // Get key by index. I selected "0".
val valueOfElement = item.getValue(keyByIndex) // Get value.
You can do:
for(String key: hashMap.keySet()){
for(String value: hashMap.get(key)) {
// use the value here
}
}
This will iterate over every key, and then every value of the list associated with each key.
A solution is already selected. However, I post this solution for those who want to use an alternative approach:
// use LinkedHashMap if you want to read values from the hashmap in the same order as you put them into it
private ArrayList<String> getMapValueAt(LinkedHashMap<String, ArrayList<String>> hashMap, int index)
{
Map.Entry<String, ArrayList<String>> entry = (Map.Entry<String, ArrayList<String>>) hashMap.entrySet().toArray()[index];
return entry.getValue();
}
for (Object key : data.keySet()) {
String lKey = (String) key;
List<String> list = data.get(key);
}
I came across the same problem, read a couple of answers from different related questions and came up with my own class.
public class IndexableMap<K, V> extends HashMap<K, V> {
private LinkedList<K> keyList = new LinkedList<>();
#Override
public V put(K key, V value) {
if (!keyList.contains(key))
keyList.add(key);
return super.put(key, value);
}
#Override
public void putAll(Map<? extends K, ? extends V> m) {
for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
#Override
public void clear() {
keyList.clear();
super.clear();
}
public List<K> getKeys() {
return keyList;
}
public int getKeyIndex(K key) {
return keyList.indexOf(key);
}
public K getKeyAt(int index) {
if (keyList.size() > index)
return keyList.get(index);
return null;
}
public V getValueAt(int index) {
K key = getKeyAt(index);
if (key != null)
return get(key);
return null;
}
}
Example (types are differing from OPs question just for clarity):
Map<String, Double> myMap = new IndexableMap<>();
List<String> keys = myMap.getKeys();
int keyIndex = myMap.getKeyIndex("keyString");
String key = myMap.getKeyAt(2);
Double value myMap.getValueAt(2);
Keep in mind that it does not override any of the complex methods, so you will need to do this on your own if you want to reliably access one of these.
Edit: I made a change to the putAll() method, because the old one had a rare chance to cause HashMap and LinkedList being in different states.
Try this:
myhashmap.entrySet()
.forEach{
println(it.getKey())
println(it.getValue())
}
or if you want by index
myhashmap.entrySet()[0].getKey()
myhashmap.entrySet()[0].getValue()
myhashmap.entrySet()[1].getKey()
myhashmap.entrySet()[1].getValue()
HashMaps don't keep your key/value pairs in a specific order. They are ordered based on the hash that each key's returns from its Object.hashCode() method. You can however iterate over the set of key/value pairs using an iterator with:
for (String key : hashmap.keySet())
{
for (list : hashmap.get(key))
{
//list.toString()
}
}
If you don't care about the actual key, a concise way to iterate over all the Map's values would be to use its values() method
Map<String, List<String>> myMap;
for ( List<String> stringList : myMap.values() ) {
for ( String myString : stringList ) {
// process the string here
}
}
The values() method is part of the Map interface and returns a Collection view of the values in the map.
You can use Kotlin extension function
fun LinkedHashMap<String, String>.getKeyByPosition(position: Int) =
this.keys.toTypedArray()[position]
fun LinkedHashMap<String, String>.getValueByPosition(position: Int) =
this.values.toTypedArray()[position]
You'll need to create multiple HashMaps like this for example
Map<String, String> fruitDetails = new HashMap();
fruitDetails.put("Mango", "Mango is a delicious fruit!");
fruitDetails.put("Guava" "Guava is a delicious fruit!");
fruitDetails.put("Pineapple", "Pineapple is a delicious fruit!");
Map<String, String> fruitDetails2 = new HashMap();
fruitDetails2.put("Orange", "Orange is a delicious fruit!");
fruitDetails2.put("Banana" "Banana is a delicious fruit!");
fruitDetails2.put("Apple", "Apple is a delicious fruit!");
// STEP 2: Create a numeric key based HashMap containing fruitDetails so we can access them by index
Map<Integer, Map<String, String>> hashMap = new HashMap();
hashMap.put(0, fruitDetails);
hashMap.put(1, fruitDetails2);
// Now we can successfully access the fruitDetails by index like this
String fruit1 = hashMap.get(0).get("Guava");
String fruit2 = hashMap.get(1).get("Apple");
System.out.println(fruit1); // outputs: Guava is a delicious fruit!
System.out.println(fruit2); // outputs: Apple is a delicious fruit!

Categories

Resources