2010-08-31 26 views
37

Có thể hợp nhất các trình vòng lặp trong Java không? Tôi có hai vòng lặp và tôi muốn kết hợp/hợp nhất chúng để tôi có thể lặp lại mặc dù các phần tử của chúng trong một lần (trong cùng một vòng lặp) thay vì hai bước. Điều đó có thể không?Có thể hợp nhất các trình vòng lặp trong Java không?

Lưu ý rằng số phần tử trong hai danh sách có thể khác nhau do đó một vòng lặp trên cả hai danh sách không phải là giải pháp.

Iterator<User> pUsers = userService.getPrimaryUsersInGroup(group.getId()); 
Iterator<User> sUsers = userService.getSecondaryUsersInGroup(group.getId()); 

while(pUsers.hasNext()) { 
    User user = pUsers.next(); 
    ..... 
} 

while(sUsers.hasNext()) { 
    User user = sUsers.next(); 
    ..... 
} 

Trả lời

16

Bạn có thể tạo ra thực hiện riêng của bạn về giao diện Iterator đó lặp trên các vòng lặp:

public class IteratorOfIterators implements Iterator { 
    private final List<Iterator> iterators; 

    public IteratorOfIterators(List<Iterator> iterators) { 
     this.iterators = iterators; 
    } 

    public IteratorOfIterators(Iterator... iterators) { 
     this.iterators = Arrays.asList(iterators); 
    } 


    public boolean hasNext() { /* implementation */ } 

    public Object next() { /* implementation */ } 

    public void remove() { /* implementation */ } 
} 

(. Tôi đã không được thêm Generics để Iterator cho ngắn gọn) Việc thực hiện không phải là quá khó, nhưng không phải là tầm thường nhất, bạn cần theo dõi trong đó Iterator bạn hiện đang lặp lại và gọi next() bạn sẽ cần lặp lại hết mức có thể thông qua các trình vòng lặp cho đến khi bạn tìm thấy hasNext() trả về true hoặc bạn có thể nhấn vào cuối trình lặp cuối cùng.

Tôi không biết bất kỳ triển khai nào đã tồn tại cho việc này.

Cập nhật:
Tôi đã up-bình chọn Andrew Duffy's câu trả lời - không cần phải tái phát minh ra bánh xe. Tôi thực sự cần phải nhìn vào Guava sâu hơn.

Tôi đã thêm một hàm tạo khác cho một số biến đối số - gần như thoát khỏi chủ đề, vì cách lớp được xây dựng ở đây không thực sự quan tâm, chỉ là khái niệm về cách nó hoạt động.

+2

Thêm một constructor varargs và tôi cũng muốn bỏ phiếu bạn lên :-) – Christoffer

46

Guava (trước đây là Google Collections) có Iterators.concat.

+0

