How to Map Values to a Nested TreeMap - java

Nested TreeMap:
TreeMap<String,TreeMap<String,TreeMap<String,String>>> map = new TreeMap<>();
Trying to map things to the TreeMap:
add("1","1","1","111",map);
map.add("1","1","1","111");
map.put("1", ("1",("1","111")));
I am trying to map things to the nested TreeMap as seen above, but nothing I have tried has worked. What is the proper way to do what I am attempting?

You'll have to do it one step at a time, e.g. the simple case assuming all child maps already exist (note that we get the existing maps):
map.get("1").get("1").put("1", "111");
However, this isn't the case based on your description, and so each step of the way you'll have to create a new entry if one doesn't exist, so it becomes more complicated, as you have to look up the current map and then create/add:
// from your example:
String key1 = "1";
String key2 = "1";
String key3 = "1";
String value = "111";
// insert if doesn't exist yet:
TreeMap<String,TreeMap<String,String>> map1 = map.get(key1);
if (map1 == null) {
map1 = new TreeMap<String,TreeMap<String,String>>();
map.put(key1, map1);
}
// and again:
TreeMap<String,String> map2 = map1.get(key2);
if (map2 == null) {
map2 = new TreeMap<String,String>();
map1.put(key2, map2);
}
// and now we're set up and ready to go:
map2.put(key3, map3);
Since it's kind of cumbersome, it generally helps to write a utility function to do this for you.
Alternatively, if it is appropriate for your application, you could consider collapsing your entire structure into a single map and using a more complex key, for example:
static class ComplicatedKey implements Comparable<ComplicatedKey> {
String key1;
String key2;
String key3;
public ComplicatedKey (String key1, String key2, String key3) { ... }
// implement equals and compareTo appropriately.
}
Then:
TreeMap<ComplicatedKey,String> map = ...;
map.put(new ComplicatedKey("1", "1", "1"), "111");
Yet another option is to roll your own multi-level tree, you could even use a TreeMap internally in each of your nodes to maintain lists of child nodes.

Related

Java HashMap key fits to a pattern?

I have a Map dataset, and I want to iterate through the keys and search for matches.
So I want to find the maps element, where the key fits to this pattern:
String searchedKey = "A?C"; // ? means it can be any character
Map<String, MyObject> myMap = new HashMap<>();
myMap.put("ABC", MyObject(1));
myMap.put("CDF", MyObject(2));
myMap.put("ADS", MyObject(3));
for (Map.Entry<String,MyObject> entry : myMap.entrySet()) {
// in this case, I want to find the first element, because it's key fits the searchedKey, where ? can be anything
}
How can I do this?
Thanks!
You could do something like this to return a list of found MyObjects. Note I changed ? to . for any character.
String searchedKey = "A.C"; // ? means it can be any character
Map<String, MyObject> myMap = new HashMap<>();
myMap.put("ABC", new MyObject(1));
myMap.put("CDF", new MyObject(2));
myMap.put("ARS", new MyObject(3));
myMap.put("VS", new MyObject(4));
myMap.put("AQC", new MyObject(3));
myMap.put("DS", new MyObject(3));
myMap.put("ASC", new MyObject(10));
List<Map.Entry<String,MyObject>> list = myMap.entrySet().stream()
.filter(e -> e.getKey().matches(searchedKey))
.collect(Collectors.toList());
list.forEach(System.out::println);
Prints
ASC=10
ABC=1
AQC=3
The MyObject class
class MyObject {
int val;
public MyObject(int v) {
this.val = v;
}
public String toString() {
return val + "";
}
}
You could use Regex-Patterns that allow to search Strings for matchings of a logical sequence using String#matches(String).
Here is a page that might help you create and test a regex for your needs. You might also have to construct your pattern flexible during runtime, depending on how your search works.
Tho keep in mind that a HashMap does not keep the order in which the keys were inserted. keySet() does not return them in a fixed order. If you need them ordered, you could use a LinkedHashMap

How to put together multiple Maps <Character, Set<String>> without overriding Sets

