Reputation: 517
I have 2 Lists:
List<String> subjectArr = Arrays.asList("aa", "bb", "cc");
List<Long> numArr = Arrays.asList(2L, 6L, 4L);
How do I create new List
and zip two Lists into it?
List<?> subjectNumArr = zip(subjectArr, numArr);
// subjectNumArr == [{'aa',2},{'bb',6},{'cc',4}]
Upvotes: 50
Views: 67279
Reputation: 39576
The operation you want is called zipping.
You need to implement a method zip
:
public static <A, B> List<Map.Entry<A, B>> zip(List<A> as, List<B> bs) {
Iterator<A> it1 = as.iterator();
Iterator<B> it2 = bs.iterator();
List<Map.Entry<A, B>> result = new ArrayList<>();
while (it1.hasNext() && it2.hasNext()) {
result.add(Map.entry(it1.next(), it2.next()));
}
return result;
}
And you use it like this:
zip(subjectArr, numArr);
Upvotes: 7
Reputation: 100269
Here's Java-8 solution using the Pair
class (like in @ZhekaKozlov's answer):
public static <A, B> List<Pair<A, B>> zipJava8(List<A> as, List<B> bs) {
return IntStream.range(0, Math.min(as.size(), bs.size()))
.mapToObj(i -> new Pair<>(as.get(i), bs.get(i)))
.collect(Collectors.toList());
}
In Java 9 onwards you can use Map.entry()
:
public static <A, B> List<Map.Entry<A, B>> zipJava9(List<A> as, List<B> bs) {
return IntStream.range(0, Math.min(as.size(), bs.size()))
.mapToObj(i -> Map.entry(as.get(i), bs.get(i)))
.collect(Collectors.toList());
}
Upvotes: 50
Reputation: 48693
I believe that the most concise and idiomatic way to approach this would be to use Tagir Valeev's answer, but allow an optional third param.
This third param would be a BiFunction that will combine the left and right items in each of the respective lists.
Here are the relevant imports:
import java.util.AbstractMap.SimpleEntry;
import java.util.List;
import java.util.function.BiFunction;
import java.util.stream.IntStream;
Now, here is the implementation of ListUtils.zip
with two method signatures:
<L,R> zip(List<L>, List<L>): List<SimpleEntry<L,R>>
<L,R,E> zip(List<L>, List<L>, BiFunction<L,R,E>): List<SimpleEntry<L, R>>
/** Utility class for working with lists. */
public class ListUtils {
/**
* Zips two lists into a list of {@link SimpleEntry} pairs.
*
* @param left the first list
* @param right the second list
* @param <L> the type of items in the first list
* @param <R> the type of items in the second list
* @return a list of {@link SimpleEntry} pairs containing items from both lists
*/
public static <L, R> List<SimpleEntry<L, R>> zip(List<L> left, List<R> right) {
return zip(left, right, SimpleEntry::new);
}
/**
* Zips two lists into a list of elements produced by the given combiner function.
*
* @param left the first list
* @param right the second list
* @param combiner the function that combines items from both lists
* @param <L> the type of items in the first list
* @param <R> the type of items in the second list
* @param <E> the type of items in the resulting list
* @return a list of items of type {@code Out} produced by combining items from the input lists
*/
public static <L, R, E> List<E> zip(List<L> left, List<R> right, BiFunction<L, R, E> combiner) {
return IntStream.range(0, Math.min(left.size(), right.size()))
.mapToObj(i -> combiner.apply(left.get(i), right.get(i)))
.toList();
}
}
To demonstrate the variant that takes a bi-function, we can create our own Pair
class implementation.
/**
* A simple record to represent a pair of elements.
*
* @param <L> the type of the left element
* @param <R> the type of the right element
*/
record Pair<L, R>(L left, R right) {
public static <L, R> Pair<L, R> of(L left, R right) {
return new Pair<>(left, right);
}
}
Here is how you would use both variants:
public static void main(String[] args) {
List<Integer> list1 = List.of(1, 2, 3);
List<String> list2 = List.of("a", "b", "c");
// Default implementation
List<SimpleEntry<Integer, String>> zipped = zip(list1, list2);
System.out.println(zipped);
// Custom implementation with Pair
List<Pair<Integer, String>> zipped2 = zip(list1, list2, Pair::of);
System.out.println(zipped2);
}
Here is the output:
[1=a, 2=b, 3=c]
[Pair[left=1, right=a], Pair[left=2, right=b], Pair[left=3, right=c]]
Upvotes: 0
Reputation: 21239
The StreamEx library has zip
and zipWith
methods which can be used to achieve the desired result:
record SubjectAndNum(String subject, Long num) {}
List<SubjectAndNum> subjectNumArr =
StreamEx.zip(subjectArr, numArr, SubjectAndNum::new).toList();
or
List<Map.Entry<String, Long>> subjectNumArr =
StreamEx.zip(subjectArr, numArr, Map::entry).toList();
or
List<Map.Entry<String, Long>> subjectNumArr =
StreamEx.of(subjectArr).zipWith(StreamEx.of(numArr)).toList();
Upvotes: 0
Reputation: 10695
As per related question, you can use Guava (>= 21.0) to do this:
List<String> subjectArr = Arrays.asList("aa", "bb", "cc");
List<Long> numArr = Arrays.asList(2L, 6L, 4L);
List<Pair> pairs = Streams.zip(subjectArr.stream(), numArr.stream(), Pair::new)
.collect(Collectors.toList());
Note that the guava method is annotated as @Beta
, though what that means in practice is up to interpretation, the method has not changed since version 21.0.
Upvotes: 16
Reputation: 6109
To get an Iterator<C>
from an Iterator<A>
, an Iterator<B>
, and a BiFunction<A, B, C>
:
public static <A, B, C> Iterator<C> map(Iterator<A> a, Iterator<B> b, BiFunction<A, B, C> f) {
return new Iterator<C>() {
public boolean hasNext() {
return a.hasNext() && b.hasNext(); // This uses the shorter of the two `Iterator`s.
}
public C next() {
return f.apply(a.next(), b.next());
}
};
}
Upvotes: 12
Reputation: 6675
You should create an ArrayList of List:
ArrayList<List> subjectNumArr = new ArrayList<>();
Iterator iter = subjectArr.iterator();
int count=0;
while(iter.hasNext()){
subjectNumArr.add(Arrays.asList(iter.next(),numArr.get[count++]);
}
Upvotes: 1
Reputation: 10695
Use one of the answers from Zipping streams using JDK8 with lambda (java.util.stream.Streams.zip) to zip and apply a function at the same time
e.g. Using a zipped Stream:
<A,B,C> Stream<C> zipped(List<A> lista, List<B> listb, BiFunction<A,B,C> zipper){
int shortestLength = Math.min(lista.size(),listb.size());
return IntStream.range(0,shortestLength).mapToObject( i -> {
return zipper.apply(lista.get(i), listb.get(i));
});
}
for which you may also use Guava's Streams.zip()
Upvotes: 2
Reputation: 7462
Use an ArrayList of Map.Entry<String, Long>
, checking that both arraylists have equal size (as it seems to be your requirement), like that:
List<Map.Entry<String,Long>> subjectNumArr = new ArrayList<>(numArr.size());
if (subjectArr.size() == numArr.size()) {
for (int i = 0; i < subjectArr.size(); ++i) {
subjectNumArr.add(new AbstractMap.SimpleEntry<String, Long>(subjectArr.get(i), numArr.get(i));
}
}
That's all the code you need!
Then, to iterate over the results, use something like:
for (Map.Entry<String, Long> entry : subjectNumArr) {
String key = entry.getKey();
Long value = entry.getValue();
}
or, you can simply get the pair at position i (keeping insertion order), by:
Map.Entry<String, Long> entry = subjectNumArr.get(i);
This can also hold duplicate entries, unlike the Map solution that I initially suggested, without requiring to define your own (Pair) class.
Upvotes: 8
Reputation: 21598
My ideas:
Arrays.asList
. It is easy to understand, short and automatically generates generic collections.List
in the example, maybe Collection
would be even better. Only declare variables as ArrayList
if you need the list to be so specific. That will give you the possibility to use other implementations, without having to change much code.I would create Pair
objects like this:
import java.util.*;
class Pair {
String subject;
Long num;
}
public class Snippet {
public static void main(String[] args) {
List<String> subjectArr = Arrays.asList("aa", "bb", "cc");
List<Long> numArr = Arrays.asList(2l,6l,4l);
// create result list
List<Pair> pairs = new ArrayList<>();
// determine result size
int length = Math.min(subjectArr.size(), numArr.size());
// create pairs
for (int position = 0; position < length; position++) {
Pair pair = new Pair();
pair.subject = subjectArr.get(position);
pair.num = numArr.get(position);
pairs.add(pair);
}
}
}
Upvotes: 2
Reputation: 53
I agree with vefthym however if you have to do using list then create a class like below -:
class DirtyCoding{
String subject;
int numbr;
}
Then iterate over the your list, create object of DirtyCoding
, populate it and add then add it to List<DirtyCoding>
.
Upvotes: 1
Reputation: 5047
In Java 8: You can do this in one line using Stream and Collectors class.
In Java 7/6/5:
List list = new ArrayList();
if(subjectArr.size() == numArr.size())
{
for (int i = 0; i < subjectArr.size(); i++) { // Loop through every subject/name
list.add(subjectArr.get(i) + " " + numArr.get(i)); // Concat the two, and add it
}
}
Upvotes: -3