Có vẻ như không có cách nào để có được liên kết thứ hai hoạt động bình thường :( –

+1

liên kết thứ hai làm việc cho tôi. – Youssef

+0

Vâng nó làm việc tốt cho tôi theo cách bạn đã có nó – ColinD

3

di chuyển vòng lặp của bạn đến một phương thức và chuyển trình lặp đến phương thức.

void methodX(Iteartor x) { 
    while (x.hasNext()) { 
     .... 
    } 
} 
+1

Cảm ơn. Nhưng tôi vẫn phải gọi phương thức hai lần. –

+0

Điều này dường như với tôi là giải pháp đơn giản nhất (không có ổi) cho trường hợp cụ thể của bạn. Có, bạn phải gọi methodX hai lần nhưng bạn vẫn phải thực hiện hai cuộc gọi phương thức, một để kết hợp các trình vòng lặp và một để thực hiện những gì mà methodX thực hiện. Giải pháp của riêng bạn với cờ có vẻ phức tạp hơn và có thể nhiều mã hơn. – Alb

0

mỗi Iterator đối tượng nắm giữ vị trí bộ nhớ riêng (địa chỉ), do đó bạn có thể không chỉ đơn giản "hợp nhất" cho họ. trừ khi bạn mở rộng lớp iterator và viết triển khai của riêng bạn tại đó.

Nếu bạn đang đối phó với các cùng số đối tượng trong cả hai vòng lặp một giải pháp thay thế sẽ được xử lý hai vòng lặp trong một vòng lặp như thế này:

while (iterator1.hasNext() && iterator2.hasNext()) { 
     // code 
    } 
+0

OP không nhất thiết phải lặp lại cả hai cùng một lúc. Ông đang tìm kiếm nhiều hơn để lặp lại một, sau đó khác. Giải pháp thứ hai của bạn sẽ thất bại khi trình vòng lặp đầu tiên đã được xử lý. – smac89

13

tôi đã không viết mã Java trong một trong khi đó, và điều này khiến tôi tò mò muốn biết liệu tôi có còn nhận được nó hay không.

Trước tiên hãy thử:

import java.util.Iterator; 
import java.util.Arrays; /* For sample code */ 

public class IteratorIterator<T> implements Iterator<T> { 
    private final Iterator<T> is[]; 
    private int current; 

    public IteratorIterator(Iterator<T>... iterators) 
    { 
      is = iterators; 
      current = 0; 
    } 

    public boolean hasNext() { 
      while (current < is.length && !is[current].hasNext()) 
        current++; 

      return current < is.length; 
    } 

    public T next() { 
      while (current < is.length && !is[current].hasNext()) 
        current++; 

      return is[current].next(); 
    } 

    public void remove() { /* not implemented */ } 

    /* Sample use */ 
    public static void main(String... args) 
    { 
      Iterator<Integer> a = Arrays.asList(1,2,3,4).iterator(); 
      Iterator<Integer> b = Arrays.asList(10,11,12).iterator(); 
      Iterator<Integer> c = Arrays.asList(99, 98, 97).iterator(); 

      Iterator<Integer> ii = new IteratorIterator<Integer>(a,b,c); 

      while (ii.hasNext()) 
        System.out.println(ii.next()); 
    } 
} 

Bạn thể dĩ nhiên sử dụng lớp Collection hơn chứ không phải là một mảng + index tinh khiết truy cập, nhưng điều này thực sự cảm thấy một chút sạch hơn thay thế. Hay tôi chỉ thiên vị bằng văn bản chủ yếu là C những ngày này?

Dù sao, có bạn đi. Câu trả lời cho câu hỏi của bạn là "có, có thể".

3

trình lặp đi kèm TỪ bộ sưu tập hoặc tập hợp.
lý do tại sao không sử dụng phương pháp đã có sẵn
Collection.addAll(Collection c);
và sau đó tạo trình lặp của bạn từ đối tượng cuối cùng.
theo cách này, trình lặp của bạn sẽ lặp lại tất cả nội dung của cả hai bộ sưu tập.

+2

Điều này có một số nhược điểm, đặc biệt nếu bạn muốn sử dụng các trình vòng lặp lười hoặc nếu các bộ sưu tập rất lớn. – Fabian

+0

Vì vậy, nhiều lý do không sử dụng điều này. Iterator không phải đến từ một bộ sưu tập hoặc thiết lập. Ngay cả khi họ đã làm, bạn không nên sao chép tất cả những tài liệu tham khảo trừ khi bạn biết bạn sẽ đi qua tất cả. – Navin

+0

Nó không nhất thiết phải đến từ một bộ sưu tập, quay lại những điều cơ bản, một trình lặp là bất cứ thứ gì có thể lặp lại và không cần phải là một bộ sưu tập, chúng gọi nó là trừu tượng. – zakmck

16

Ngoài ra, Apache Commons Collection có một số lớp để thao tác Bộ lặp, như IteratorChain, bao bọc một số Iterator.

+2

Tôi nghĩ rằng phương pháp này ít tốn kém tài nguyên hơn, vì nó không chuyển đổi tất cả các trình vòng lặp thành một ArrayList. – guerda

1

tôi sẽ cấu trúc lại thiết kế ban đầu từ:

Iterator<User> pUsers = userService.getPrimaryUsersInGroup(group.getId()); 
Iterator<User> sUsers = userService.getSecondaryUsersInGroup(group.getId()); 

Để cái gì đó như:

Iterator<User> users = userService.getUsersInGroup(group.getId(), User.PRIMARY, User.SECONDARY, ...); 
1

Bạn có thể sử dụng my version của một iterator mở rộng. Nó sử dụng một hàng đợi đúp kết thúc vòng lặp mà đối với tôi có ý nghĩa:

import java.util.Deque; 
import java.util.Iterator; 
import java.util.concurrent.ConcurrentLinkedDeque; 

public class ExtendableIterator<T> implements Iterator<T> { 

    public Deque<Iterator<T>> its = new ConcurrentLinkedDeque<Iterator<T>>(); 

    public ExtendableIterator() { 

    } 

    public ExtendableIterator(Iterator<T> it) { 
     this(); 
     this.extend(it); 
    } 

    @Override 
    public boolean hasNext() { 
     // this is true since we never hold empty iterators 
     return !its.isEmpty() && its.peekLast().hasNext(); 
    } 

    @Override 
    public T next() { 
     T next = its.peekFirst().next(); 
     if (!its.peekFirst().hasNext()) { 
      its.removeFirst(); 
     } 
     return next; 
    } 

    public void extend(Iterator<T> it) { 
     if (it.hasNext()) { 
      its.addLast(it); 
     } 
    } 
} 
1

Các Iterator Merged:

import static java.util.Arrays.asList; 

import java.util.Iterator; 
import java.util.LinkedList; 
import java.util.List; 
import java.util.NoSuchElementException; 


public class ConcatIterator<T> implements Iterator<T> { 

    private final List<Iterable<T>> iterables; 
    private Iterator<T> current; 

    @SafeVarargs 
    public ConcatIterator(final Iterable<T>... iterables) { 
     this.iterables = new LinkedList<>(asList(iterables)); 
    } 

    @Override 
    public boolean hasNext() { 
     checkNext(); 
     return current != null && current.hasNext(); 
    } 

    @Override 
    public T next() { 
     checkNext(); 
     if (current == null || !current.hasNext()) throw new NoSuchElementException(); 
     return current.next(); 
    } 

    @Override 
    public void remove() { 
     if (current == null) throw new IllegalStateException(); 
     current.remove(); 
    } 

    private void checkNext() { 
     while ((current == null || !current.hasNext()) && !iterables.isEmpty()) { 
      current = iterables.remove(0).iterator(); 
     } 
    } 

} 

Phương pháp concat để tạo ra một Iterable:

@SafeVarargs 
public static <T> Iterable<T> concat(final Iterable<T>... iterables) { 
    return() -> new ConcatIterator<>(iterables); 
} 

JUnit đơn giản kiểm tra:

@Test 
public void testConcat() throws Exception { 
    final Iterable<Integer> it1 = asList(1, 2, 3); 
    final Iterable<Integer> it2 = asList(4, 5); 
    int j = 1; 
    for (final int i : concat(it1, it2)) { 
     assertEquals(j, i); 
     j++; 
    } 
} 
1

Bạn có thể thử ConcatIterator từ Cactoos:

Iterator<String> names = new ConcatIterator<>(
    Arrays.asList("Sarah", "Mary").iterator(), 
    Arrays.asList("Jeff", "Johnny").iterator(), 
); 

Ngoài ra kiểm tra ConcatIterable, mà concatenates Iterable s.

0

lớp công khai IteratorJoin triển khai Iterator { Iterator cuối cùng riêng tư trước, tiếp theo;

public IteratorJoin(Iterator<T> first, Iterator<T> next) { 
    this.first = first; 
    this.next = next; 
} 

@Override 
public boolean hasNext() { 
    return first.hasNext() || next.hasNext(); 
} 

@Override 
public T next() { 
    if (first.hasNext()) 
     return first.next(); 
    return next.next(); 
} 

}

Các vấn đề liên quan