I want to do something like in this example. I can't figure out why it is not working.
myMap has no value in the main class. But if I put the value in the "putSomethingInTheMap" into the map it has the right value in the main class.
Can you give me a suggestion how to handle something like this?
public static void main(String[] args) {
Map<String, Integer> meineMap = new HashMap<>();
int wert = 1;
putSomethingInTheMap(meineMap, wert);
System.out.println(meineMap.get("A"));
}
private static void putSomethingInTheMap(Map<String, Integer> myMap, int value) {
myMap = insert(value);
}
private static Map<String, Integer> insert(int wert) {
Map<String, Integer> map = new HashMap<>();
map.put("A", wert);
return map;
}
private static void putSomethingInTheMap(Map<String, Integer> myMap, int value) {
myMap.put("A", wert);
}
Basically you are doing it method pass by refrence . In main class your creating a map and passing it to putSomethingInTheMap where it is assigned by the map returned by insert.
So if you have value in main class it is due to refrence passed to method.
Because meineMap reference is same as original. You declared a new reference with new value in insert method, and java method call is pass by value, but you are pass a reference that passed as value in parameter of putSomethingInTheMap!
private static Map<String, Integer> insert(int wert) {
Map<String, Integer> map = new HashMap<>();
map.put("A", wert);
return map;
}
This part is the mistake because what you are doing here is, for every insert operation in a map you are creating a new map ( that will be empty ofcourse) and adding a value in this map and returning this map.
Now
myMap = insert(value);
call will always get a map with only 1 value every time he makes an insert operation.
Fix:
You don't need to create a new map in insert( int wert ), you just need to call put() of java map. Code for solution is already posted by #Maurice Perry
Related
I am trying to write a class that has a Map as a field. The field is as follows:
Map<String, Collection<String>> courses;
In the constructor, I have to have the field in the form:
Map<String, Set<String>;
without changing the field at all.
I am getting an error when I try to initialize the field with the set. Can someone tell me why or what to do without altering the original field?
Despite Set<String> is actually a subtype of Collection<String>, a Map<String, Set<String>> is not a subtype of Map<String, Collection<String>>. In fact, they are completely different types and you can't assign one to the other.
Luckily, the Map interface defines the putAll method, which has the following signature:
void putAll(Map<? extends K,? extends V> m)
This means that the putAll method accepts a map whose keys and values might be of types that are subtypes of its own key and value types, respectively.
So, in your example, you could do as follows:
public class YourClass {
private final Map<String, Collection<String>> courses = new HashMap<>();
public YourClass(Map<String, Set<String>> courses) {
this.courses.putAll(courses);
}
}
You only have to make sure that the courses attribute has been instantiated before invoking putAll on it.
I'm not sure what actual question is about, but...
code below is working because of Type erasure at Runtime
public class SimpleTest {
protected Map<String, ? extends Collection<String>> courses;
public SimpleTest(Map<String,Set<String>> setsMap)
{
courses = setsMap;
}
public static void main(String... args) {
Map<String, ? extends Collection<String>> setMap = new HashMap<String, Set<String>>();
SimpleTest stInstance = new SimpleTest((Map<String, Set<String>>) setMap);
String str1 = "Hi";
String str2 = "Hello";
Set<String> stringSet = new HashSet<>();
stringSet.add(str1);
List<String> stringList = new ArrayList<>();
stringList.add(str2);
((Map<String, Collection<String>>)setMap).put("set1", stringSet);
((Map<String, Collection<String>>)setMap).put("list1", stringList);
System.out.println("set1 class: " + stInstance.courses.get("set1").getClass().getName());
System.out.println("list1 class: " + stInstance.courses.get("list1").getClass().getName());
System.out.println("map content: " + stInstance.courses);
}
}
output is:
set1 class:java.util.HashSet
list1 class:java.util.ArrayList
map content:{list1=[Hello], set1=[Hi]}
PS. I do not recommend to use such "technique", at all.
But as experiment it is interesting and funny :-)
I'm trying to retrieve values from a nested hashmap depending on user selection. I'm having trouble getting the values from the nested Hashmap.
I've created the HashMap like this
private Map<String, Map<String, String>> contentTypesMap = new HashMap<String, Map<String, String>>();
If the user selects MDX i want to go to the nested HashMap and get the two string values in the 2nd hashmap.
contentTypesMap.put("MDX", new HashMap<String,String>()).put("HELLO1", "FOO");
And this my function for where to use hashmap,
public void getDatabaseSelectionValues (){
resourceType = (String) FileUtilityScreen.contentTypes.getSelectedItem();
sqlTableName = contentTypesMap.get(resourceType).get(key);
languageSelected = FileUtilityScreen.languagesFromDataBase.get(
FileUtilityScreen.languageDropDown.getSelectedItem());
}
with your exemple, if the selection is "MDX", you get your second map by doing
HashMap<String,String> selection = contentTypesMap.get("MDX")
then you can use entrySet() method to retrieve all entries of your map and iterate over entries.
See the documentation from Map.put(K key, V value)
#return the previous value associated with key, or
null if there was no mapping for key
So calling contentTypesMap.put("MDX", new HashMap<String,String>()) will always return the value that is not in the contentTypesMap map anymore.
Solution :
HashMap<String, String> map = contentTypesMap.get("MDX");
if (map == null) {
map = new HashMap<>();
contentTypesMap.put("MDX", map);
}
map.put("HELLO1", "FOO");
Or you can use guava Multimap instead.
Is the HELLO1 and FOO meant to be the value for MDX?
You are not putting it correct. Try this:
Map<String, String> value = new HashMap<String, String>();
value.put("HELLO1", "FOO");
contentTypesMap.put("MDX", value);
I'd also consider wrapping this outer map in its own object as it may get complex.
What I meant by a wrapper class in my comment above was something along these lines, to make it more readable. I'm assuming the key in the outer map is the table name, and in the inner map is the column name and data type for that column.
public class DatabaseTables {
private Map<String, Map<String, String>> dbTables = new HashMap<String, Map<String, String>>();
public void addColumn(String tableName, String column, String type){
Map<String, String> columns = dbTables.get(tableName);
if(dbTables.get(tableName) == null) {
columns = new HashMap<String, String>();
}
columns.put(column, type);
dbTables.put(tableName, columns);
}
public Map<String,String> getColumnsForTable(String tableName){
return dbTables.get(tableName);
}
}
That still feels ugly though. I think a better solution than this, would be to have a class called Table, with a string property for the table name, and a map for its columns and data types.
And then, elsewhere, you could have a list or a set of type Table.
Edit: Or, in Table.java, rather than a map for the columns, you could have a list or set of type Column. And then create a column class with two fields, one for the column name and the other for the type.
I am trying to add a Key(String), Value(HashMap) to another HashMap. I somehow keep jumbling up the syntax and logic here. How do I do that? I have a kmap here initialized and then I want to add a key which is a string, and a value which is another HashMap<String, List<Integer>>)
These can be seen in the parameters below:
static HashMap<String, HashMap<String, List<Integer>>> kmap = new HashMap<String, HashMap<String, List<Integer>>>();
public synchronized static void AddMapToList_ofMAP_(HashMap<String, List<Integer>> value, String key) {
if (!kmap.containsKey(key)) {
kmap.put(key, new HashMap<String, List<Integer>>());
}
HashMap<String, List<Integer>> q = kmap.get(key);
q.put(key, value);
}
It's WAY simpler than you're making it seem. You don't need a separate method, just invoke the put(key, value) method on your kmap. Assuming you have a Map<String, List<Integer>> in a variable named value, it's just:
kmap.put(key, value);
That's all. Just one line.
In your parameters you have got a HashMap called value. You are then trying to add that to the HashMap inside the HashMap but the value in that needs to be a List of integers.
Fix:
static HashMap<String, HashMap<String, List<Integer>>> kmap = new HashMap<String, HashMap<String, List<Integer>>>();
public synchronized static void AddMapToList_ofMAP_(
List<Integer> value, String key) {
if (!kmap.containsKey(key)) {
kmap.put(key, new HashMap<String, List<Integer>>());
}
HashMap<String, List<Integer>> q = kmap.get(key);
q.put(key, value);
}
Also, a possible way to make this better is using an Object. I'm not sure how the code and what your putting in but an object could work.
I'm also seeing you get the HashMap by the key but you also put that key in the HashMap (The one inside), surely you could just have 1 HashMap there.
I'm not sure what exactly you are trying to achieve here. Here's what possibly you may want to do. Feel free to write more test cases and optimize the code. However this will give you a based structure to work on.
public class Stackoverflow {
static HashMap<String, HashMap<String, List<Integer>>> kmap = new HashMap<String, HashMap<String, List<Integer>>>();
public synchronized static void addIntegerToKmap(String kmapKey, String intMapKey, Integer value) {
if (!kmap.containsKey(kmapKey)) {
Map<String, List<Integer>> intMap = new HashMap<String, List<Integer>>();
HashMap<String, List<Integer>> stringListHashMap = new HashMap<String, List<Integer>>();
List<Integer> integerList = new ArrayList<Integer>();
integerList.add(value);
stringListHashMap.put(intMapKey, integerList);
kmap.put(kmapKey, stringListHashMap);
}
else {
HashMap<String, List<Integer>> stringListHashMap = kmap.get(kmapKey);
List<Integer> integerList = stringListHashMap.get(intMapKey);
if (integerList != null && !integerList.isEmpty()) {
integerList.add(value);
}
else {
integerList = new ArrayList<Integer>();
integerList.add(value);
stringListHashMap.put(intMapKey, integerList);
}
}
}
public static void main(String args[]) {
addIntegerToKmap("A", "A1", 1);
addIntegerToKmap("A", "A1", 2);
addIntegerToKmap("A", "A2", 12);
addIntegerToKmap("A", "A2", 22);
addIntegerToKmap("B", "B1", 1);
addIntegerToKmap("A", "A1", 3);
}
}
The logic is not clear but maybe you want this
q.put(key, value.get(key));
instead of this:
q.put(key, value);
I thought this would be easy but I am struggling...I want to send 2 Strings to a method and have one as the map key and the other as the name of the set;
Edit: Map declaration;
Edit 2 to add constructor for map;
public HashMap<String, Set<String>> playerMap; //edit 1
public Planets()
{
playerMap = new HashMap<>();
} //edit 2
public void addMapEntry(String newPlayerAdd, String newPlanetAdd)
{
playerMap.put(newPlayerAdd, newPlanetAdd);
}
and am getting error message
Argument mismatch
java.util.Map.put(java.lang.String,jaav.util.Set) is
not applicable; (....) cannot be converted to (...).
I have created the map and previous sets in this same class and all have been added fine...
I have changed the code to
public void addMapEntry(String newPlayerAdd, Set<String> newPlanetAdd) {
planetStats = new TreeSet<>(newPlanetAdd);
playerMap.put(newPlayerAdd, newPlanetAdd);
}
And am getting a runtime error of the same type instead of a compile time error;
I feel I am not passing the variable in the correct format to the method...any help appreciated...
currently sending in the form of
allPlanets.addMapEntry("Jane", "Klethron");
You should declare your map as
Map<String, Set<String>> playerMap = new HashMap<String, Set<String>>()
in case you want to use HashMap implementation.. and should call the method as
Set<String> set = new HashSet<String>();
set.add("Klethron");
allPlanets.addMapEntry("Jane", set);
Declare your Map as
Map<String, Set<String>> playerMap = new HashMap<String, Set<String>>()
Initialize your Set
Set<String> setString = new HashSet<String>();
setString.add("String1"); //fill up the string set in this way
allPlanets.addMapEntry("Jane", setString);
I have a
TreeMap resMap new TreeMap<String, Map<String, String>>();
I would like to filter and keep only entries that values contains a known pair, let's say ('mike' => 'jordan'), and avoid a loop like below
Is there in my included libraries apache.commons and google.common a filter method (that probably would do a loop too, but at least it's less verbose
for (Entry<String, TreeMap<String, String>> el : resMap.entrySet()){
if (el.getValue().get("mike").equals("jordan")){
//
}
}
You can use filters from Guava and the Predicate interface.
Predicate<T> yourFilter = new Predicate<T>() {
public boolean apply(T o) {
// your filter
}
};
So, simple example would be:
Predicate<Integer> evenFilter = new Predicate<Integer>() {
public boolean apply(Integer i) {
return (i % 2 == 0);
}
};
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Map<Integer, Integer> evenMap = Maps.filterValues(map, evenFilter);
Rather than force your client code to use a filter/loop, build what you need into the API of your class:
public class MyClass {
private TreeMap resMap new TreeMap<String, Map<String, String>>();
public void filter(String key, String value) {
// Some impl here. Either your loop or the guava approach
}
}
BTW, if you use your loop, consider changing to this:
for (Iterator<Map.Entry<String, TreeMap<String, String>>> i = resMap.entrySet().iterator(); i.hasNext();) {
Map.Entry<String, TreeMap<String, String>> entry = i.next();
if (value.equals(entry.getValue().get(key))) {
i.remove();
}
}
The changes to the loop are:
Changed order of equals to avoid NPE
Using iterator to allow removal of entries directly
Even if you don't have a class, you could easily wrap it up in a static method on a utility class, where it could also easily be parameterized to work with any nested map:
public static <K1, K2, V> void filter(Map<K1, Map<K2, V>> map, K2 key, V value) {
// Some impl here
}
Here's a non-guava impl for the static method:
for (Iterator<Map.Entry<K1, Map<K2, V>>> i = map.entrySet().iterator(); i.hasNext();) {
Map.Entry<K1, Map<K2, V>> entry = i.next();
if (value.equals(entry.getValue().get(key))) {
i.remove();
}
}
From #Ferrybig answer in this post.
You can use the Java 8 method Collection.removeIf for this purpose:
map.values().removeIf(Object o -> o.get("mike").equals("jordan"));
This removed all values that match the predicate.
Online demo
This works by the fact that calling .values() for a HashMap returns a collection that delegated modifications back to the HashMap itself, meaning that our call for removeIf() actually changes the HashMap (this doesn't work on all java Map's)
Take a look at Guava's Predicates and Functions.
Here are two examples. The both print the key based on match in the value's properties.
private static void printMatchingEntriesUsingALoop(Map<String, Map<String, String>> resMap, String key, String value) {
for (Map.Entry<String, Map<String, String>> entry : resMap.entrySet())
if (value.equals(entry.getValue().get(key)))
System.out.println(entry.getKey());
}
private static void printMatchingEntriesUsingGuava(Map<String, Map<String, String>> resMap, final String key, final String value) {
Predicate<Map<String, String>> keyValueMatch =
new Predicate<Map<String, String>>() {
#Override
public boolean apply(#Nullable Map<String, String> stringStringMap) {
return value.equals(stringStringMap.get(key));
}
};
Maps.EntryTransformer<String, Map<String, String>, Void> printKeys =
new Maps.EntryTransformer<String, Map<String, String>, Void>() {
#Override
public Void transformEntry(#Nullable String s,
#Nullable Map<String, String> stringStringMap) {
System.out.println(s);
return null;
}
};
Maps.transformEntries(Maps.filterValues(resMap, keyValueMatch), printKeys);
}
public static void main(String... args) {
Map<String, Map<String, String>> resMap = new TreeMap<String, Map<String, String>>();
printMatchingEntriesUsingALoop(resMap, "first", "mike");
printMatchingEntriesUsingGuava(resMap, "first", "mike");
}
One uses a loop and one use Guava.
While the first one performs better, you should really decide which will be the easiest to understand and maintain.
Some suggestions from #missingfaktor. You have to use your own judgement, but he highlighted some of the issues well.
a lot of code duplication.
special case handling.
More cyclomatic complexity.
More chances of error, as a result of first three bullets.
Hard to follow code.
Imagine you are a new developer who has to support this software. Which would you rather be facing?
You can filter the map using java 8 and streams. The first step in this process is converting to a stream using entrySet().stream(). This gives you a Stream<Map.Entry<String, TreeMap<String, String>>. You can then use filter(...) to filter the list. When you filter, you should return true when the incoming value should be included in the filter result. After you filtered the results, you can use foreach to loop over the final result.
The final result will look like the following:
resMap.entrySet().stream()
.filter(e -> el.getValue().get("mike").equals("jordan"))
.foreach(e -> {
// Do something with your entry here
});