In my project I am using two maps Map<Character, Set<String>>.
map1 - is temporally holding needed values
map2 - is summing all data from map1 after each loop
for example i got:
map2 = (B; Beryllium, Boron, Bromine)
map2 = (H; Hellum, Hydrogen, Hafnium)
now new map1 is:
map1 = (B; Bismuth)
map1 = (O; Oxygen)
In my code adding Oxygen as new entry is ok, but adding new entry for B ends by overraidding existing data in values and leave me only Bismuth.
My code:
while (iterator.hasNext()) {
Set<String> words = new TreeSet<>();
String word = iterator.next();
char[] wordChars = word.toCharArray();
//some code
words.add(word);
map1.put(wordChars[i], words);
}
map2.putAll(map1);
I tought about using .merge but I have no idea how to use it with Sets as values, and I cannot use simple Strings with concat.
You can use Map#merge like this:
Map<String, Set<String>> map1; // [key="B";values=["Beryllium", "Boron", "Bromine"]]
Map<String, Set<String>> map2; // [key="B";values=["Bismuth"] key="I";values=["Iron"]]
for (Entry<String, Set<String>> entry : map2.entrySet()) {
map1.merge(entry.getKey(), entry.getValue(), (s1, s2) -> {s1.addAll(s2); return s1;});
}
//map1 = [key="B";values=["Beryllium", "Boron", "Bromine", "Bismuth"] key="I";values=["Iron"]]
Map::compute is probably what you're looking for. This gives you a way to map any existing value (if there is one), or provide one if not.
For example, in your case something like the following would probably suffice:
oldMap.compute("B", current -> {
if (current == null) {
// No existing entry, so use newMap's one
return newMap.get("B");
} else {
// There was an existing value, so combine the Sets
final Set<String> newValue = new HashSet<>(current);
newValue.addAll(newMap.get("B"));
return newValue;
}
});
There's also MultiValueMap and Multimap from spring and guava respectively (if you're ok bringing in dependencies) which cover this case with less work already.
Temporary map1 will not be needed in this case. Get the set for that character, if null create a new set. Add the word to that set and put in the map:
while (iterator.hasNext()) {
String word = iterator.next();
//some code
Set<String> words = map2.get(word.charAt(0));
if(words == null) {
words = new TreeSet<>();
}
words.add(word);
map2.put(word.charAt(0), words);
}
When using the merge() function, if the specified key is not already associated with a value or the value is null, it associates the key with the given value.
Otherwise, i.e if the key is associated with a value, it replaces the value with the results of the given remapping function. So in order to do not overwrite the old value you must write your remapping function so that it combines the old and new values.
To do so replace this line :
map2.putAll(map1);
with
map1.forEach( (key, value)->{
map2.merge(key, value, (value1,value2) -> Stream.of(value1,value2)
.flatMap(Set::stream)
.collect(Collectors.toSet()));
});
This will iterate over map1 and add echh key which is not present into map2 and associate it with the given value and for each key which is already present it combines the old values and new values.
Alternative you can also work with Map.computeIfPresent and Map.putIfAbsent
map1.forEach( (key, value)->{
map2.computeIfPresent(key, (k,v) -> Stream.of(v,value).flatMap(Set::stream).collect(Collectors.toSet()));
map2.putIfAbsent(key, value);
});

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.

how to get the values from hash map and put them in array?

I am trying to pull the values from hash map and put them in array, but I keep getting a null pointer exception.
Is there any other way to do this?
//some code....
String n[]=new String[tcur.getCount()];;
String t[]=new String[tcur.getCount()];;
HashMap<Integer, String> typehashmap=new HashMap<Integer, String>();
HashMap<Integer, String> namehashmap=new HashMap<Integer, String>();
//some code..
String type[]=typehashmap.keySet().toArray(new String[typehashmap.size()]);
String name[]=namehashmap.keySet().toArray(new String[namehashmap.size()]);
for (int i=0;i<=type.length;i++) {
n[i]=namehashmap.get(nameiterator[i]).toString();
System.out.println(n[i]);
t[i]=typehashmap.get(typeiterator[i]).toString();
System.out.println (t[i]);
}
Why don't you use this?
Set<Integer> keys = namehashmap.keySet();
Collection<String> values = namehashmap.values();
You can work with values collection many ways.
for(String value:values) { ... }
If you still want arrays, maybe you want do:
keys.toArray();
values.toArray();
Real question is, what do you try to accomplish? Are your two HashMaps interconnected, say entries with the same integer key in both maps belong together? If so, I would first consider making a wrapper class for this e.g.:
private static class TypeName {
private String type;
private String name;
}
And using a HashMap
Furthermore, IMHO the best way to iterate over a HashMap is to use the entry set:
Map<A,B> map = new HashMap<A,B>();
for(Entry<A,B> entry : map.entrySet()) {
A a = entry.getKey();
B b = entry.getValue();
// do things
}

Categories

Resources