Convert 2 collections into a Map - java

I have 2 Lists as List<String> a and List<String> b of equal size.
What is the most efficient way to create a Map<String, String> in Java 8 using lambdas or something else where List<String> a are the keys and List<String> b are the values?
The Java 7 way is as follows:
Map<String, String> map = new HashMap<String, String>();
for(int i=0;i<a.size();i++)
map.put(a.get(i), b.get(i));

Since there is no zip operation on Stream (and no Pair class), a simple solution is to use an IntStream and loop over the indexes of each List.
Map<String, String> map =
IntStream.range(0, a.size()).boxed().collect(Collectors.toMap(a::get, b::get));
Alternatively, you can use the StreamEx library which offers a zip method and have:
Map<String, String> map = EntryStream.zip(a, b).toMap();

Related

How to group values of map into lists based on key with java streams?

Say I have mappings from Strings to a Mapping from Strings to int, such as
Map<String, Map<String, Integer>> myMap1 = new HashMap<>();
myMap1.put("A", Map.of("X", 1))
myMap1.put("B", Map.of("Y", 1))
Map<String, Map<String, Integer>> myMap2 = new HashMap<>();
myMap2.put("B", Map.of("Y", 3))
I would like to merge these mappings such that we get a mapping where the key is the inner map's key, and the value would be the average of the inner maps values of the same keys.
So the output to the example above would be
{"X" : 1, "Y", 2}
We can discard the outer map's key altogether.
What is the nicest way to do this with java. I thought there might be some nice way to do it with Collectors.groupBy method but I am quite inexperienced with this.
I’m going to assume there might be more than two maps, so let’s make a List out of them:
Collection<Map<String, Map<String, Integer>>> myMaps =
List.of(myMap1, myMap2);
Then we can use flatMap on the values() of each Map, which gives us a stream of Map<String, Integer> maps.
We can obtain the entrySet() of each of those, then apply flatMap to the streams of those entry sets, to give us a single Stream of Map.Entry<String, Integer> objects, which we can then group.
There is a groupingBy method which takes a second Collector for customizing the values of the groups, by collecting all of the grouped values seen. We can use that to get our averages, using an averaging collector.
Map<String, Double> averages =
myMaps.stream().flatMap(map -> map.values().stream()) // stream of Map<String, Integer>
.flatMap(innerMap -> innerMap.entrySet().stream()) // stream of Map.Entry<String, Integer>
.collect(Collectors.groupingBy(Map.Entry::getKey, // group by String key
Collectors.averagingInt(Map.Entry::getValue))); // value for each key = average of its Integers

Converting a collection to Map by sorting it using java 8 streams

I have a list that I need to custom sort and then convert to a map with its Id vs. name map.
Here is my code:
Map<Long, String> map = new LinkedHashMap<>();
list.stream().sorted(Comparator.comparing(Building::getName)).forEach(b-> map.put(b.getId(), b.getName()));
I think this will do the job but I wonder if I can avoid creating LinkedHashMap here and use fancy functional programming to do the job in one line.
You have Collectors.toMap for that purpose :
Map<Long, String> map =
list.stream()
.sorted(Comparator.comparing(Building::getName))
.collect(Collectors.toMap(Building::getId,Building::getName));
If you want to force the Map implementation that will be instantiated, use this :
Map<Long, String> map =
list.stream()
.sorted(Comparator.comparing(Building::getName))
.collect(Collectors.toMap(Building::getId,
Building::getName,
(v1,v2)->v1,
LinkedHashMap::new));

Data Structure Set, List, Map or Tree?

I am trying to choose a Java data structure with the following properties
Key: Long
Value: Set
Is there a structure that I can index into and add values to the Set?
For example say I have the object <1, [a,b,c]> and I want to add d this so that the output is <1, [a,b,c,d]>?
Can this be easily done in java?
If you are able to use a third party library, Guava has Multimaps, which make it easy to store multiple values for a single key.
For example:
import com.google.common.collect.HashMultimap;
HashMultimap<Long, String> multimap = HashMultimap.create();
multimap.put(1L, "a");
multimap.put(1L, "b");
multimap.put(1L, "c");
multimap.put(1L, "d");
See the docs.
As others have stated, you will be best served by a Map<Long, Set<String>>. In your example:
Map<Long, Set<String>> myMap = new HashMap<Long, Set<String>>();
Set<String> initialSet = new HashSet<String>();
initialSet.add("a");
initialSet.add("b");
initialSet.add("c");
myMap.put(1, initialSet);
myMap.get(1).add("d"); // to add the "d"
I have the object <1, [a,b,c]> and I want to add d this so that the output is <1, [a,b,c,d]>?
Map<Long, Set<Character>
would work good
Yes a Map would work.
Map yourmap = new HashMap<Long, List<String>>();
Just use:
HashMap<Long, String> map = new HashMap<Long, ArrayList<String>>();
If you want to add something to it just
ArrayList<String> list = map.get(KEY_YOU_WANT_TO_CHECK);
if(list == null){
list = new ArrayList<String>();
list.add(STRING_YOU_WANT_TO_ADD);
map.put(KEY_YOU_WANT_TO_CHECK,list);
}else{
list.add(STRING_YOU_WANT_TO_ADD);
}
Of course you can replace ArrayList with HashSet also with Vector and TreeSet

