stikkos
stikkos

Reputation: 1936

Java - Iterating over every two elements in a list

What's the best way to iterate over a list while processing 2 elements at the same time?

Example:

List<String> strings = Arrays.asList("item 1", "item 2", "item 3", "item 4");
for(int i = 0; i < strings.size(); i++){
    String first = strings.get(i);
    String second = null;
    if(strings.size() > i + 1){
        second = strings.get(i + 1);
    }
    System.out.println("First [" + first + "] - Second [" + second + "]");
}

Results in:

First [item 1] - Second [item 2]
First [item 2] - Second [item 3]
First [item 3] - Second [item 4]
First [item 4] - Second [null]

I would like to achieve:

First [item 1] - Second [item 2]
First [item 3] - Second [item 4]

Upvotes: 10

Views: 43855

Answers (11)

hyde
hyde

Reputation: 62817

Just increase i by 2 on each iteration, instead of ++ (which increments by 1):

for (int i = 0; i + 1 < strings.size(); i += 2) {
  String first = strings.get(i);
  String second = strings.get(i + 1);
}

Upvotes: 21

Benjamin M
Benjamin M

Reputation: 24527

I created the following method using a Java8 BiConsumer:

public static <T> void tupleIterator(Iterable<T> iterable, BiConsumer<T, T> consumer) {
    Iterator<T> it = iterable.iterator();
    if(!it.hasNext()) return;
    T first = it.next();

    while(it.hasNext()) {
        T next = it.next();
        consumer.accept(first, next);
        first = next;
    }
}

use it like this:

List<String> myIterable = Arrays.asList("1", "2", "3");
tupleIterator(myIterable, (obj1, obj2) -> {
    System.out.println(obj1 + " " + obj2);
});

This will output:

1 2
2 3

Upvotes: 12

Stoneboy
Stoneboy

Reputation: 201

Now in Java 8, by using https://github.com/wapatesh/fig

You can write:

seq.forEachSlice(2, (values)->{
    // [1,2]  [3, 4]
});

Upvotes: 1

Nils von Barth
Nils von Barth

Reputation: 3429

You can avoid indexing with an Iterator; this works for any Iterable, not just a list. Just get an iterator and increment it twice per loop iteration:

List<String> strings = Arrays.asList("item 1", "item 2", "item 3", "item 4");
Iterator<String> stringsIterator = strings.iterator();
while (stringsIterator.hasNext()) {
  String first = stringsIterator.next();
  String second = stringsIterator.next();
  System.out.println("First [" + first + "] - Second [" + second + "]");
}

This assumes a list of even length, and throws NoSuchElementException on the last pass if it's odd length. You can handle this in various ways:

  • use a try-catch;
  • have a guard clause that checks that the length is even beforehand;
  • check before getting the second element.

Checking the second element:

List<String> strings = Arrays.asList("item 1", "item 2", "item 3");
Iterator<String> stringsIterator = strings.iterator();
while (stringsIterator.hasNext()) {
  String first = stringsIterator.next();
  String second = stringIterator.hasNext() ? stringIterator.next() : null;
  System.out.println("First [" + first + "] - Second [" + second + "]");
}

Iterators confuse some people, so you can also use a for-each loop with a branch and an auxiliary flip-flop variable for parity. This is much worse, since it makes the loop's logic much more complicated to simplify the iteration: rather than the action being done once per pass through the loop, in sequence and without branching, you instead have to go through it twice and branch mentally. Note that this skips the last element if it's of odd length; could add a check of isFirst afterwards if want to handle these cases too.

List<String> strings = Arrays.asList("item 1", "item 2", "item 3", "item 4");
boolean isFirst = true;
String first = null;
String second = null;
for (String string : strings) {
  if (isFirst) {
    first = string;
    isFirst = false;
  } else {
    second = string;
    isFirst = true;
    System.out.println("First [" + first + "] - Second [" + second + "]");
  }
}

Lastly, note that all these iterators and auxiliary variables have excess scope (they are only of use for the loop itself, so they pollute the local environment): they could be wrapped in blocks to limit the scope, though usually the resulting nesting is considered worse than the excess scope:

