Hey :) This is my first post and my first approach at streams! I am trying to learn it by myself, but since its a new style of programming Im having a tough time :D
So here's my problem:
In the following Method I have a Map of <Long, Ingredient> as parameter. (Ingredient has the String Attribute "name".)
I want to return an inverted Map of <String, Long>. (The string being the Attribute "name" of the class Ingredient.)
Here is my solution using a for-loop (which worked)
public static Map<String, Long> focusOnNameAndInvert(Map<Long, Ingredient> articles) {
ArrayList<String> nameList = new ArrayList<>(articles.values().stream().map(Ingredient::getName).collect(Collectors.toList()));
ArrayList<Long> keyList = new ArrayList<>(articles.keySet());
Map<String, Long> nameAndInvertedMap = new HashMap<>();
for (int i = 0; i<nameList.size(); i ){
nameAndInvertedMap.put(nameList.get(i), keyList.get(i));
}
return nameAndInvertedMap;
}
Here is my approach on using streams (which I need help with)
The "<>" on the right side of initializing the HashMap is red underlined and says "Cannot infer arguments" (Using IntelliJ)
public static Map<String, Long> focusOnNameAndInvert(Map<Long, Ingredient> articles) {
Map<String, Long> nameAndInvertedMap = new HashMap<>(
articles.values().stream().map(Ingredient::getName),
articles.keySet().stream().map(articles::get));
return nameAndInvertedMap;
}
Thank you for all input, tips, critique and especially for your time! Wishing you all a nice day :-)
CodePudding user response:
Your method is not actually doing what you hoped. As you can see from the compiler error, there is no HashMap
constructor which accepts a List
of keys and a List
of values. Besides, even if it did, you're streaming the given Map
(twice), then trying to swap the keys and the values in different streams and finally not even collecting the values of your streams. Streams are pipelines of operations lazily evaluated, if you do not add a terminal operation they are not even executed.
Here there is an official Oracle's tutorial which sums up quite good the concept of streams and how they work:
https://docs.oracle.com/javase/tutorial/collections/streams/parallelism.html#laziness
You can write what your method by streaming the entries of the given map and then collect them with the collect(Collectors.toMap())
terminal operation where each Ingredient
's name is mapped as the key while the long keys as their value.
public static Map<String, Long> focusOnNameAndInvert(Map<Long, Ingredient> articles) {
return articles.entrySet().stream()
.collect(Collectors.toMap(entry -> entry.getValue().getName(), entry -> entry.getKey()));
}
Note that collecting the entries with the 2 parameters version of the method toMap()
does not work if there are multiple keys, i.e. duplicate ingredient names in your case. The 3 parameters version of the method should be preferred as it allows handling the case of colliding keys (multiple same keys mapping different values). Possible implementations could be: discarding a value over the other, summing the values and so on.
Here is a possible implementation where just one of the two values is maintained.
public static Map<String, Long> focusOnNameAndInvert(Map<Long, Ingredient> articles) {
return articles.entrySet().stream()
.collect(Collectors.toMap(entry -> entry.getValue().getName(), entry -> entry.getKey(), (longVal1, longVal2) -> longVal1));
}
CodePudding user response:
Your stream is incorrect. Should be something like this:
public static Map<String, Long> focusOnNameAndInvert(Map<Long, Ingredient> articles) {
return articles.entrySet().stream()
.collect(Collectors.toMap(entry -> entry.getValue().getName(), Map.Entry::getKey));
}