Homework: Looking for better strategy, or approach rather than complete code.
I\'v got two arrayLists of integers under two conditions:
I propose the following code:
private static List<Integer> joinTwoLists(List<Integer> a, List<Integer> b) {
final boolean aIsBigger = a.size() > b.size();
final List<Integer> joined = new ArrayList<>(aIsBigger ? a : b);
final AtomicInteger index = new AtomicInteger(1);
for (Integer value : aIsBigger ? b : a) {
joined.add(index.getAndAdd(2), value);
}
return joined;
}
A few comments:
addAll
and subList
)List<Integer> numbers = Arrays.asList(10, 20, 30, 40);
- note that it creates a fixed-size list so you can't add or removeList listSmall, listBig; if (list1.size() < list2.size()) { listSmall = list1; listBig = list2; } else { /* the opposite */}
- then you know that listSmall
is the small one and you only need one loop.Here's another way of thinking about this problem, and a way that would just as easily extend to 3 lists.
First, recognize that the hard part to solve here is iteration, not creating the new list. Creating a new list from an iterable is trivial.
So imagine we have a method like this:
public <T> Iterable<T> interleave(Iterable<? extends T>... lists) {
return new Iterable<T>() {
@Override
public Iterator<T> iterator() {
return new InterleavingIterator<T>(lists);
}
};
}
What we need to make is an Iterator<T>
that cycles through each iterator one at a time. That's the perfect job for a queue (fifo)! Your iterator could look something like this:
class InterleavingIterator<T> implements Iterator<T> {
private final Queue<Iterator<? extends T>> iterators = new LinkedList<>();
public InterleavingIterator(Iterable<? extends T>> iteratables) {
for ( Iterable<T> iterable : iterables ) {
Iterator<T> iterator = iterable.iterator();
if ( iterator.hasNext() ) {
this.iterators.add(iterator);
}
}
}
public boolean hasNext() {
return !iterators.isEmpty();
}
public T next() {
Iterator<T> nextIterator = iterators.poll();
T result = nextIterator.next();
if ( nextIterator.hasNext() ) {
iterators.add(nextIterator);
}
return result;
}
}
In short, every time the next element is requested, the iterator at the top of the queue is popped, the result of next()
is returned, and if the iterator still has elements (hasNext()
) it is put to the back of the queue.
This works exactly the same for any number of lists, and doesn't need any icky condition checking.
To create a new list with it, you could just do:
List<Integer> combined = new ArrayList<Integer>(interleave(list1, list2));
This is my approach.
public static void interleave(ArrayList<Integer> a1, ArrayList<Integer> a2) {
int start = 0;
int i;
int size = a1.size();
for (i = 1; i < a1.size(); i += 2) {
if (start < a2.size()) {
a1.add(i, a2.get(start++));
}
}
while (a1.size() < (size + a2.size())) {
a1.add(i++, a2.get(start++));
}
}
Do you like this solution?
public static void main(final String[] args) {
ArrayList<Integer> numbers = new ArrayList<Integer>();
numbers.add(10); numbers.add(20); numbers.add(30); numbers.add(40);
//numbers.add(50); numbers.add(60); numbers.add(70);
ArrayList<Integer> numbers2 = new ArrayList<Integer>();
numbers2.add(4); numbers2.add(5); numbers2.add(6); numbers2.add(7);
numbers2.add(8); numbers2.add(9); numbers2.add(10); numbers2.add(11);
System.out.println("list1: " + numbers);
System.out.println("list2: " + numbers2);
List<Integer> interleaved = interleave(numbers, numbers2);
System.out.println("\nCombined: " + interleaved);
}
public static List<Integer> interleave(
final List<Integer> list1,
final List<Integer> list2
) {
List<Integer> result
= new ArrayList<Integer>(list1.size() + list2.size());
Iterator<Integer> it1 = list1.iterator();
Iterator<Integer> it2 = list2.iterator();
while (it1.hasNext() || it2.hasNext()) {
if (it1.hasNext()) {
result.add(it1.next());
}
if (it2.hasNext()) {
result.add(it2.next());
}
}
return result;
}