How to map values in a map in Java 8? [duplicate]

This question already has answers here:
Using streams, how can I map the values in a HashMap?
(4 answers)
Closed 8 years ago.
Say I have a Map<String, Integer>. Is there an easy way to get a Map<String, String> from it?
By easy, I mean not like this:
Map<String, String> mapped = new HashMap<>();
for(String key : originalMap.keySet()) {
mapped.put(key, originalMap.get(key).toString());
}
But rather some one liner like:
Map<String, String> mapped = originalMap.mapValues(v -> v.toString());
But obviously there is no method mapValues.
You need to stream the entries and collect them in a new map:
Map<String, String> result = map.entrySet()
.stream()
.collect(Collectors.toMap(Entry::getKey, e -> String.valueOf(e.getValue()));
The easiest way to do so is:
Map<String, Integer> map = new HashMap<>();
Map<String, String> mapped = map.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, entry -> String.valueOf(entry.getValue())));
What you do here, is:
Obtain a Stream<Map.Entry<String, Integer>>
Collect the results in the resulting map:
Map the entries to their key.
Map the entries to the new values, incorporating String.valueOf.
The reason you cannot do it in a one-liner, is because the Map interface does not offer such, the closest you can get to that is map.replaceAll, but that method dictates that the type should remain the same.

How do you properly nest multiple ArrayLists / Maps in Java?

I am trying to run a very simple program, and I'm stuck on the basics of declaring the nested lists and maps.
I'm working on a project which requires me to store polynomials into an ArrayList.
Each polynomial is named, so I want a key/value map to pull the name of the polynomial (1, 2, 3 etc.) as the key, and the actual polynomial as the value.
NOW the actual polynomial requires key values as well because the nature of this program requires that the exponent be associated with the coefficient.
So for example I need an ArrayList of polynomials, say the first one is a simple:
polynomial 1: 2x^3
the array list contains the whole thing as a map, and the map contains key: polynomial 1 and value: is a Map... with the 2 and 3 being key/values.
The code I have is below but I'm not 100% on how to format such nested logic.
public static void main(String[] args) throws IOException{
ArrayList<Map> polynomialArray = new ArrayList<Map>();
Map<String, Map<Integer, Integer>> polynomialIndex = new Map<String, Map<Integer, Integer>>();
String filename = "polynomials.txt";
Scanner file = new Scanner(new File(filename));
for(int i = 0; file.hasNextLine(); i++){
//this will eventually scan polynomials out of a file and do stuff
}
EDIT:
Updated the key/value in Map, still having issues.
The code above is giving me the following error:
Cannot instantiate the type Map<String,Map<Integer,Integer>>
So how then do I go about doing this or am I just going about this all the wrong way?
You can't instantiate new Map<String, Map<Integer, Integer>>() because java.util.Map is an interface (it doesn't have a constructor). You need to use a concrete type like java.util.HashMap:
Map<String, Map<Integer, Integer>> polynomialIndex = new HashMap<String, Map<Integer, Integer>>();
Also, if you're using Java 7 or above, you can use generic type inference to save some typing:
Map<String, Map<Integer, Integer>> polynomialIndex = new HashMap<>();
This is incorrect:
Map<String, Map<Integer>> polynomialIndex = new Map<String, Map<Integer>>();
Maps need to have two parameters and your nested map Map<Integer> only has one. I think you're looking for something like:
Map<String, Map<Integer, Integer>> polynomialIndex = new Map<String, Map<Integer, Integer>>();
Or it may be best done separate.
Map<String, Map> polynomialIndex = new Map<String, Map>();
Map<Integer, Integer> polynomialNumbers = new Map<Integer, Integer>();
With this you can just put the numbers in the polynomailNumbers Map then use that in polynomialIndex.

Categories

Resources