How to get the first key value from map using JAVA 8? - java

As for now I am doing :
Map<Item, Boolean> processedItem = processedItemMap.get(i);
Map.Entry<Item, Boolean> entrySet = getNextPosition(processedItem);
Item key = entrySet.getKey();
Boolean value = entrySet.getValue();
public static Map.Entry<Item, Boolean> getNextPosition(Map<Item, Boolean> processedItem) {
return processedItem.entrySet().iterator().next();
}
Is there any cleaner way to do this with java8 ?

I see two problems with your method:
it will throw an exception if the map is empty
a HashMap, for example, has no order - so your method is really more of a getAny() than a getNext().
With a stream you could use either:
//if order is important, e.g. with a TreeMap/LinkedHashMap
map.entrySet().stream().findFirst();
//if order is not important or with unordered maps (HashMap...)
map.entrySet().stream().findAny();
which returns an Optional.

Seems like you need findFirst here
Optional<Map.Entry<Item, Boolean>> firstEntry =
processedItem.entrySet().stream().findFirst();
Obviously a HashMap has no order, so findFirst might return a different result on different calls. Probably a more suitable method would be findAny for your case.

Related

How to return a Key from HashMap based on its Value in Java

Consider the following:
for (Boolean i: x.values()) {
if (i == false) {
return // KEY;
}
}
In the above code, I am trying to iterate through the HashMap. And I want to return a Key when the value is false.
How can I do it?
You need to loop through the HashMap's entrySet:
for (Map.Entry<Object, Boolean> entry : x.entrySet()) {
if(entry.getValue() == false){
return entry.getKey();
}
}
In order to find first a key that is mapped to a particular value you need to iterate either over the key set or over the entry set.
The latter option is preferred because at each iteration step you will have a key-value pair on your hands. Therefore, it would be slightly faster than interrogating the map at every iteration (remainder: there could be collisions in a real hashmap, and finding the target node can take more time than accessing the value on a map-entry).
But you definitely can't use a collection of map values returned by a method values() like you're doing in the code-snippet you've posted. There's no fast way of retrieving a key when you have only a value on your hands.
In the example below, ill show how to obtain a key from a map of arbitrary type that matches the condition (a Predicate) provided dynamically at runtime using Stream API.
There's one important thing to consider: target value might not be present in the map. And the code should handle this case. We can provide a default value (either directly or via Supplier), throw an exception (if according to the application logic the given value is always expected to be present), or by utilizing methods ifPresent(), ifPresentOrElse() provided by the Optional class. In the example below, I've chosen to provide a default value.
Take a look at these tutorials for more information on lambda expressions and streams
public static void main(String[] args) {
Map<String, Boolean> sourceMap =
Map.of("A", true, "B", false, "C", true);
String result = getFirstKey(sourceMap, entry -> !entry.getValue(), "");
System.out.println(result);
}
public static <K, V> K getFirstKey(Map<K, V> map,
Predicate<Map.Entry<K, V>> condition,
K defaultKey) {
return map.entrySet().stream()
.filter(condition) // Stream<Map.Entry<K, V>>
.findFirst() // Optional<Map.Entry<K, V>>
.map(Map.Entry::getKey) // Optional<K>
.orElse(defaultKey); // or apply .orElseThrow() depending on your needs
}
Output
B // key assosiated with a value of `false`

Using Java 8 Optional for safe fetching from Map

I have a nullable Map<String, String> myMap in my Java 8 class.
I need to fetch a value from the map based on the following condition.
get keyA;
if not present, get keyB;
if not present, get keyC.
I understand that Java 8's Optional<T> brings the required behavior. Is there a way to use Optional to elegantly get the data instead of using containsKey checks?
Building on Alexander Ivanchenko's answer, you could get the first non-null value from the map based on a series of alternate keys:
public static Optional<String> getValue(Map<String, String> map,
String keyA, String... keys) {
Optional<String> result = Optional.ofNullable(map.get(keyA));
for (String key : keys) {
if (result.isPresent()) break;
result = result.or(() -> Optional.ofNullable(map.get(key)));
}
return result;
}
You could use the getOrDefault method of map, but something like
map.getOrDefault("keyA", map.getOrDefault("keyB", map.get("keyC")))
seems overly specific and complicated, and you still have to deal with the fact that neither keyA nor keyB nor keyC might be present, so you might still get null, and this has the performance penalty of looking up all three values from the map no matter which is returned.
It also only works for three keys.
No. There is no special integration of Optional with Maps.
More to the point, Optional does not support null values. Assuming that by "nullable map" you mean "a map that can contain null values", you can't possibly use Optional to distinguish between "null values that are in your map" and "values that aren't in your map at all." There is absolutely no way to use Optional helpfully in this scenario.
You can get an Optional result of retrieving the value corresponding to one of the given keys from a Map containing nullable values by using Stream.ofNullable() and Optional.or().
Method Optional.or() expects a supplier of Optional which will be utilized only if this method was invoked on an empty optional.
public static Optional<String> getValue(Map<String, String> myMap,
String keyA, String keyB, String keyC) {
return Stream.ofNullable(myMap) // precaution for myMap == null
.flatMap(map -> Stream.ofNullable(map.get(keyA)))
.findFirst()
.or(() -> Optional.ofNullable(myMap.get(keyB))) // evaluated only if keyA is not present
.or(() -> Optional.ofNullable(myMap.get(keyC))); // evaluated only if both keyA and keyB are not present
}
Note: the method above is meant to accommodate the safe fetching (as the question title states) and will return an empty Optional if all the given keys are absent or if one or more of them is mapped to a null value.

