This question already has an answer here:
Using Collectors.toMap to return a LinkedHashMap
(1 answer)
Closed 1 year ago.
I am trying to convert a properties object into a HashMap<String, String>. I am able to convert it to a Map, but not able to do it diectly to a HashMap.
So far, I have done this:
dbProperties.entrySet().stream().collect(Collectors.toMap(k -> k.getKey().toString(), v -> v.getValue().toString()));
This gives me a Map<Object, Object>. Is there way to get it to a HashMap<String, String> without casting or anything?
You can do it by providing a BinaryOperator and Supplier.
import java.util.HashMap;
import java.util.Properties;
import java.util.stream.Collectors;
public class Main {
public static void main(String args[]) {
Properties dbProperties = new Properties();
dbProperties.put("One", "1");
dbProperties.put("Two", "2");
HashMap<String, String> map =
dbProperties.entrySet()
.stream()
.collect(Collectors
.toMap(k -> k.getKey().toString(),
v -> v.getValue().toString(),
(v1, v2) -> v1,
HashMap::new
)
);
System.out.println(map);
}
}
Output:
{One=1, Two=2}
Related
This question already has answers here:
what does java8 stream map do here?
(4 answers)
Closed 7 months ago.
I have a hashmap in Java with a string key and a HashSet value. The hashset may contain many PlacementBundles inside it.
public Map<String, Set<PlacementBundle>> placementByConcept;
I am trying to remove the value from the HashSet while iterating the map which matches a specific condition.
I tried the below code but cannot remove the matching element from the HashSet.
placementByConcept.entrySet()
.stream()
.map(e -> e.getValue()
.removeIf(s -> s.getBeatObjectiveId().equals("non-scored")));
you can use forEach:
placementByConcept.entrySet().forEach(e -> e.getValue().removeIf(s -> s.getBeatObjectiveId().equals("non-scored")));
public class Remove {
public static void main(String[] args)
{
HashMap<Integer, String>
map = new HashMap<>();
map.put(1, "Stack");
map.put(2, "Overflow");
map.put(3, "StackOverflow");
int keyToBeRemoved = 2;
System.out.println("Original HashMap: "
+ map);
map.entrySet()
.removeIf(
entry -> (keyToBeRemoved == entry.getKey()));
System.out.println("New HashMap: "
+ map);
}
}
Output:
Original HashMap: {1=Stack, 2=Overflow, 3=StackOverflow}
New HashMap: {1=Stack, 3=StackOverflow}
In your case Set<PlacementBundle> is an immutable collection. You can't remove an element from it.
Thank you Holger for pointing out the assumption I made which may not be true for the asked question.
If Set is immutable collection and you use foreach as suggested in the accepted answer, you will get UnsupportedOperationException
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
#Slf4j
public class Test {
public static void main(String[] args) {
Map<String, Set<PlacementBundle>> placementByConcept = new HashMap<>();
placementByConcept.put("concept1", Set.of(
PlacementBundle.builder().beatObjectiveId("scored").build(),
PlacementBundle.builder().beatObjectiveId("non-scored").build())
);
placementByConcept.put("concept2", Set.of(
PlacementBundle.builder().beatObjectiveId("scored").build(),
PlacementBundle.builder().beatObjectiveId("non-scored").build())
);
log.info("Original: {}", placementByConcept);
/* This won't give any exception, neither will remove the entries */
placementByConcept.entrySet()
.stream()
.map(e -> e.getValue()
.removeIf(s -> s.getBeatObjectiveId().equals("non-scored")));
log.info("Does not work: {}", placementByConcept);
/* This will give you the exception UnsupportedOperationException */
// placementByConcept.entrySet().forEach(e -> e.getValue().removeIf(s -> s.getBeatObjectiveId().equals("non-scored")));
/* This is one of the correct way */
for (Map.Entry<String, Set<PlacementBundle>> entry : placementByConcept.entrySet()) {
var filtered = entry.getValue().stream()
.filter(placementBundle -> !placementBundle.getBeatObjectiveId().equals("non-scored"))
.collect(Collectors.toUnmodifiableSet());
log.debug("New Value Set: {}", filtered);
entry.setValue(filtered);
}
log.info("After: {}", placementByConcept);
}
}
#Builder
#Data
class PlacementBundle {
private String beatObjectiveId;
}
Output:
Original: {concept2=[PlacementBundle(beatObjectiveId=scored), PlacementBundle(beatObjectiveId=non-scored)], concept1=[PlacementBundle(beatObjectiveId=scored), PlacementBundle(beatObjectiveId=non-scored)]}
Does not work: {concept2=[PlacementBundle(beatObjectiveId=scored), PlacementBundle(beatObjectiveId=non-scored)], concept1=[PlacementBundle(beatObjectiveId=scored), PlacementBundle(beatObjectiveId=non-scored)]}
After: {concept2=[PlacementBundle(beatObjectiveId=scored)], concept1=[PlacementBundle(beatObjectiveId=scored)]}
I am trying to get the Get only the key values from the List of Map object using the stream in java 8.
When I stream the List of map object I am getting Stream<List<String>> instead of List<String>.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("Hello World");
Map<String, String> a = new HashMap<String, String>();
a.put("1", "Bharathi");
a.put("2", "Test");
a.put("3", "Hello");
List<Map<String, String>> b = new ArrayList<>();
b.add(a);
System.out.println("Hello World" + b);
/*
* b.stream().map(c-> c.entrySet().stream().collect( Collectors.toMap(entry ->
* entry.getKey(), entry -> entry.getValue())));
*/
Stream<List<String>> map2 = b.stream()
.map(c -> c.entrySet().stream().map(map -> map.getKey()).collect(Collectors.toList()));
//List<List<String>> collect = map2.map(v -> v).collect(Collectors.toList());
}
}
How to get the key object from the List of Map object?
you can use flatMap over the keySet of each Map within:
List<String> output = lst.stream()
.flatMap(mp -> mp.keySet().stream())
.collect(Collectors.toList());
You can simply flatMap it:
b.stream().flatMap(m -> m.keySet().stream()).collect(Collectors.toList())
Of course flatMap is stream base solution! however you can do it with non-stream version in simple way.
List<String> map2 = new ArrayList<>();
b.forEach(map -> map2.addAll(map.keySet()));
You also can use a slightly more declarative way:
List<String> collect = b.stream()
.map(Map::keySet) // map maps to key sets
.flatMap(Collection::stream) // union all key sets to the one stream
.collect(Collectors.toList()); // collect the stream to a new list
I am not so familiar with Java 8 (still learning) and looking to see if I could find something equivalent of the below code using streams.
The below code mainly tries to get corresponding double value for each value in String and then sums it up. I could not find much help anywhere on this format. I am not sure if using streams would clean up the code or would make it messier.
// safe assumptions - String/List (Key/Value) cannot be null or empty
// inputMap --> Map<String, List<String>>
Map<String, Double> finalResult = new HashMap<>();
for (Map.Entry<String, List<String>> entry : inputMap.entrySet()) {
Double score = 0.0;
for (String current: entry.getValue()) {
score += computeScore(current);
}
finalResult.put(entry.getKey(), score);
}
private Double computeScore(String a) { .. }
Map<String, Double> finalResult = inputMap.entrySet()
.stream()
.collect(Collectors.toMap(
Entry::getKey,
e -> e.getValue()
.stream()
.mapToDouble(str -> computeScore(str))
.sum()));
Above code iterates over the map and creates a new map with same keys & before putting the values, it first iterates over each value - which is a list, computes score via calling computeScore() over each list element and then sums the scores collected to be put in the value.
You could also use the forEach method along with the stream API to yield the result you're seeking.
Map<String, Double> resultSet = new HashMap<>();
inputMap.forEach((k, v) -> resultSet.put(k, v.stream()
.mapToDouble(s -> computeScore(s)).sum()));
s -> computeScore(s) could be changed to use a method reference i.e. T::computeScore where T is the name of the class containing computeScore.
How about this one:
Map<String, Double> finalResult = inputMap.entrySet()
.stream()
.map(entry -> new AbstractMap.SimpleEntry<String, Double>( // maps each key to a new
// Entry<String, Double>
entry.getKey(), // the same key
entry.getValue().stream()
.mapToDouble(string -> computeScore(string)).sum())) // List<String> mapped to
// List<Double> and summed
.collect(Collectors.toMap(Entry::getKey, Entry::getValue)); // collected by the same
// key and a newly
// calulcated value
The version above could be merged to the single collect(..) method:
Map<String, Double> finalResult = inputMap.entrySet()
.stream()
.collect(Collectors.toMap(
Entry::getKey, // keeps the same key
entry -> entry.getValue()
.stream() // List<String> -> Stream<String>
// then Stream<String> -> Stream<Double>
.mapToDouble(string -> computeScore(string))
.sum())); // and summed
The key parts:
collect(..) performs a reduction on the elements using a certain strategy with a Collector.
Entry::getKey is a shortcut for entry -> entry.getKey. A function for mapping the key.
entry -> entry.getValue().stream() returns the Stream<String>
mapToDouble(..) returns the DoubleStream. This has an aggregating operation sum(..) which sums the elements - together creates a new value for the Map.
Regardless of whether you use the stream-based or the loop-based solution, it would be beneficial and add some clarity and structure to extract the inner loop into a method:
private double computeScore(Collection<String> strings)
{
return strings.stream().mapToDouble(this::computeScore).sum();
}
Of course, this could also be implemented using a loop, but ... that's exactly the point: This method can now be called, either in the outer loop, or on the values of a stream of map entries.
The outer loop or stream could also be pulled into a method. In the example below, I generalized this a bit: The type of the keys of the map does not matter. Neither does whether the values are List or Collection instances.
As an alternative to the currently accepted answer, the stream-based solution here does not fill a new map that is created manually. Instead, it uses a Collector.
(This is similar to other answers, but I think that the extracted computeScore method greatly simplifies the otherwise rather ugly lambdas that are necessary for the nested streams)
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
public class ToStreamOrNotToStream
{
public static void main(String[] args)
{
ToStreamOrNotToStream t = new ToStreamOrNotToStream();
Map<String, List<String>> inputMap =
new LinkedHashMap<String, List<String>>();
inputMap.put("A", Arrays.asList("1.0", "2.0", "3.0"));
inputMap.put("B", Arrays.asList("2.0", "3.0", "4.0"));
inputMap.put("C", Arrays.asList("3.0", "4.0", "5.0"));
System.out.println("Result A: " + t.computeA(inputMap));
System.out.println("Result B: " + t.computeB(inputMap));
}
private <T> Map<T, Double> computeA(
Map<T, ? extends Collection<String>> inputMap)
{
Map<T, Double> finalResult = new HashMap<>();
for (Entry<T, ? extends Collection<String>> entry : inputMap.entrySet())
{
double score = computeScore(entry.getValue());
finalResult.put(entry.getKey(), score);
}
return finalResult;
}
private <T> Map<T, Double> computeB(
Map<T, ? extends Collection<String>> inputMap)
{
return inputMap.entrySet().stream().collect(
Collectors.toMap(Entry::getKey, e -> computeScore(e.getValue())));
}
private double computeScore(Collection<String> strings)
{
return strings.stream().mapToDouble(this::computeScore).sum();
}
private double computeScore(String a)
{
return Double.parseDouble(a);
}
}
I found it somewhat shorter:
value = startDates.entrySet().stream().mapToDouble(Entry::getValue).sum();
This question already has answers here:
Sort a Map<Key, Value> by values
(64 answers)
Sorting Java objects using multiple keys
(7 answers)
Closed 3 years ago.
How sort HashMap entries by multiple properties.
Suppose I have a map with key String and value as Object.
Map<String, UserMetrics> map = new HashMap<>
map.put("user10",new UserMetrics(1,100,111));
map.put("user3",new UserMetrics(10,330,444));
map.put("user11",new UserMetrics(333,100,555));
map.put("user1",new UserMetrics(1,111,433));
public static class UsageMetrics implements Serializable {
private long param1;
private long param2;
private long param3;....
}
I want to sort users first by "param1" and then after by "param2"
result expected:<>
user10, UserMetrics(1,100,111)
user1, UserMetrics(1,111,433))
user3, UserMetrics(10,330,444));
user11, UserMetrics(333,100,555))
You can use the following comparator for sorting:
Comparator
.comparingLong(UserMetrics::getParam1)
.thenComparingLong(UserMetrics::getParam2);
The difficulty is, however that you want to sort values, not keys. It seems also you need both keys and values. For this you could make and sort a copy of the entry set of your map. Something along the lines:
List<Map.Entry<String, UserMetrics>> sortedEntries = new ArrayList<>(map.entrySet());
Collections.sort(sortedEntries,
Map.Entry.comparingByValue(
Comparator
.comparingLong(UserMetrics::getParam1)
.thenComparingLong(UserMetrics::getParam2)));
Alternatively you can also use a sorted collection (like TreeSet) or a sorted stream - normally you can provide your own comparator to the "sorting things".
Also note that I'm using comparingLong/thenComparingLong, unlike other answers where people just used comparing/thenComparing. The problem with comparing/thenComparing is that if you have primitive types like long, comparing/thenComparing will essentially box them into wrapper types like Long, which is totally unnecessary.
You can use below code. I have written by using Employee the s value object, so you can use your own Object :
public class Main {
public static void main(String[] args) throws InterruptedException {
HashMap<String, Employee> map = new HashMap<>();
Map<String, Employee> sortedMap = map.entrySet()
.stream()
.sorted(Entry.comparingByValue(Main::compare))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue,
(e1, e2) -> e1, LinkedHashMap::new));
}
public static int compare(Employee e1, Employee e2) {
return e1.getAge() - e2.getAge();
}
}
Edited:
Here is another way you can use Comparator#comparing and thenComparing to sort.
Map<String, Employee> sortedMap = map.entrySet()
.stream()
.sorted(Entry.comparingByValue(
Comparator.comparing(Employee::getAge)
.thenComparing(Employee::getSalary)))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue,
(e1, e2) -> e1, LinkedHashMap::new));
HashMap is not an efficient data structure to sort by value. But if you really need to use it, you can try something like this:
map.entrySet().stream().sorted(Comparator.comparing(entry -> entry.getValue().param1)
.thenComparing(entry -> entry.getValue().param2)
.thenComparing(entry -> entry.getValue().param3))
.collect(Collectors.toList());
Suppose I have a map of given name, surname pairs and I want to find the given name of the first entry in that map that has the surname matching a certain value.
How would we do this in a java 8 fashion.
In my test case example below I put two ways that would do it.
However the first one (looking for the given name of the first person with a surname of "Donkey") will throw java.util.NoSuchElementException: No value present so it is not safe.
The second one works but it is not only harder to read but it it is a bit not quite functional.
Just wondering if someone here would suggest me an easier clearer way of achieving this using either stream() or forEach() or both.
#Test
public void shouldBeAbleToReturnTheKeyOfTheFirstMatchingValue() throws Exception {
Map<String, String> names = new LinkedHashMap<>();
names.put("John", "Doe");
names.put("Fred", "Flintstone");
names.put("Jane", "Doe");
String keyOfTheFirst = names.entrySet().stream().filter(e -> e.getValue().equals("Doe")).findFirst().get().getKey();
assertEquals("John", keyOfTheFirst);
try {
names.entrySet().stream().filter(e -> e.getValue().equals("Donkey")).findFirst().get();
} catch (NoSuchElementException e){
// Expected
}
Optional<Map.Entry<String, String>> optionalEntry = names.entrySet().stream().filter(e -> e.getValue().equals("Donkey")).findFirst();
keyOfTheFirst = optionalEntry.isPresent() ? optionalEntry.get().getKey() : null;
assertNull(keyOfTheFirst);
}
Thank you in advance.
To return a default value if there is no match, use Optional#orElse
names.entrySet().stream()
.filter(e -> e.getValue().equals("Donkey"))
.map(Map.Entry::getKey)
.findFirst()
.orElse(null);
From a similar question:
public static <T, E> Set<T> getKeysByValue(Map<T, E> map, E value) {
return map.entrySet()
.stream()
.filter(entry -> Objects.equals(entry.getValue(), value))
.map(Map.Entry::getKey)
.collect(Collectors.toSet());
}
Then you can select the first, if you want to. Remember that the key is unique, the value is not.
Edit:
The whole code (thanks #Peter Lawrey)
package test;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
public class Main {
public static void main(String[] args) {
Map<String, String> names = new LinkedHashMap<>();
names.put("John", "Doe");
names.put("Fred", "Flintstone");
names.put("Jane", "Doe");
Optional<String> firstKey = names.entrySet().stream()
.filter(entry -> Objects.equals(entry.getValue(), "Doe"))
.map(Map.Entry::getKey).findFirst();
if (firstKey.isPresent()) {
System.out.println(firstKey.get());
}
}
}
The solution provided by #Misha is the best one if you don't want to use the third-party code. My library has the special shortcut method ofKeys for such cases as I discovered that it's quite common task:
StreamEx.ofKeys(names, "Donkey"::equals).findFirst().orElse(null);
I like old fashioned:
static <K, V> K findFirstKeyByValue(Map<K, V> map, String value) {
for (Entry<K, V> e : map.entrySet())
if (e.getValue().equals(value))
return e.getKey();
return null;
}
Below is my code snippet to get key from map,
Map<String,String> pageDetails = new HashMap<String,String>();
public String getAssociatedKey(){
pageDetails.entrySet().stream().filter( e -> e.getValue().contains("John").findFirst().get().getKey();
}
In order to avoid null pointer exception if map entry exist with null value:
private String getValueByKey(Map<String, String> map, String key)
{
return map.entrySet().stream().filter(e ->
StringUtils.equalsIgnoreCase(e.getKey(), key)).findFirst().get()
.getValue();
}