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.
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());
Best way in java to merge two lists to one map? - Stack Overflow
Creating Map composed of 2 Lists using stream().collect in Java - Stack Overflow
collections - Merge Map<String, List<String> Java 8 Stream - Stack Overflow
java - Group by two lists in one map using lambda - Code Review Stack Exchange
Assuming, that both lists are of equal length and that keys and values have the same index in both lists, then this is an easy approach:
for (int i = 0; i < strings.size(); i++) {
map.put(strings.get(i), integers.get(i));
}
Over here in Scala land, we'd write it as:
integers.zip(strings).toMap
:-P
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));
You can do this by iterating over all the entries in mapAdded and merging them into mapGlobal.
The following iterates over the entries of mapAdded by calling forEach(action) where the action consumes the key and value of each entry. For each entry, we call merge(key, value, remappingFunction) on mapGlobal: this will either create the entry under the key k and value v if the key didn't exist or it will invoke the given remapping function if they already existed. This function takes the 2 lists to merge, which in this case, are first added to a TreeSet to ensure both unique and sorted elements and converted back into a list:
mapAdded.forEach((k, v) -> mapGlobal.merge(k, v, (v1, v2) -> {
Set<String> set = new TreeSet<>(v1);
set.addAll(v2);
return new ArrayList<>(set);
}));
If you want to run that potentially in parallel, you can create a Stream pipeline by getting the entrySet() and calling parallelStream() on it. But then, you need to make sure to use a map that supports concurrency for mapGlobal, like a ConcurrentHashMap.
ConcurrentMap<String, List<String>> mapGlobal = new ConcurrentHashMap<>();
// ...
mapAdded.entrySet().parallelStream().forEach(e -> mapGlobal.merge(e.getKey(), e.getValue(), (v1, v2) -> {
Set<String> set = new TreeSet<>(v1);
set.addAll(v2);
return new ArrayList<>(set);
}));
Using foreach over Map can be used to merge given arraylist.
public Map<String, ArrayList<String>> merge(Map<String, ArrayList<String>> map1, Map<String, ArrayList<String>> map2) {
Map<String, ArrayList<String>> map = new HashMap<>();
map.putAll(map1);
map2.forEach((key , value) -> {
//Get the value for key in map.
ArrayList<String> list = map.get(key);
if (list == null) {
map.put(key,value);
}
else {
//Merge two list together
ArrayList<String> mergedValue = new ArrayList<>(value);
mergedValue.addAll(list);
map.put(key , mergedValue);
}
});
return map;
}
Easiest way is to use Map to group by operators.Actually you can use Map instead of List but you can also iterate through map to append key and values and insert them back to the list. You can use the following code to achieve the result you want.
public static void main(String[] args) {
List<Object> list1 = new ArrayList<Object>();
list1.add("operator1, id1");
list1.add("operator1, id2");
list1.add("operator2, id3");
list1.add("operator2, id4");
list1.add("operator3, id5");
list1.add("operator4, id6");
List<Object> list2 = new ArrayList<Object>();
list2.add("operator4, id7");
list2.add("operator3, id8");
list2.add("operator2, id9");
list2.add("operator2, id10");
list2.add("operator1, id11");
list2.add("operator1, id12");
Map<String, String> map = new HashMap<String, String>();
List<Object> list3 = new ArrayList<Object>();
Iterator<Object> it1 = list1.iterator();
Iterator<Object> it2 = list2.iterator();
while (it1.hasNext() && it2.hasNext()) {
String[] line1 = ((String) it1.next()).split(",");
map.put(line1[0], map.get(line1[0]) == null ? line1[1] : map.get(line1[0]) + ", " + line1[1]);
String[] line2 = ((String) it2.next()).split(",");
map.put(line2[0], map.get(line2[0]) == null ? line2[1] : map.get(line2[0]) + ", " + line2[1]);
}
for (Map.Entry<String, String> entry : map.entrySet()) {
String item = entry.getKey() + "," + entry.getValue();
System.out.println(item);
list3.add(item);
}
}
After running the code above the result will be :
operator4, id6, id7
operator1, id1, id2, id11, id12
operator3, id5, id8
operator2, id3, id4, id9, id10
Instead of List3 create a map as target source. Map will have operator as key and comma separated ids as value.
- Read the content of list by iterating it.
- Split the content with comma as a delimiter.
- If operator is not present in map then add it else read the value, append the id and put it back in the map.
- In the end it is just a matter of iterating the map to print what you need.
You should check if locations iterator has next element
if (locations.hasNext()) {
tag = locations.next();
} else {
tag = null;
}
Since every entry to a Map has to have a key but not a value, you might as well just iterate until names is done and then stop, since further values without keys can't be stored:
for (int i = 0; i < names.size(); i++) {
finalMap.put(names.get(i), i < location.size() ? location.get(i) : null);
}
final List<HashMap<String, String>> joinedById = list1.stream()
.flatMap(m1 -> list2.stream()
.filter(y -> m1.get("id").equals(y.get("id")))
.map(m2 -> new HashMap<String, String>() {{
putAll(m1);
putAll(m2);
}}))
.collect(Collectors.toList());
There are lots of ways, with and without streams. The best way would depend on the more exact input requirements, which you haven’t given us. The following would work for the example lists in your question:
if (list1.size() != list2.size()) {
throw new IllegalStateException("Lists don’t match, not same size");
}
List<Map<String, Character>> comnbinedList = IntStream.range(0, list1.size())
.mapToObj(i -> {
Map<String, Character> m1 = list1.get(i);
Character id1 = m1.get("id");
Map<String, Character> m2 = list2.get(i);
Character id2 = m1.get("id");
if (! id1.equals(id2)) {
throw new IllegalStateException("Lists don’t match, id " + id1 + " != " + id2);
}
HashMap<String, Character> mergedMap = new HashMap<>(m1);
mergedMap.putAll(m2);
return mergedMap;
})
.collect(Collectors.toList());
The result is:
[{id=1, attr2=b, attr1=a, attr4=y, attr3=x}, {id=2, attr2=d, attr1=c, attr4=z, attr3=x}, {id=3, attr2=f, attr1=e, attr4=y, attr3=z}]
You will want to declare "id" a constant, of course.
You can do something like this:
List<Food> foodList = fruits.keySet().stream()
.concat(veggies.keySet().stream())
.distinct()
.map(id -> {
Food food = new Food();
food.setId(id);
food.setFruitsList(fruits.getOrDefault(id, new ArrayList<>()));
food.setVeggiesList(veggies.getOrDefault(id, new ArrayList<>()));
return food ;
})
.collect(Collectors.toList());
If your Food class has a constructor taking the three parameters, it gets just a little more concise:
List<Food> foodList = fruits.keySet().stream()
.concat(veggies.keySet().stream())
.distinct()
.map(id -> new Food(
id,
fruits.getOrDefault(id, new ArrayList<>()),
veggies.getOrDefault(id, new ArrayList<>())
)
.collect(Collectors.toList());
If you know the ids are the same in both maps, you can skip the .concat() and .distinct() steps (which are quite expensive), and just use get() instead of getOrDefault().
You can also (in any case) do
Set<Integer> allKeys = new HashSet<>(fruits.keySet());
allKeys.addAll(veggies.keySet());
List<Food> foodList = allKeys.stream()
.map(/* as before */)
.collect(Collectors.toList());
which is not quite a "pure stream" a solution, but is probably more efficient.
You can merge the two keys streams and use a distinct method to get only unique keys.
List<Food> foods = Stream.concat(fruits.keySet().stream() , veggies.keySet().stream())
.distinct()
.map(e -> new Food(e, fruits.get(e), veggies.get(e)))
.collect(Collectors.toList());
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.
This is my implementation, i generalized for n lists.
public static Map<String,Object> mixLists(List<?>...lists){
Map<String,Object> map = new HashMap<String,Object>();
for (int i = 0;i < lists.length;i++){
if (!lists[i].isEmpty())
map.put(lists[i].get(0).getClass().getName(), lists[i]);
}
return map;
}
The function returns a map that you can iterate like a List.
This is not the best solution but you can create a new Object that has Post & Comment in it. Then add elements with same username form Post and Comment to it:
class PostData {
Post post;
Comment comment;
@Override
public String toString() {
return post.toString() + comment.toString();
}
}
Then create a list of that object:
List<PostData> postData = new ArrayList<>();