I have a collection:
Collection<Map<String, Object>> items = new ArrayList();
Map<String, Object> item1 = new HashMap();
Map<String, Object> item2 = new HashMap();
item1.put("first_name", "john");
item1.put("last_name", "doe");
item2.put("first_name", "jane");
item2.put("last_name", "doe");
items.add(item1);
items.add(item2);
I would like to filter out using stream all maps in collection that have "first_name" set as "jane". And it should return a collection of maps (same type).
If it could be done by not using stream I don't mind but I'd prefer stream.
Not too hard to do. Just add this to your code.
Collection<Map<String, Object>> items2 = items.stream().filter(a -> !"jane".equals(a.get("first_name"))).collect(Collectors.toList());
You do need to specify what happens if the map doesn't contain the key "first_name" at all, I'm assuming that it needs to have a first_name key (that isn't jane) to be valid post-filter.
Additionally, just a heads up. Your existing code isn't using generics correctly; my IDE at least gave me a warning of unchecked casting on your first three lines. After Java 1.7 you can have it infer generic type arguments, but you still need to supply the symbols <> to make that happen. Here's a fixed version:
Collection<Map<String, Object>> items = new ArrayList<>();
Map<String, Object> item1 = new HashMap<>();
Map<String, Object> item2 = new HashMap<>();
Related
I have 4 separate hashmaps all of the same type. I would like to merge the values of them all into a single list. I know how to set a List to hashMapOne.values(), but this doesn't help me here since I need to add all values from all 4 lists. Can I do this without looping and individually adding each one?
HashMap<String, MyEntity> hashMapOne = new HashMap<String, MyEntity>();
HashMap<String, MyEntity> hashMapTwo = new HashMap<String, MyEntity>();
HashMap<String, MyEntity> hashMapThree = new HashMap<String, MyEntity>();
HashMap<String, MyEntity> hashMapFour = new HashMap<String, MyEntity>();
List<MyEntity> finalList = new ArrayList<MyEntity>();
List<MyEntity> finalList = new ArrayList<MyEntity>();
finalList.addAll(hashMapOne.values());
finalList.addAll(hashMapTwo.values());
finalList.addAll(hashMapThree.values());
finalList.addAll(hashMapFour.values());
If I were you, I'd just use Stream#of for all Map#values, and then call Stream#flatMap and Stream#collect to transform it to a List:
List<MyEntity> finalList = Stream.of(hashMapOne.values(), hashMapTwo.values(),
hashMapThree.values(), hashMapFour.values())
.flatMap(Collection::stream)
.collect(Collectors.toList());
Facing a challenge to come up with an efficient way of merging two ArrayLists of Maps.
The map looks like this:
{Username=User1, Role=Admin}
So one list looks like this:
List1 = [{Username=User1, Role=Admin},{Username=User2, Role=Auditor}]
and so on.
There is another list:
List 2 = [{Username=User1, Role=Integrator},{Username=User2, Role=Manager}]
Note: The users have different roles in different lists.
What i want to end up with is:
MergedList = [{Username=User1, Role=[Admin,Integrator]},{Username=User2, Role=[Auditor,Manager}]
Another Note: The actual list has 50,000 maps and each map has 20entries!! Just tried to keep it simple here.
Below are the stuff i tried. But failed.
Tried putAll.
Tried merge.
Tried something that I found in another post
map2.forEach((k, v) -> map3.merge(k, v, String::concat));
With regard to the performance and massive amount of data, I recommend you to avoid any usage of java-stream (although it is quite quick itself) and the Map::merge method.
Here you have to stick with the constructs closes to the JVM level and for-loops are your friends, here is the simplest approach I am aware of that might work:
final Map<String, Set<String>> newMap = new HashMap<>();
for (Map<String, String> map: list) { // iterate the List<Map>
for (Entry<String, String> entry: map.entrySet()) { // iterate the entries
final String key = entry.getKey(); // get the entry's key
newMap.computeIfAbsent(key, k -> new HashSet<>()); // compute a new pair
newMap.get(key).add(entry.getValue()); // add a value in any case
}
}
Set prevents duplicate values.
This solution assumes the following data structure. Slight variations are easy to apply to the solution above.
List<Map<String, String>> list = new ArrayList<>();
Map<String, String> map1 = new HashMap<>();
map1.put("User1", "Admin");
map1.put("User2", "Auditor");
Map<String, String> map2 = new HashMap<>();
map2.put("User1", "Integrator");
map2.put("User2", "Manager");
map2.put("User3", "Coffee machine");
list.add(map1);
list.add(map2);
You can use Java Streams to achieve this:
Map<String, List<String>> result = Stream.concat(users1.stream(), users2.stream())
.collect(Collectors.groupingBy(m -> m.get("Username"), Collectors.mapping(m -> m.get("Role"), Collectors.toList())));
This groups all the users and collects their roles.
The result will be:
{User1=[Admin, Integrator], User2=[Auditor, Manager]}
I have a generic map
Map<String, List<A<?>>> map = new HashMap<>();
then I have a list like:
List<A<Integer>> list1 = getData();
List<A<String>> list2 = getData2();
I'm trying to add them to the map, but I'm getting an error saying the argument is wrong.
map.put("a", list1);
map.put("b", list2);
//doesn't work
Making the map like
Map<String, List<A>> map = HashMap<>();
doesn't work either.
How can I change "map" to be able to add those 2 lists?
Generics are not evaluated recursively. The wildcard (<?>) does not get evaluated here.
If you have a Map<String, List<A<?>>> then you must add List<A<?>> values to it; List<A<Integer>> does not qualify as a List<A<?>>. List<A<?>> means a List of A instances with unknown types, so you must pass a List of A instances with unknown types.
What you can do is create such a List, explicitly, and add all the elements of your typed-A List to it:
List<A<Integer>> list1 = getData();
List<A<String>> list2 = getData2();
List<A<?>> list1Unknown = new ArrayList<>();
list1.forEach(list1Unknown::add);
List<A<?>> list2Unknown = new ArrayList<>();
list2.forEach(list2Unknown::add);
map.put("a", list1Unknown);
map.put("b", list2Unknown);
If you just want to place any two lists inside the map then use: Map<String, List> map = new HashMap<>();.
Alternatively, if you want to restrict the type of the List that you would like to add:
Map<String, List<? extends Object>> map = new HashMap<>();.
Why I have chosen to extend Object is because it is the only class present in the hierarchies of both Integer and String.
Is there any difference between the following declarations -
List<String> list = new ArrayList<String>();
and
List<String> list = new ArrayList<>();
In both cases anyhow , list will have elements of type String only.
There is no difference. However, the first one is legal in Java <= 7 whereas the second one is legal only in Java 7 and was introduced as a short-hand notation*. The compiler will infer the generic type from the declaration.
*It was basically introduced to remove redundant information and reduce code-noise. So you now have:
Map<String, List<String>> myMap = new HashMap<>();
versus:
Map<String, List<String>> myMap = new HashMap<String, List<String>>();
The first one is a lot easier on the eyes.
I need to use a Map with a List inside :
Map<String, List<String>> keyToGroup = new HashMap<String, ArrayList<String>>();
I am getting compiler error on this line in eclipse.
The only working thing seem to be changing the inside List in the Map to ArrayList
Map<String, ArrayList<String>> keyToGroup = new HashMap<String, ArrayList<String>>();
I had to change the signature of many interfaces' methods, but I still don't get it; why isn't the first definition work?
Isn't it the same, should not
Map<String, List<String>> keyToGroup
&
Map<String, ArrayList<String>>
be the same?
No, they're not. Consider this:
Map<String, List<String>> keyToGroup = new HashMap<String, ArrayList<String>>();
keyToGroup.put("foo", new LinkedList<String>());
The second line is fine, because a LinkedList<String> is a List<String> - but it's not logically fine in terms of adding it to a HashMap<String, ArrayList<String>>, because a LinkedList<String> is not an ArrayList<String>.
To make it clearer:
Map<String, ArrayList<String>> map1 = new HashMap<String, ArrayList<String>>();
Map<String, List<String>> map2 = map1; // This is invalid
map2.put("foo", new LinkedList<String>());
ArrayList<String> oops = map1.get("foo"); // Because this would be broken
This isn't just the case with collections as the type argument. It's even simpler to see with normal inheritance:
List<Banana> bunchOfBananas = new ArrayList<Banana>();
List<Fruit> fruitBowl = bunchOfBananas; // Invalid!
fruitBowl.add(new Apple());
Banana banana = bunchOfBananas.get(0);
Even though every banana is a fruit, so a "collection of bananas" is a "collection of fruit* in the sense of fetching them, not every fruit is a banana.
You can use wildcard parameterized types to help in some cases, but it depends on exactly what you're trying to achieve.
Ask yourself a question if you need particular list implementation in your Map or any List?
In case of particular implementation you can use your last example:
Map<String, ArrayList<String>> keyToGroup = new HashMap<String, ArrayList<String>>();
In case of any list just use:
Map<String, List<String>> keyToGroup = new HashMap<String, List<String>>();
keyToGroup.put("arraylist", new ArrayList<String());
keyToGroup.put("linkedlist", new LinkedList<String());
BTW the second option usually is better from design point of view so if you don't know exactly for now - try using second option first.
No they are not. Generics are not covariant in Java.
If they are covariant you can logically put any type of List instead of ArrayList which defeats the purpose of having generics.
Consider reading Effective Java (2nd Edition) Chapter 5: Generics which has very good explanation of Generics.
Another good read is http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html