简体   繁体   中英

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]

i增加2:

for(int i = 0; i < strings.size(); i += 2) {

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 + "]");
}

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
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) + "]");  
}

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

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();
        }

    }
}
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 + "]");
}
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 + "]");
    }
}

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){
    ...
}

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 + "]");
  }
}

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

You can write:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM