Java 中可以合并迭代器吗?

发布于 2024-09-16 16:46:06 字数 446 浏览 8 评论 0 原文

Java 中可以合并迭代器吗?我有两个迭代器,我想组合/合并它们,以便我可以一次性(在同一循环中)迭代它们的元素,而不是两步。这可能吗?

请注意,两个列表中的元素数量可能不同,因此对两个列表进行一次循环并不是解决方案。

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

Is it possible to merge iterators in Java? I have two iterators and I want to combine/merge them so that I could iterate though their elements in one go (in same loop) rather than two steps. Is that possible?

Note that the number of elements in the two lists can be different therefore one loop over both lists is not the solution.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

小傻瓜 2024-09-23 16:46:06

Guava(以前称为 Google Collections)拥有 Iterators.concat

Guava (formerly Google Collections) has Iterators.concat.

风流物 2024-09-23 16:46:06

此外,Apache Commons Collection 有几个用于操作迭代器的类,例如 IteratorChain,包装一个数字迭代器。

Also the Apache Commons Collection have several classes for manipulating Iterators, like the IteratorChain, that wraps a number of Iterators.

束缚m 2024-09-23 16:46:06

您可以创建自己的 Iterator 接口实现,该接口对迭代器进行迭代:

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 */ }
}

(为了简洁起见,我没有向迭代器添加泛型。) 实现并不太难,但这并不是最简单的,您需要跟踪当前正在迭代的 Iterator ,并调用您需要迭代的 next()您可以遍历迭代器,直到找到返回 truehasNext(),否则您可能会到达最后一个迭代器的末尾。

我不知道已有任何实现。

更新:
我对 Andrew Duffy 的投了赞成票答案——不需要重新发明轮子。我真的需要更深入地研究番石榴。

我为可变数量的参数添加了另一个构造函数 - 几乎偏离了主题,因为这里的类如何构造并不是真正令人感兴趣,只是它如何工作的概念。

You could create your own implementation of the Iterator interface which iterates over the iterators:

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 */ }
}

(I've not added generics to the Iterator for brevity.) The implementation is not too hard, but isn't the most trivial, you need to keep track of which Iterator you are currently iterating over, and calling next() you'll need to iterate as far as you can through the iterators until you find a hasNext() that returns true, or you may hit the end of the last iterator.

I'm not aware of any implementation that already exists for this.

Update:
I've up-voted Andrew Duffy's answer - no need to re-invent the wheel. I really need to look into Guava in more depth.

I've added another constructor for a variable number of arguments - almost getting off topic, as how the class is constructed here isn't really of interest, just the concept of how it works.

ゝ偶尔ゞ 2024-09-23 16:46:06

我已经有一段时间没有编写 Java 代码了,这让我很好奇我是否仍然“掌握了它”。

第一次尝试:

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

您当然可以使用更多的 Collection 类,而不是纯数组 + 索引计数器,但这实际上感觉比替代方案更干净。或者我现在只是对主要使用 C 语言有偏见?

无论如何,就这样吧。你的问题的答案是“是的,可能”。

I haven't written Java code in a while, and this got me curious to whether I've still "got it".

First try:

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

You could of course use more Collection classes rather than a pure array + index counter, but this actually feels a bit cleaner than the alternative. Or am I just biased from writing mostly C these days?

Anyway, there you go. The answer to you question is "yes, probably".

北城孤痞 2024-09-23 16:46:06

从 Java 8 及更高版本开始,可以使用 流 API。这还允许将迭代器与其他类型的流串联。

Streams.concat(
   StreamSupport.stream(<iter1>, false), 
   StreamSupport.stream(<iter2>, false));

Starting with Java 8 and later this can be done without external dependencies using Stream API. This also allows concatenation of iterator with other types of streams.

Streams.concat(
   StreamSupport.stream(<iter1>, false), 
   StreamSupport.stream(<iter2>, false));
远昼 2024-09-23 16:46:06
public class IteratorJoin<T> implements Iterator<T> {
    private final Iterator<T> first, next;

    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();
    }
}
public class IteratorJoin<T> implements Iterator<T> {
    private final Iterator<T> first, next;

    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();
    }
}
沒落の蓅哖 2024-09-23 16:46:06