Create Map with provided set of keys and default values for them

I have a method that receives List<String> keys and does some computations on these and at the end it returns Map<String, String> that has keys from that List and values computed or not. If value cannot be computed I want to have empty String for that key.
The clearest way would be to create Map containing all the keys with default values (empty String in that case) at the start of computations and then replace computed values.
What would be the best way to do so? There is no proper initializer in Collections API I think.
The easiest answer came to me seconds ago:
final Map<String, String> labelsToReturn = keys.stream().collect(toMap(x -> x, x -> ""));
solves the problem perfectly.
Just use stream and toMap with key / value mapping to initialize the map.
I advise you to use Stream API Collectors.toMap() method. There is a way:
private static void build(List<String> keys) {
Map<String, String> defaultValues = keys.stream().collect(
Collectors.toMap(key -> key, key -> "default value")
);
// further computations
}
Map<String,String> map = new HashMap<>();
String emptyString = "";
for(String key:keys){
Object yourcomputation = emptyString;
//asign your computation to new value base on key
map.put(key,yourcomputation);
}

Java Streams | groupingBy same elements

I have a stream of words and I would like to sort them according to the occurrence of same elements (=words).
e.g.: {hello, world, hello}
to
Map<String, List<String>>
hello, {hello, hello}
world, {world}
What i have so far:
Map<Object, List<String>> list = streamofWords.collect(Collectors.groupingBy(???));
Problem 1: The stream seems to lose the information that he is processing Strings, therefore the compiler forces me to change the type to Object, List
Problem 2: I don't know what to put inside the parentesis to group it by the same occurrence. I know that I am able to process single elements within th lambda-expression but I have no idea how to reach "outside" each element to check for equality.
Thank You
To get a Map<String, List<String>>, you just need to tell to the groupingBy collector that you want to group the values by identity, so the function x -> x.
Map<String, List<String>> occurrences =
streamOfWords.collect(groupingBy(str -> str));
However this a bit useless, as you see you have the same type of informations two times. You should look into a Map<String, Long>, where's the value indicates the occurrences of the String in the Stream.
Map<String, Long> occurrences =
streamOfWords.collect(groupingBy(str -> str, counting()));
Basically instead of having a groupingBy that return values as List, you use the downstream collector counting() to tell that you want to count the number of times this value appears.
Your sort requirement should imply that you should have a Map<Long, List<String>> (what if different Strings appear the same number of times?), and as the default toMap collector returns an HashMap, it has no notions of ordering, but you could store the elements in a TreeMap instead.
I've tried to summarize a bit what I've said in the comments.
You seems to have troubles with how str -> str can tell whether "hello" or "world" are different.
First of all str -> str is a function, that is, for an input x yields a value f(x). For example, f(x) = x + 2 is a function that for any value x returns x + 2.
Here we are using the identity function, that is f(x) = x. When you collect the elements from the pipeline in the Map, this function will be called before to obtain the key from the value. So in your example, you have 3 elements for which the identity function yields:
f("hello") = "hello"
f("world") = "world"
So far so good.
Now when collect() is called, for every value in the stream you'll apply the function on it and evaluate the result (which will be the key in the Map). If a key already exists, we take the currently mapped value and we merge in a List the value we wanted to put (i.e the value from which you just applied the function on) with this previous mapped value. That's why you get a Map<String, List<String>> at the end.
Let's take another example. Now the stream contains the values "hello", "world" and "hey" and the function that we want to apply to group the elements is str -> str.substring(0, 2), that is, the function that takes the first two characters of the String.
Similarly, we have:
f("hello") = "he"
f("world") = "wo"
f("hey") = "he"
Here you see that both "hello" and "hey" yields the same key when applying the function and hence they will be grouped in the same List when collecting them, so that the final result is:
"he" -> ["hello", "hey"]
"wo" -> ["world"]
To have an analogy with mathematics, you could have take any non-bijective function, such as x2. For x = -2 and x = 2 we have that f(x) = 4. So if we grouped integers by this function, -2 and 2 would have been in the same "bag".
Looking at the source code won't help you to understand what's going on at first. It's useful if you want to know how it's implemented under the hood. But try first to think of the concept with a higher level of abstraction and then maybe things will become clearer.
Hope it helps! :)
The KeyExtractor you are searching for is the identity function:
Map<String, List<String>> list = streamofWords.collect(Collectors.groupingBy(Function.identity()));
EDIT added explanation:
Function.identity() retuns a 'Function' with one method that does nothing more than returning the argument it gets.
Collectors.groupingBy(Function<S, K> keyExtractor) provides a collector, which collects all elements of the stream to a Map<K, List<S>>. It is using the keyExtractor implementation it gets to inspect the stream's objects of type S and deduce a key of type K from them. This key is the map's key used to get (or create) the list in the result map the stream element is added to.
If you want to group by some fields of an object, not a whole object and you don't want to change your equals and hashCode methods I'd create a class holding a set of keys for grouping purposes:
import java.util.Arrays;
#Getter
public class MultiKey {
public MultiKey(Object... keys) {
this.keys = keys;
}
private Object[] keys;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MultiKey multiKey = (MultiKey) o;
return Arrays.equals(keys, multiKey.keys);
}
#Override
public int hashCode() {
return Arrays.hashCode(keys);
}
}
And the groupingBy itself:
Map<MultiKey, List<VhfEventView>> groupedList = list
.stream()
.collect(Collectors.groupingBy(
e -> new MultiKey(e.getGroupingKey1(), e.getGroupingKey2())));

