Is there a way to recursively traverse a HashMap so that value1 of key1 is actually the new key2 which returns value2 that again will be the next key3 and so on ... till it returns null? The logic is as follows:
hm.get(key)
hm.get(hm.get(key))
hm.get(hm.get(hm.get(key)))
......
I'm assuming this may be done through some recursion procedure? Please correct me if I were wrong. Thanks!
Is this the one you wanted procedure? it will return the ultimate value by traversing the hashmap:
Public Object traverseMap(Object key)
while(hm.get(key) != null){
key = hm.get(key);
}
return key;
}
If the hashmap would be set up this way (i.e. it contains a value which is also the key for another value) it would be possible. You could do that in a recursive method but a loop would be sufficient:
Object key = someInitialKey;
Object value = null;
do {
value = hm.get( key );
key = value;
} while( value != null );
Well, anyway, that's the (tail!) recursive version you asked for:
public class Qsdf {
public static Object traverseMap(Map m, Object key) {
return traverseMap(m, key, new HashSet());
}
public static Object traverseMap(Map m, Object key, Set traversed) {
if (key == null) { // first key has to be null
throw new NullPointerException();
}
traversed.add(key);
Object value = m.get(key);
if (traversed.contains(value)) { // added after Stephen C's comment on other answer
// cycle found, either throw exception, return null, or return key
return key;
}
return value != null ?
traverseMap(m, value, traversed) :
key; // I guess you want to return the last value that isn't also a key
}
public static void main(String[] args) {
final HashMap<Integer, Integer> m = new HashMap<Integer, Integer>();
m.put(0, 1);
m.put(1, 2);
m.put(3, 4);
m.put(2, 3);
final Object o = traverseMap(m, 0);
System.out.println(o);
}
}
Related
I have a list of string, and I want to be able to group them hierarchically.
Example of the list:
var list = new String[]{"caso.id",
"caso.unidadeDoCaso.id",
"caso.etiqueta",
"caso.sigiloso",
"caso.idPecaSegredoJustica",
"caso.numeroAno",
"caso.numero",
"caso.competencia.id",
"caso.competencia.ativo",
"caso.competencia.nome",
"caso.responsavel.id",
"caso.responsavel.dadosPessoais.nome",
"caso.escrivao.id",
"caso.escrivao.dadosPessoais.nome"};
I want to group them in Maps.
Like:
caso->
id
sigiloso,
...
unidadeDoCaso->
id
competencia->
id
ativo
...
responsavel->
id
dadosPessoais->
nome
...
...
...
I was able to group just one level. I was wondering if there's a way to do it recursively.
In spite of my suggestion I decided to provide this. There are two recursive routines.
one to fill the map.
the other to print it.
String[] array = {
"caso.id","caso.unidadeDoCaso.id","caso.etiqueta",
"caso.sigiloso","caso.idPecaSegredoJustica","caso.numeroAno",
"caso.numero","caso.competencia.id","caso.competencia.ativo",
"caso.competencia.nome","caso.responsavel.id",
"caso.responsavel.dadosPessoais.nome","caso.escrivao.id",
"caso.escrivao.dadosPessoais.nome"
};
Create the map
Then iterated across the data, splitting on the dot.
then call fill with the map, just split nodes, and the starting node index.
Map<String, Object> map = new HashMap<>();
for (String s : array) {
String[] nodes = s.split("\\.");
fill(map, nodes, 0);
}
print(map, "");
prints
caso
unidadeDoCaso
id
etiqueta
idPecaSegredoJustica
escrivao
id
dadosPessoais
nome
sigiloso
numero
id
numeroAno
responsavel
id
dadosPessoais
nome
competencia
ativo
nome
id
The fill method continues until the supplied nodes are all processed.
first the map is checked to see if the node exists or not(equal to null)
if not present, a new map is constructed and added to the supplied map. Then the method is called to process the next node.
otherwise, the method is called to add the current node to the map after the one that exists and continue processing the nodes.
public static void fill(Map<String, Object> map, String[] nodes, int i) {
if (i >= nodes.length) {
return;
}
String node = nodes[i];
#SuppressWarnings("unchecked")
Map<String, Object> obj = (Map<String, Object>)(node);
if (obj == null) {
Map<String, Object> m = new HashMap<>();
map.put(node, m);
fill(m, nodes, i + 1);
} else {
fill( obj, nodes, i + 1);
}
}
This prints the map elements and indents each subsequent nested map level on a separate line.
#SuppressWarnings("unchecked")
public static void print(Map<String, Object> map, String indent) {
for (String key : map.keySet()) {
if (key != null) {
System.out.println(indent + key);
print((Map<String, Object>) map.get(key), indent + " ");
}
}
}
Here is how you could do this using a Map<String, Map> and mutable reduction using the collect method that takes a supplier, accumulator, and combiner. The API is not the most pleasant to use, as WJS pointed out.
It requires unchecked casts because you can't represent these recursive structures of unknown depth using generics.
class Scratch {
public static void main(String[] args) {
var list = new String[]{"caso.id",
"caso.unidadeDoCaso.id",
"caso.etiqueta",
"caso.sigiloso",
"caso.idPecaSegredoJustica",
"caso.numeroAno",
"caso.numero",
"caso.competencia.id",
"caso.competencia.ativo",
"caso.competencia.nome",
"caso.responsavel.id",
"caso.responsavel.dadosPessoais.nome",
"caso.escrivao.id",
"caso.escrivao.dadosPessoais.nome"};
Map<String, Map> result = Arrays.stream(list).collect(HashMap::new, Scratch::mapRecursively, HashMap::putAll);
System.out.println(result);
// {caso={unidadeDoCaso={id=null}, etiqueta=null, idPecaSegredoJustica=null, escrivao={id=null, dadosPessoais={nome=null}}, sigiloso=null, numero=null, id=null, numeroAno=null, responsavel={id=null, dadosPessoais={nome=null}}, competencia={ativo=null, nome=null, id=null}}}
System.out.println(result.get("caso").keySet());
// [unidadeDoCaso, etiqueta, idPecaSegredoJustica, escrivao, sigiloso, numero, id, numeroAno, responsavel, competencia]
}
#SuppressWarnings("unchecked")
private static void mapRecursively(HashMap<String, Map> map, String s) {
// first recursion: s = caso.competencia.id
// second recursion: s = id
int dot = s.indexOf('.');
// Base case 1
if (dot == -1) {
map.put(s, null);
return;
}
String key = s.substring(0, dot); // caso
String value = s.substring(dot + 1); // competencia.id
boolean isFirstTimeToComeAcrossWord = !map.containsKey(key);
if (isFirstTimeToComeAcrossWord) {
map.put(key, new HashMap<>());
}
// Base case 2
int dot2 = value.indexOf('.');
if (dot2 == -1) {
map.get(key).put(value, null);
return;
}
String newKey = value.substring(0, dot2); // competencia
String leftover = value.substring(dot2 + 1); // id
boolean isFirstTimeWeComeAcrossNestedWord = !map.get(key).containsKey(newKey);
// Recursive cases
if (isFirstTimeWeComeAcrossNestedWord) {
var newMap = new HashMap<String, Map>();
map.get(key).put(newKey, newMap);
mapRecursively(newMap, leftover);
} else {
mapRecursively((HashMap<String, Map>) map.get(key).get(newKey), leftover);
}
}
}
For the following snippet, I see that the latest insertion replaces the old, i.e. the data in Line 2 replaces the one in Line 1.
Map<String, String> someMap = new HashMap<>();
someMap.put("A", "101A9901"); // Line 1
someMap.put("B", "102Z4902");
someMap.put("A", "103C5389"); // Line 2
However, while working with a custom Key class, the old one is retained the new one is never added, i.e. line 1 is not replaced with line 2
Order order1 = new Order(101L, 201L, new BigDecimal(284.50), "Preparing");
Order order2 = new Order(102L, 204L, new BigDecimal(780.00), "Dispatched");
Order order3 = new Order(101L, 201L, new BigDecimal(284.50), "Cancelled");
Order order4 = new Order(104L, 207L, new BigDecimal(550.00), "Cancelled");
Order order5 = new Order(105L, 203L, new BigDecimal(320.50), "Confirmed");
Order order6 = new Order(106L, 207L, new BigDecimal(470.00), "Delivered");
Map<Order, String> orderMapAddtionalStatus = new HashMap<>();
orderMapAddtionalStatus.put(order1, "OK"); // line 1
orderMapAddtionalStatus.put(order2, "OK");
orderMapAddtionalStatus.put(order3, "OK"); // line 2
orderMapAddtionalStatus.put(order4, "OK");
orderMapAddtionalStatus.put(order5, "OK");
orderMapAddtionalStatus.put(order6, "OK");
I have also overridden the equals and hashCode method. Following are the overridden methods -
#Override
public int hashCode() {
int result = getOrderId().hashCode();
result = 31 * result + getCustomerId().hashCode();
return result;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Order)) return false;
Order order = (Order) o;
if (!getOrderId().equals(order.getOrderId())) return false;
return getCustomerId().equals(order.getCustomerId());
}
Can some please tell me what I might be missing?
If you have a look at HashMap.putVal(...), the method that is used interally, you'll see the following:
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
This means the key is added to a new node if it wasn't already present.
However, if there already is a key this portion is executed after the node has been found:
Node<K,V> e;
... //code to find the existing node
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
As you can see, the key is not replaced and that's consistent with the JavaDoc on put(...):
If the map previously contained a mapping for the key, the oldvalue is replaced.
This states the value is replaced, not the key. From the perspective of the map this is consistent since equals() and hashCode() state the keys are the same so either of both (the existing and the new key) can be used.
So I'm trying to create a smart data structure based off AVL tree and Hash Table.
I'm making sure I need to check first which implementation the data type will have depending on the size the list given to it.
For example, if I have a list n of size 1000, it'll be implemented using a Hash table. For anything more than 1000, using an AVL tree.
Code for this:
public class SmartULS<K,V> {
protected TreeMap<K,V> tree = new TreeMap<>();
protected AbstractHashMap<K,V> hashMap = new AbstractHashMap<K,V>();
public void setSmartThresholdULS(size){
int threshold = 1000;
if (size >= threshold) {
map = new AbtractMap<K,V>();
}
else
map = new TreeMap<K,V>();
}
}
Now after this, I should be writing the standard methods such as
get(SmartULS, Key), add(SmartULS, Key, Value), remove(SmartULS,Key), nextKey(Key), previousKey(Key), etc.
I'm really lost as to how to start this? I've thought about creating these methods like this(written in pseudo):
Algorithm add(SmartULS, Key, Value):
i<- 0
If SmartULS instanceof AbstractHashMap then
For i to SmartULS.size do
If Key equals to SmartULS[i] then
SmartULS.get(Key).setValue(Value)
Else
SmartULS.add(Key, Value)
Else if SmartULS instanceof TreeMap then
Entry newAdd equals new MapEntry(Key, Value)
Position<Entry> p = treeSearch(root( ), Key)
You're on the correct track, this is how I understood your question and implemented it:
public class SmartULS<K, V> {
Map<K,V> map;
public static final int THRESHOLD = 1000;
public SmartULS(int size) {
if(size < THRESHOLD) {
map = new HashMap();
} else {
map = new TreeMap();
}
}
public V get(K key) {
return map.get(key);
}
public V put(K key, V value) {
return map.put(key, value);
}
public V remove(K key) {
return map.remove(key);
}
}
Based on the initial size given, the constructor decides if to initialize a hash table or a tree. I also added a the get, put and remove functions and used the Map's interface functions.
I didn't understand what the nextKey and previousKey functions are suppose to do or return, so couldn't help there.
A way of using the class would be as follows:
public static void main(String[] args) {
SmartULS<String, String> smartULS = new SmartULS(952);
smartULS.put("firstKey", "firstValue");
smartULS.put("secondKey", "secondsValue");
String value = smartULS.get("firstKey");
smartULS.remove("secondKey");
}
Hope this helps:)
My hashmap class is as follows:
public HashMap<String, Integer> getWordCounts() {
HashMap<String, Integer> map = new HashMap<String, Integer>();
String[] quoteOne = getWordArray();
for (String stuff : quoteOne) {
map.put(stuff, +1);
}
return map;
}
As it goes through quoteOne I want it to put each word from the array into the hashmap but for duplicates add 1 to the integer. e.g. "If you see this you are cool" would be put into the hashmap as
if 1
you 2
see 1
this 1
are 1
cool 1
But my code is outting it into the hashmap with you 1. What is wrong?
In your code, for every word you see, you put +1 (the int value of positive 1).
You need to update the value, not override it.
for (String stuff : quoteOne) {
Integer oldVal = map.get(stuff);
if (oldVal == null) {
oldVal = 0;
}
map.put(stuff, oldVal+1);
}
Your for loop will be
for (String stuff : quoteOne) {
if(map.get(stuff) != null){
int i = map.get(stuff);
map.put(stuff,i+1)
}else{
map.put(stuff, 1);
}
}
HashMap replaces values if same key is provided.
From Java doc of HashMap#put
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.
Try something like this
for(String w : words) {
Integer i = wordCounts.get(w);
if(i == null) wordCounts.put(w, 1);
else wordCounts.put(w, i + 1);
}
for(String i: quoteOne)
map.put(i, (map.get(i)!=null)? map.get(i)+1:1);
I am extending AbstractMap and I want to implement my own hash-map using two parallel arrays:
K[] keys;
V[] values;
Suppose I want to store null values as well, how could I initialize these two arrays so that I can differentiate between a space in the array where I could place some new key-value pairs and a space where I am storing a null?
Might I suggest not using two arrays, and instead do something along the lines of:
class Node {
K key;
V value;
}
Node[] nodes;
Then a non-entry is an element in nodes that is equal to null.
If the values can be null but the keys cannot be null then having a null key would mean that there is no key.
If the key can also be null you can use a parallel array of booleans to store whether each space is taken or not.
K[] keys;
V[] values;
boolean[] hasValue;
Not quite sure the details of your question, but you could always have some special object for your "blank".
private static final Object BLANK = new Object();
Then if the item in the array == BLANK, then consider it to be an empty slot.
Since there can only be one null key, you can simply have a special reference value (not in the array) that holds the value of the object mapped from this null key (and possibly a boolean indicating if this value has been set). Unfortunately this will probably complicate iteration.
E.g.
private boolean isNullMapped = false;
private V nullValue = null;
public put(K key, V value)
{
if (key == null) { nullValue = value; }
...
}
Alternatively, you can wrap all keys in a wrapper object (supposing you still want to use parallel arrays instead of entries), and if the value contained in this wrapper object is null, then it represents the null key.
E.g.
private static class KeyWrapper<K>
{
public K key;
}
Lastly, as a question for consideration, if you are not having entries in your arrays, but instead are directly holding arrays of K and V, then how are you accounting for different keys that happen to share the same hash code? The java.util implementation has arrays of entries that also act as linked lists to account for this possibility (and incidentally, the null key is always mapped to array index 0).
Storing a null value is not a problem in your scenario. So long as keys[n] != null, just return values[n] whether values[n] is null or not.
Remember that you are not being asked to key on n but objects of type K so every access of the Map will require a search through keys to find the key they are looking for.
However, if you want to allow the storage of a value against a null key then using something like private static final Object NULL_KEY = "NULL" would probably do the trick as the other suggestions point out.
private static final Object NULL_KEY = "NULL";
K[] keys;
V[] values;
private int find(K key) {
for (int i = 0; i < keys.length; i++) {
if (keys[i] == key) {
return i;
}
}
return -1;
}
public V put(K key, V value) {
V old = null;
if (key != null) {
int i = find(key);
if (i >= 0) {
old = values[i];
values[i] = value;
} else {
// ...
}
} else {
return put((K) NULL_KEY, value);
}
return old;
}
public V get(K key) {
if (key != null) {
int i = find(key);
if (i >= 0) {
return values[i];
}
return null;
} else {
return (get((K) NULL_KEY));
}
}
In the java.util implementation a special object representing null is used.