List<String> strings = Arrays.asList("item 1", "item 2", "item 3", "item 4");
{
  Iterator<String> stringsIterator = strings.iterator();
  while (stringsIterator.hasNext()) {
    String first = stringsIterator.next();
    String second = stringsIterator.next();
    System.out.println("First [" + first + "] - Second [" + second + "]");
  }
}

Upvotes: 1

Cratylus
Cratylus

Reputation: 54084

List<String> strings = Arrays.asList("item 1", "item 2", "item 3", "item 4");    
int i = 0;  
for(; i < strings.size() - 1; i+=2){  
    String first = strings.get(i);  
    String second =  strings.get(i + 1);  
    System.out.println("First [" + first + "] - Second [" + second + "]");  
}  
//For odd sized lists
if(i < strings.size()){         
    System.out.println("First [" + strings.get(i) + "]");  
}

Upvotes: 4

forty-two
forty-two

Reputation: 12817

We should of course provide a solution for the general case ;-)

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(new Integer[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
    for (Pair<Integer> p : Pair.over(list)) {
        System.out.printf("%d, %d\n", p.first, p.second);
    }
}

static class Pair<T> {
    T first;

    T second;

    public Pair(T first, T second) {
        this.first = first;
        this.second = second;
    }

    public static <T> Iterable<Pair<T>> over(Collection<T> collection) {
        return new PairWise<T>(collection);
    }

    private static class PairWise<T> implements Iterable<Pair<T>>, Iterator<Pair<T>> {

        final Iterator<T> iterator;

        PairWise(Collection<T> collection) {
            super();
            this.iterator = collection.iterator();
        }

        @Override
        public Iterator<Pair<T>> iterator() {
            return this;
        }

        @Override
        public boolean hasNext() {
            return iterator.hasNext();
        }

        @Override
        public Pair<T> next() {
            T first = null;
            T second = null;
            if (iterator.hasNext())
                first = iterator.next();
            else
                throw new NoSuchElementException();
            if (iterator.hasNext())
                second = iterator.next();
            return new Pair<T>(first, second);
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }

    }
}

Upvotes: 2

Lahniep
Lahniep

Reputation: 729

For performance, I will recommend you to compute the size of the list only one and not create a new string at each new loop.

List<String> strings = Arrays.asList("item 1", "item 2", "item 3", "item 4");
int length = strings.size();
String first, second = null;
for(int i = 0; i < length; i += 2){
    ...
}

Upvotes: 0

Superboleta
Superboleta

Reputation: 21

List<String> strings = Arrays.asList("item 1", "item 2", "item 3", "item 4");
for(int i = 0; i < strings.size(); i++){
    if(i½2 = 0){
        String first = strings.get(i);
        System.out.print("First [" + first + "] ");
    }else{
         String second = strings.get(i + 1);
         System.out.println("- Second [" + second + "]");
    }
}

Upvotes: 0

Subir Kumar Sao
Subir Kumar Sao

Reputation: 8401

for(int i = 0; i < strings.size(); i++){
    String first = strings.get(i++);
    String second = null;
    if(strings.size() > i){
        second = strings.get(i);
    }
    System.out.println("First [" + first + "] - Second [" + second + "]");
}

Upvotes: 0

Nidis
Nidis

Reputation: 145

What if you increase i by 2 in each iteration? Should do... Otherwise consider increase i inside the actual loop

Upvotes: 2

Habib
Habib

Reputation: 223287

You need to modify and increment i for the second value, modify the statement:

second = strings.get(i + 1);

to

second = strings.get(++i);

This will increment the i as well, since this seems to be the desired behaviour.

So your code would be:

List<String> strings = Arrays.asList("item 1", "item 2", "item 3", "item 4");
for(int i = 0; i < strings.size(); i++){
    String first = strings.get(i);
    String second = null;
    if(strings.size() > i + 1){
        second = strings.get(++i); //Change here
    }
    System.out.println("First [" + first + "] - Second [" + second + "]");
}

Upvotes: 7

Related Questions