将循环移至方法并将迭代器传递给方法。

void methodX(Iterator x) {
    while (x.hasNext()) {
        ....
    }
}

move your loop to a method and pass the iterator to method.

void methodX(Iterator x) {
    while (x.hasNext()) {
        ....
    }
}
找回味觉 2024-09-23 16:46:06

迭代器来自集合或集合。
为什么不使用已有的方法
Collection.addAll(Collection c);
然后从最后一个对象创建迭代器。
这样,您的迭代器将迭代两个集合的所有内容。

an iterator comes FROM a collection or a set.
why not use the method already available
Collection.addAll(Collection c);
and then create your iterator from the last object.
this way, your iterator will iterate all the contents of both collection.

╭ゆ眷念 2024-09-23 16:46:06

您可以使用我的版本的可扩展迭代器。它使用迭代器的双端队列,这对我来说是有意义的:

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

You can use my version of an extendable iterator. It uses a double-ended queue of iterators which to me makes sense:

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);
        }
    }
}
晨曦÷微暖 2024-09-23 16:46:06

合并迭代器:

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

}

创建 Iterableconcat 方法:

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

简单的 JUnit 测试:

@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++;
    }
}

The Merged Iterator:

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

}

The concat method to create an Iterable:

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

Simple JUnit test:

@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++;
    }
}
紅太極 2024-09-23 16:46:06

我会将原始设计从: 重构

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

为:

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

I would refactor the original design from:

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

To something like:

Iterator<User> users = userService.getUsersInGroup(group.getId(), User.PRIMARY, User.SECONDARY, ...);
胡大本事 2024-09-23 16:46:06

您可以尝试 ConcatIterator 来自 Cactoos

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

另请检查 ConcatIterable,连接 Iterable

You can try ConcatIterator from Cactoos:

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

Also check ConcatIterable, which concatenates Iterables.

じ违心 2024-09-23 16:46:06

Apache Commons Collections 中有 public static 迭代器 org.apache.commons.collections4.IteratorUtils.chainedIterator(Collection> iterators) 上面写着

获取一个迭代器,该迭代器逐个迭代迭代器集合。

这应该是你想要的。

import java.util.Arrays;
import java.util.Iterator;

import org.apache.commons.collections4.IteratorUtils;
//also works: import org.apache.commons.collections.IteratorUtils;

class Scratch {
    public static void main( String[] args ) {
        final Iterator<String> combinedIterator = IteratorUtils.chainedIterator(
                Arrays.asList( "a", "b", "c" ).iterator(),
                Arrays.asList( "1", "2", "3" ).iterator()
            );
        while( combinedIterator.hasNext() ){
            System.out.println( combinedIterator.next() );
        }
        // "abc123" will have been printed out
    }
}

In the Apache Commons Collections there is public static <E> Iterator<E> org.apache.commons.collections4.IteratorUtils.chainedIterator(Collection<Iterator<? extends E>> iterators) that says

Gets an iterator that iterates through a collections of Iterators one after another.

which should be what you want.

import java.util.Arrays;
import java.util.Iterator;

import org.apache.commons.collections4.IteratorUtils;
//also works: import org.apache.commons.collections.IteratorUtils;

class Scratch {
    public static void main( String[] args ) {
        final Iterator<String> combinedIterator = IteratorUtils.chainedIterator(
                Arrays.asList( "a", "b", "c" ).iterator(),
                Arrays.asList( "1", "2", "3" ).iterator()
            );
        while( combinedIterator.hasNext() ){
            System.out.println( combinedIterator.next() );
        }
        // "abc123" will have been printed out
    }
}
最丧也最甜 2024-09-23 16:46:06

每个 Iterator 对象都拥有自己的内存位置(地址),因此您不能简单地“合并”它们。除非您扩展迭代器类并在那里编写自己的实现。

如果您在两个迭代器中处理相同数量的对象,另一种解决方案是在一个循环中处理两个迭代器,如下所示:

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

every Iterator object holds own memory location (adress), so you can't simply "merge" them. except if you extend iterator class and write your own implementation there.

If you are dealing with the same number of objects in both iterators an alternative solution would be to process two iterators in one loop like this :

   while (iterator1.hasNext() && iterator2.hasNext()) {
      // code
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文