Using the keySet() method in HashMap

I have a method that goes through the possible states in a board and stores them in a HashMap
void up(String str){
int a = str.indexOf("0");
if(a>2){
String s = str.substring(0,a-3)+"0"+str.substring(a-2,a)+str.charAt(a-3)+str.substring(a+1);
add(s,map.get(str)+1);
if(s.equals("123456780")) {
System.out.println("The solution is on the level "+map.get(s)+" of the tree");
//If I get here, I need to know the keys on the map
// How can I store them and Iterate through them using
// map.keySet()?
}
}
}
I'm interested in the group of keys. What should I do to print them all?
HashSet t = map.keySet() is being rejected by the compiler as well as
LinkedHashSet t = map.keySet()
Use:
Set<MyGenericType> keySet = map.keySet();
Always try to specify the Interface type for collections returned by these methods. This way regardless of the actual implementation class of the Set returned by these methods (in your case map.keySet()) you would be ok. This way if the next release the jdk guys use a different implementation for the returned Set your code will still work.
map.keySet() returns a View on the Keys of the map. Making changes to this view results in changing the underlying map though those changes are limited. See the javadoc for Map:
http://java.sun.com/j2se/1.5.0/docs/api/java/util/Map.html#keySet%28%29
Map<String, String> someStrings = new HashMap<String, String>();
for(Map.Entry<String, String> entry : someStrings.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
}
This is how I like to iterate through Maps. If you specifically want just the keySet(), that answer is elsewhere on this page.
for ( String key : map.keySet() ) {
System.out.println( key );
}
Set t = map.ketSet()
The API does not specify what type of Set is returned.
You should try to declare variables as the interface rather than a particular implementation.
Just
Set t = map.keySet();
Unless you're using an older JDK, I think its a little cleaner to use generics when using the Collections classes.
So thats
Set<MyType> s = map.keySet();
And then if you just iterate through them, then you can use any kind of loop you'd like. But if you're going to be modifying the map based on this keySet, you you have to use the keySet's iterator.
All that's guaranteed from keySet() is something that implements the interface Set. And that could possibly be some undocumented class like SecretHashSetKeys$foo, so just program to the interface Set.
I ran into this trying to get a view on a TreeSet, the return type ended up being TreeSet$3 on close examination.
Map<String, Object> map = new HashMap<>();
map.put("name","jaemin");
map.put("gender", "male");
map.put("age", 30);
Set<String> set = map.keySet();
System.out.println("this is map : " + map);
System.out.println("this is set : " + set);
It puts the key values in the map into the set.
From Javadocs HashMap has several methods that can be used to manipulate and extract data from a hasmap.
public Set<K> keySet()
Returns a Set view of the keys contained in this map. The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. If the map is modified while an iteration over the set is in progress (except through the iterator's own remove operation), the results of the iteration are undefined. The set supports element removal, which removes the corresponding mapping from the map, via the Iterator.remove, Set.remove, removeAll, retainAll, and clear operations. It does not support the add or addAll operations.
Specified by:
keySet in interface Map
Overrides:
keySet in class AbstractMap
Returns:
a set view of the keys contained in this map
so if you have a map myMap of any datatype , such that the map defined as map<T> , if you iterate it as follows:
for (T key : myMap.keySet() ) {
System.out.println(key); // which represent the value of datatype T
}
e.g if the map was defined as Map<Integer,Boolean>
Then for the above example we will have:
for (Integer key : myMap.keySet()){
System.out.println(key) // the key printed out will be of type Integer
}

Categories

Resources