First of all, assuming you are going to create a HashMap, your key class (NameAndAge) must override equals and hashCode().
Second of all, in order to be efficient, I suggest you first create a Map<String,NameAndSalary> from the second List:
Map<String,NameAndSalary> helper =
nameAndSalaryList.stream()
.collect(Collectors.toMap(NameAndSalary::getName,
Function.identity()));
Finally, you can create the Map you want:
Map<NameAndAge,NameAndSalary> output =
nameAndAgeList.stream()
.collect(Collectors.toMap(Function.identity(),
naa->helper.get(naa.getName())));
Answer from Eran on Stack OverflowFirst of all, assuming you are going to create a HashMap, your key class (NameAndAge) must override equals and hashCode().
Second of all, in order to be efficient, I suggest you first create a Map<String,NameAndSalary> from the second List:
Map<String,NameAndSalary> helper =
nameAndSalaryList.stream()
.collect(Collectors.toMap(NameAndSalary::getName,
Function.identity()));
Finally, you can create the Map you want:
Map<NameAndAge,NameAndSalary> output =
nameAndAgeList.stream()
.collect(Collectors.toMap(Function.identity(),
naa->helper.get(naa.getName())));
This should do the trick, too:
Map<NameAndAge, NameAndSalary> map = new HashMap<>();
nameAndAgeList.forEach(age -> {
NameAndSalary salary = nameAndSalaryList.stream().filter(
s -> age.getName().equals(s.getName())).
findFirst().
orElseThrow(IllegalStateException::new);
map.put(age, salary);
});
Mind that it would throw an IllegalStateException if a matching name can't be found.
Creating Map composed of 2 Lists using stream().collect in Java - Stack Overflow
dictionary - What is the best way to combine two lists into a map in Java? - Stack Overflow
lambda - Java 8 : Iterate from 2 lists and create a Map<String, Custom Object> - Stack Overflow
java - Create map of matching elements in two lists - Stack Overflow
Instead of using an auxiliary list to hold the indices, you can have them generated by an IntStream.
Map<Double, String> map = IntStream.range(0, list1.size())
.boxed()
.collect(Collectors.toMap(i -> list1.get(i), i -> list2.get(i)));
Indeed the best approach is to use IntStream.range(startInclusive, endExclusive) in order to access to each element of both lists with get(index) and finally use Math.min(a, b) to avoid getting IndexOutOfBoundsException if the lists are not of the exact same size, so the final code would be:
Map<Double, String> map2 = IntStream.range(0, Math.min(list1.size(), list2.size()))
.boxed()
.collect(Collectors.toMap(list1::get, list2::get));
Been a while since this question was asked but these days I'm partial to something like:
public static <K, V> Map<K, V> zipToMap(List<K> keys, List<V> values) {
return IntStream.range(0, keys.size()).boxed()
.collect(Collectors.toMap(keys::get, values::get));
}
For those unfamiliar with streams, what this does is gets an IntStream from 0 to the length, then boxes it, making it a Stream<Integer> so that it can be transformed into an object, then collects them using Collectors.toMap which takes two suppliers, one of which generates the keys, the other the values.
This could stand some validation (like requiring keys.size() be less than values.size()) but it works great as a simple solution.
EDIT: The above works great for anything with constant time lookup, but if you want something that will work on the same order (and still use this same sort of pattern) you could do something like:
public static <K, V> Map<K, V> zipToMap(List<K> keys, List<V> values) {
Iterator<K> keyIter = keys.iterator();
Iterator<V> valIter = values.iterator();
return IntStream.range(0, keys.size()).boxed()
.collect(Collectors.toMap(_i -> keyIter.next(), _i -> valIter.next()));
}
The output is the same (again, missing length checks, etc.) but the time complexity isn't dependent on the implementation of the get method for whatever list is used.
I'd often use the following idiom. I admit it is debatable whether it is clearer.
Iterator<String> i1 = names.iterator();
Iterator<String> i2 = things.iterator();
while (i1.hasNext() && i2.hasNext()) {
map.put(i1.next(), i2.next());
}
if (i1.hasNext() || i2.hasNext()) complainAboutSizes();
It has the advantage that it also works for Collections and similar things without random access or without efficient random access, like LinkedList, TreeSets or SQL ResultSets. For example, if you'd use the original algorithm on LinkedLists, you've got a slow Shlemiel the painter algorithm which actually needs n*n operations for lists of length n.
As 13ren pointed out, you can also use the fact that Iterator.next throws a NoSuchElementException if you try to read after the end of one list when the lengths are mismatched. So you'll get the terser but maybe a little confusing variant:
Iterator<String> i1 = names.iterator();
Iterator<String> i2 = things.iterator();
while (i1.hasNext() || i2.hasNext()) map.put(i1.next(), i2.next());
You should use somethins like:
Map<String, B> mapList2 = list2.stream().collect(Collectors.toMap(B::getCode,
Function.identity()); // create temporary map
Map<String,B> result = list1.stream().collect(
LinkedHashMap::new,
(map, a) -> map.put(
a.getCode(), // get code from A
new C(
mapList2.get(a.getCode()).getLocation() // get location from B
a.getStatus() // get status from A
),
Map::putAll); // use LinkedHashMap to keep order
Map<String, C> map = IntStream.range(0, list1.size())
.mapToObj(i -> new CodeIndex(list1.get(i).code(), i))
.collect(Collectors.toMap(
CodeIndex::code,
ci -> new C(list2.get(ci.index()).location(), list1.get(ci.index()).status()),
(a, b) -> a,
LinkedHashMap::new
));
What happens is that we first get all valid indexes of both lists, then we map to a (custom) container class holding both the index and the code. (I called it CodeIndex, but you may as well use Map.Entry or some Pair class.)
At last, we collect it into a LinkedHashMap (because it retains insertion order), mapping the keys to simply CodeIndex::code and the values to new C(«location_from_list2», «status_from_list1»).
Note that this code traverses the lists once.
Of course, this assumes that:
- both lists have the same codes;
- all codes of both
list1andlist2are in the same order.
Note that I used record-style getters, that is, getters with the same name as their corresponding field names. For example, location() instead of getLocation().
First, create a Set using the second list. Then use the toMap collector to create the map which has the string as it's key, and it's existence in the setTwo as the value. Here's how it looks.
Set<String> setTwo = new HashSet<>(listTwo);
Map<String, Boolean> existenceMap = listOne.stream()
.collect(Collectors.toMap(s -> s, setTwo::contains, (a, b) -> a));
I would recommend map with boolean key and values as list
Map<Boolean, List<String>> map = list1.stream()
.collect(Collectors.partitioningBy(list2::contains)); // or set::contains
One approach is to iterate over values and then iterate over the indexes of each array, matching them to the corresponding index in keys:
List<Map<String, String>> maps = values.stream()
.map(a -> IntStream.range(0, keys.size())
.boxed()
.collect(Collectors.toMap(keys::get, i -> a[i])))
.collect(Collectors.toList());
The intermediate boxing is unfortunately necessary in order to use the toMap() collector. If you want to avoid the overhead of boxed indexing, you can define a custom collector in the inner stream:
IntStream.range(0, keys.size())
.collect(HashMap<String, String>::new,
(m, i) -> m.put(keys.get(i), a[i]),
HashMap::putAll)
Please find below two ways to achieve this using java 8 stream:
List values =Arrays.asList("a,aa,aaa".split(","), "b,bb,bbb".split(","));
List<Map<String, String>> maps= values.stream().map(l-> IntStream.range(0, l.length).boxed().collect(Collectors.toMap(i-> String.valueOf(i+1), j-> l[j]))).collect(Collectors.toList());
System.out.println("Printing list of map using boxed"+ maps);
List<Map<String, String>> maps2= values.stream().map(l-> IntStream.range(0, l.length).mapToObj(a-> Integer.valueOf(a)).collect(Collectors.toMap(i-> String.valueOf(i+1), j-> l[j]))).collect(Collectors.toList());
System.out.println("Printing list of map using maptoObject"+ maps2);
Replace value -> true with a lambda item -> list2.contains(item) or a method reference list2::contains
itemList.stream()
.collect(Collectors.toMap(Function.identity(), list2::contains));
and read @michalk's comment.
Try this: for better performance use Set.
Set<String> selected = new HashSet<>(list2);
Map<String, Boolean> mapOfDataListTest = itemList.stream()
.collect(Collectors.toMap(key -> key, value ->selected.contains(value) ));
This should be optimal. You first build a map from the currencies to their commercial banks. Then you run through your centrals building a map from commercial to central (looked up in the first map).
List<CurrencyItem> currenciesByCommercialBank = new ArrayList<>();
List<CurrencyItem> currenciesByCentralBank = new ArrayList<>();
// Build my lookup from CurrencyName to CommercialBank.
Map<CurrencyName, CurrencyItem> commercials = currenciesByCommercialBank
.stream()
.collect(
Collectors.toMap(
// Map from currency name.
ci -> ci.getName(),
// To the commercial bank itself.
ci -> ci));
Map<CurrencyItem, CurrencyItem> commercialToCentral = currenciesByCentralBank
.stream()
.collect(
Collectors.toMap(
// Map from the equivalent commercial
ci -> commercials.get(ci.getName()),
// To this central.
ci -> ci
));
The following code is O(n2), but it should be OK for small collections (which your lists probably are):
return currenciesByCommercialBank
.stream()
.map(c ->
new AbstractMap.SimpleImmutableEntry<>(
c, currenciesByCentralBank.stream()
.filter(c2 -> c.currencyName == c2.currencyName)
.findFirst()
.get()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
The above is appropriate if you want to assert that currenciesByCentralBank contains a match for each item in currenciesByCommercialBank. If the two lists can have mismatches, then the following would be appropriate:
currenciesByCommercialBank
.stream()
.flatMap(c ->
currenciesByCentralBank.stream()
.filter(c2 -> c.currencyName == c2.currencyName)
.map(c2 -> new AbstractMap.SimpleImmutableEntry<>(c, c2)))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
In this case the map will contain all the matches and won't complain about missing entries.
Just to be clear, I think your code is intended to do the following: update the name of each item in list1 to be the name of any item in list2 that has the same ID. There doesn't seem to be anything checking if the names of items in list1 are null.
If that's correct, then:
list2.forEach(obj2 -> list1.stream()
.filter(obj1 -> obj1.getId().equals(obj2.getId()))
.forEach(obj1 -> obj1.setName(obj2.getName()));
If you want to check if name is null, then add a new filter before setting the name:
.filter(Objects::isNull)
As I mentioned in the comments. If the id is a uniqe identifier for your objects, then a Map is more appropriate than a List.
So you better work on such a map (assuming id is an integer):
Map<Integer, Object1> obj1map;
You could create that map from your first list with
obj1map = list1.stream().collect(toMap(Object1::getId, Function.identity()));
Now you can stream over your second list and update the map accordingly:
list2
.stream()
.filter(o -> o.getName() != null) // remove null names
.filter(o -> obj1map.containsKey(o.getId())) // just to make sure
.forEach(o -> obj1map.get(o.getId()).setName(o.getName()));
As has been suggested, using a Map<String,List<Employee>> is probably what you want. I constructed a small class and an example to illustrate the process.
List<Employee> emps = List.of(
new Employee("001", "Jim"),
new Employee("002", "Bob"),
new Employee("001", "June"),
new Employee("002", "Jean"));
// For each employee, using departmentId as the key, construct
// a map containing lists of all employees that have that ID
Map<String, List<Employee>> empMap =
emps
.stream()
.collect(
Collectors.groupingBy(emp -> emp.departmentId)
);
for (Entry<?, ?> e : empMap.entrySet()) {
System.out.println(e.getKey() + "-->" + e.getValue());
}
class Employee {
public String departmentId;
public String name;
public Employee(String departmentId, String name) {
this.departmentId = departmentId;
this.name = name;
}
public String toString() {
return "(" + departmentId + " " + name + ")";
}
}
This displays
001-->[(001 Jim), (001 June)]
002-->[(002 Bob), (002 Jean)]
Another option, which would do the exact same thing is as follows:
This creates an empty map. Then it iterates over the list of employees and computes whether the value for the key exists. If it does, it returns the list and adds the entry to that list. If it doesn't, it then creates the new ArrayList and then adds the employee for that list, storing the list in the provided key.
Map<String, List<Employee>> emp2 = new HashMap<>();
for (Employee emp : emps) {
emp2.compute(emp.departmentId,
(ID, list) -> list == null ? new ArrayList<>()
: list).add(emp);
}
In this case my preference would be the first method.
It is not possible to do this. Because Map can not have duplicate key.
You can create Map<String,List<Employee>> by using Collectors.groupingBy()