I have a List<Map<String, Object>> and want to find an item by its id and then return a property of that HashMap. Let me illustrate this with an example:
List<Map<String, Object>> test = new ArrayList<Map<String, Object>>();
var first = new HashMap<String, Object>();
first.put("Id", "4711");
first.put("State", "Ok");
var second = new HashMap<String, Object>();
second.put("Id", "4712");
second.put("State", "Not Ok");
test.add(first);
test.add(second);
How would I search the list of hashmaps where Id is 4712 and get its State?
I know I can do this by manually iterating over the list. However, I would prefer this to be done with streams.
As Joachim Sauer wrote in his comment, just because some aspect of the Java programming language is newer (and appears "cooler") doesn't mean it is better. As the saying goes:
The right tool for the right job.
Anyway, the below code produces the desired result from the input provided in your question.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Test {
public static void main(String[] args) {
List<Map<String, Object>> test = new ArrayList<Map<String, Object>>();
var first = new HashMap<String, Object>();
first.put("Id", "4711");
first.put("State", "Ok");
var second = new HashMap<String, Object>();
second.put("Id", "4712");
second.put("State", "Not Ok");
test.add(first);
test.add(second);
Object val = test.stream() // Every element in stream has type Map<String, Object>
.filter(m -> "4712".equals(m.get("Id")))
.findFirst() // returns Optional<Map<String, Object>>
.get() // throws 'NoSuchElementException' if nothing found
.get("State"); // i.e. java.util.Map#get
System.out.println(val);
}
}
Running the above code displays:
Not Ok
You can use the get method and pass "4712" as key.
See Java documentation, https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html#get-java.lang.Object-
Related
I have a HashMap of type:
Map<String, Map<String, List<MyPojo>>> myMap;
MyPojo has an element String domain.
In some cases this domain can be null.
I want to filter my map so that none of the submap Map<String, List<MyPojo>> should have null domain.
Opening note: You probably shouldn't be having a Map<String, Map<String, List<MyPojo>>> - that's far too convoluted. There should be more written-out types here. Perhaps a Map<String, Students> or some such. Your question doesn't make clear what your problem domain is, so all I can say is that your starting type is probably not good code style.
Let's get to your question:
If you mean filter as in j.u.Stream's filter, then you can't. The Map interface doesn't have a removeIf, and the stream/filter stuff isn't about changing existing types, only about making new stuff. Any attempt to modify the underlying map would just get you ConcurrentModificationExceptions.
Here is how to change the map 'in place'
var it = myMap.entrySet().iterator();
while (it.hasNext()) {
if (it.next().getValue().values().stream()
// we now have a stream of lists.
.anyMatch(
list -> list.stream().anyMatch(mp -> mp.getDomain() == null))) {
it.remove();
}
}
You have a nested anyMatch operation here: You want to remove a k/v pair if any of the entries in the submap contains a list for which any of its entries has a null domain.
Let's see it in action:
import java.util.*;
import java.util.stream.*;
#lombok.Value class MyPojo {
String domain;
}
class Test { public static void main(String[] args) {
Map<String, Map<String, List<MyPojo>>> myMap = new HashMap<>();
myMap.put("A", Map.of("X", List.of(), "Y", List.of(new MyPojo(null))));
myMap.put("B", Map.of("V", List.of(), "W", List.of(new MyPojo("domain"))));
System.out.println(myMap);
var it = myMap.entrySet().iterator();
while (it.hasNext()) {
if (it.next().getValue().values().stream()
// we now have a stream of lists.
.anyMatch(
list -> list.stream().anyMatch(mp -> mp.getDomain() == null))) {
it.remove();
}
}
System.out.println(myMap);
}}
Code that produces a new map is not that hard to figure out given the above.
As I mention in the comment, it's unclear (to me at least) if you want the mappings to stay in place for empty Lists or Maps after your filtering, but if you don't care about empty Maps/Lists afterwards you can just use:
map.values().stream().flatMap(v -> v.values().stream())
.forEach(l -> l.removeIf(p -> p.getDomain() == null));
I have the following sample result when I query a database :
dataList = [{ name : name1, rollno: rollno1 }, { name : name2, rollno: rollno2 } ]
I want to convert this list of hashmaps into a single hashmap using Java 8 streams.
I tried using Collectors.toMap() but i am not sure how to refer to rollNo as key and the hashmap as the value inside the toMap method.
Exected output :
{ rollno1 : { name : name1, rollno: rollno1 } , rollno2 : { name : name2, rollno: rollno2 } }
I tried doing it using a for each loop on the list and then adding the rollno as key to a hashmap and the hashmap as value itself of that rollno.
HashMap<String,HashMap<String,String>> newMap = new HashMap();
for(HashMap<String,String> record : dataList){
String key = record.get("rollno").toString();
newMap.put(key,record);
}
Is there a way to refactor this code using functional streams in Java 8?
Will using streams collect method give any performance advantage over the foreach for doing this?
Will appreciate any leads. Thanks
Here's complete example how to do it
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<HashMap<String, String>> input = new ArrayList<>();
HashMap<String, String> subinput1 = new HashMap<>();
subinput1.put("name", "name1");
subinput1.put("rollno", "rollno1");
input.add(subinput1);
HashMap<String, String> subinput2 = new HashMap<>();
subinput2.put("name", "name2");
subinput2.put("rollno", "rollno2");
input.add(subinput2);
HashMap<String, HashMap<String, String>> result = (HashMap<String, HashMap<String, String>>) input.stream()
.collect(Collectors.toMap(v -> (String) v.get("rollno"), e -> e));
System.out.println(result);
}
}
It iterates over a collection of HashMaps, takes the key in which it should be stored in the result HashMap, then it creates a Map of Maps where the key is the "rollno" from the input map, and value is the input map itself.
As I don't know the type of Object you are using, so I am performing this on String. But it is valid for any type of object.
HashMap result = (HashMap<String, HashMap<String, String>>) listOfHashMaps.stream()
.collect(Collectors.toMap(e-> e.get("roll"),e->e));
As type casting will help you to achieve this.
You can use Collectors.toMap with Function.identity() as below,
list.stream()
.collect(toMap(e->e.get("rollno"), Function.identity()));
public static void main(String[] args) {
List<HashMap<String, String>> input = new ArrayList<>();
HashMap<String, String> subinput1 = new HashMap<>();
subinput1.put("name", "name1");
subinput1.put("rollno", "rollno1");
input.add(subinput1);
HashMap<String, String> subinput2 = new HashMap<>();
subinput2.put("name", "name2");
subinput2.put("rollno", "rollno2");
input.add(subinput2);
//Test key conflict
HashMap<String, String> subinput3 = new HashMap<>();
subinput2.put("name", "name3");
subinput2.put("rollno", "rollno2");
input.add(subinput2);
System.out.println("input:"+ JSONObject.toJSONString(input));
HashMap<String, HashMap<String, String>> result = (HashMap<String, HashMap<String, String>>)
input.stream()
.collect(Collectors.toMap(
v -> (String) v.get("rollno"),
Function.identity(),(oldValue, newValue) -> newValue
));
//fastjson hashmap-toString use =
System.out.println(JSONObject.toJSONString(result));
}
Probably something like this:
Map<RollNo, List<Roll>> rollsPerType = rolls.stream()
.collect(groupingBy(Roll::getRollNo));
where Roll is the main object and RollNo is the property inside (since you didn't provide the actual definitions).
learning Java and have figured out how to store a hashmap in an array. But I can't figure out how to get to the stored data. Here is a simplified version of what I'm doing. I've got as far as displaying the specific array items, but how do I access the hash map stored in the array?
import java.util.*;
public class HelloWorld {
public static void main(String[] args) {
Map<String, String> custOrder = new HashMap<String, String>();
List ordersPlaced = new ArrayList();
custOrder.put("colour", "blue");
custOrder.put("manu", "bmw");
custOrder.put("body", "4x4");
ordersPlaced.add(custOrder);
custOrder = new HashMap();
custOrder.put("colour", "green");
custOrder.put("manu", "merc");
custOrder.put("body", "saloon");
ordersPlaced.add(custOrder);
System.out.println(ordersPlaced.get(0).toString());
}
}
Hope that makes sense. Thanks in advance
Neil
You're already accessing it.
In order to get the iterate on the map's items, you can:
ordersPlaced.get(0).forEach((key, value) -> {
System.out.println("Key is: " + key + ", Value is: " + value);
});
Or, earlier to Java 8, something like:
for (Map.Entry<String, String> entry : ordersPlaced.get(0).entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
}
Please note that you should avoid using raw type list. Instead, you should have:
List<Map<String, String>> ordersPlaced = new ArrayList<>();
And then:
Map<String, String> m = ordersPlaced.get(0);
You know it already.
You can get back the stored map by writing
Map<String, String> placedCustOrder = ordersPlaced.get(0);
And avoid using raw types while using List. Declare your list as
List<Map<String, String>> ordersPlaced = new ArrayList<>();
I would like to know how to access the colour of the data stored in the array at location 0
Since you got the map as I said in the line 1
Map<String, String> placedCustOrder = ordersPlaced.get(0);
String colorVal = placedCustOrder.get("colour");
I strongly suggest you to look through Map documentation before proceeding further.
I want to compute the union of the keys of two hashmaps. I wrote the following code (MWE below), but I
get UnsupportedOperationException. What would be a good of accomplishing this?
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class AddAll {
public static void main(String args[]){
Map<String, Integer> first = new HashMap<String, Integer>();
Map<String, Integer> second = new HashMap<String, Integer>();
first.put("First", 1);
second.put("Second", 2);
Set<String> one = first.keySet();
Set<String> two = second.keySet();
Set<String> union = one;
union.addAll(two);
System.out.println(union);
}
}
So, union is not a copy of one, it is one. It is first.keySet(). And first.keySet() isn't a copy of the keys of first, it's a view, and won't support adds, as documented in Map.keySet().
So you need to actually do a copy. The simplest way is probably to write
one = new HashSet<String>(first);
which uses the "copy constructor" of HashSet to do an actual copy, instead of just referring to the same object.
Remember the keySet is the actual data of the map, it's not a copy. If it let you call addAll there, you'd be dumping all those keys into the first map with no values! The HashMap purposely only allows you to add new mappings by using the put type methods of the actual map.
You want union to be an actual new set probably, not the backing data of the first hashmapL
Set<String> one = first.keySet();
Set<String> two = second.keySet();
Set<String> union = new HashSet<String>(one);
union.addAll(two);
Use below code instead
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class AddAll {
public static void main(String args[]){
Map<String, Integer> first = new HashMap<String, Integer>();
Map<String, Integer> second = new HashMap<String, Integer>();
Map<String, Integer> union = new HashMap<String, Integer>();
first.put("First", 1);
second.put("Second", 2);
union.putAll(first);
union.putAll(second);
System.out.println(union);
System.out.println(union.keySet());
}
}
My JSON string has nested values.
Something like
"[{"listed_count":1720,"status":{"retweet_count":78}}]"
I want the value of the retweet_count.
I'm using Jackson.
The code below outputs "{retweet_count=78}" and not 78. I'm wondering if I can get nested values the kind of way PHP does it i.e status->retweet_count. Thanks.
import java.io.IOException;
import java.util.List;
import java.util.Map;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
public class tests {
public static void main(String [] args) throws IOException{
ObjectMapper mapper = new ObjectMapper();
List <Map<String, Object>> fwers = mapper.readValue("[{\"listed_count\":1720,\"status\":{\"retweet_count\":78}}]]", new TypeReference<List <Map<String, Object>>>() {});
System.out.println(fwers.get(0).get("status"));
}
}
If you know the basic structure of the data you're retrieving, it makes sense to represent it properly. You get all sorts of niceties like type safety ;)
public static class TweetThingy {
public int listed_count;
public Status status;
public static class Status {
public int retweet_count;
}
}
List<TweetThingy> tt = mapper.readValue(..., new TypeReference<List<TweetThingy>>() {});
System.out.println(tt.get(0).status.retweet_count);
Try something like that. If you use JsonNode your life gonna be easier.
JsonNode node = mapper.readValue("[{\"listed_count\":1720,\"status\":{\"retweet_count\":78}}]]", JsonNode.class);
System.out.println(node.findValues("retweet_count").get(0).asInt());
You can probably do System.out.println(fwers.get(0).get("status").get("retweet_count"));
Edit 1:
Change
List <Map<String, Object>> fwers = mapper.readValue(..., new TypeReference<List <Map<String, Object>>>() {});
to
List<Map<String, Map<String, Object>>> fwers = mapper.readValue(..., new TypeReference<List<Map<String, Map<String, Object>>>>() {});
And then do System.out.println(fwers.get(0).get("status").get("retweet_count"));
You don't have a Map of pairs, you have a Map of <String, Map<String, Object>> pairs.
Edit 2:
Alright I get it. So you have a list of maps. And in the first map in the list, you have a kv pair where the value is an integer, and another kv pair where the value is another map. When you say you have a list of maps of maps it complains because the kv pair with the int value isn't a map (it's just an int). So you either have to make all of your kv pairs maps (change that int to a map) and then use my edits above. Or you can use your original code, but cast the Object to a Map when you know it is a Map.
So try this:
Map m = (Map) fwers.get(0).get("status");
System.out.println(m.get("retweet_count"));