【问题标题】:Shortest way to get an Iterator over a range of Integers in Java在Java中获取一系列整数的迭代器的最短方法
【发布时间】:2008-12-16 11:16:22
【问题描述】:

在 Java 中获取一系列整数的迭代器的最短方法是什么?换句话说,实现以下内容:

/** 
* Returns an Iterator over the integers from first to first+count.
*/
Iterator<Integer> iterator(Integer first, Integer count);

类似

(first..first+count).iterator()

【问题讨论】:

  • 有什么理由不能以经典方式“加 1”吗?只是感兴趣
  • 这是一个框架,需要我写一个迭代器
  • 清晰胜过简洁,并且有许多 Range 类可用,包括迭代。

标签: java iterator


【解决方案1】:

此实现没有内存占用。

/**
 * @param begin inclusive
 * @param end exclusive
 * @return list of integers from begin to end
 */
public static List<Integer> range(final int begin, final int end) {
    return new AbstractList<Integer>() {
            @Override
            public Integer get(int index) {
                return begin + index;
            }

            @Override
            public int size() {
                return end - begin;
            }
        };
}

编辑:

在 Java 8 及更高版本中,您可以简单地说:

IntStream.range(begin, end).iterator()          // returns PrimitiveIterator.OfInt

或者如果您需要盒装版本:

IntStream.range(begin, end).boxed().iterator()  // returns Iterator<Integer>

【讨论】:

  • 虽然列表本身很短,但它返回一个 List 而不是提供更多接口方法的 Iterable / Iterator。这些没有在这里实现,如果列表实例在“不可预见的”上下文中使用,这反过来可能会导致意想不到的结果。所以你可以说这个实现打破了 List 接口契约。此外,AbstractList 中迭代器的实现并不像它可能的那么简单,并且使用了比必要更多的实例变量,因此内存占用量很小。因此,我宁愿直接实现 Iterable / Iterator。
  • @DanielBimschas 我认为这不是真的。此类确实遵守 unmodifiable 列表的约定,如 JavaDoc 中为java.util.Collection 定义的那样。特别是,如果有人调用它的“破坏性”方法之一,如add()remove(),它会抛出UnsupportedOperationException。更多详细信息,请参见java.util.AbstractList 的 JavaDoc。
  • @Saintali fair point w.r.t.我的“违反列表接口合同”评论。请忽略这部分。恕我直言,我的其他 cmets 仍然有效。
  • get() 方法应该检查其参数是否在有效范围内,如果不是(例如负数或大于大小)则抛出 IndexOutofBoundsException
  • 越来越挑剔。列表确实有内存占用(beginend),但它是不变的。
【解决方案2】:

未经测试。将其映射到“min, count”作为练习留给读者。

public class IntRangeIterator implements Iterator<Integer> {
  private int nextValue;
  private final int max;
  public IntRangeIterator(int min, int max) {
    if (min > max) {
      throw new IllegalArgumentException("min must be <= max");
    }
    this.nextValue = min;
    this.max = max;
  }

  public boolean hasNext() {
    return nextValue <= max;
  }

  public Integer next() {
    if (!hasNext()) {
      throw new NoSuchElementException();
    }
    return Integer.valueOf(nextValue++);
  }

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

【讨论】:

  • 这看起来和我刚刚为自己写的几乎一模一样 :)
【解决方案3】:

如果您真的想要最少的代码,那么 Bombe 的答案就可以了。但是,它无缘无故地吸收内存。如果你想自己实现它,它会是这样的:

import java.util.*;

public class IntegerRange implements Iterator<Integer>
{
    private final int start;
    private final int count;

    private int position = -1;

    public IntegerRange(int start, int count)
    {
        this.start = start;
        this.count = count;
    }

    public boolean hasNext()
    {
        return position+1 < count;
    }

    public Integer next()
    {
        if (position+1 >= count)
        {
            throw new NoSuchElementException();
        }
        position++;
        return start + position;
    }

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

【讨论】:

    【解决方案4】:

    使用 guava 框架的示例。请注意,这不会实现集合(尽管您必须阅读 ContiguousSet 实现来验证这一点)。

    import com.google.common.collect.ContiguousSet;
    import com.google.common.collect.DiscreteDomain;
    import com.google.common.collect.DiscreteDomains;
    
    class RangeIterator { 
    
        public Iterator<Integer> range(int start, int length) {
            assert length > 0;
            Range<Integer> dim_range = Ranges.closedOpen(start, start + length);
            DiscreteDomain<Integer> ints = DiscreteDomains.integers();
            ContiguousSet<Integer> dim = dim_range.asSet(ints);
            return dim.iterator();
        }
    }
    

    【讨论】:

    • 这个最短的版本:Iterator&lt;Integer&gt; range = Ranges.closedOpen(start, end).asSet(DiscreteDomains.integers()).iterator();
    • @Miguel Range#asSet(DiscreteDomain) 方法 is deprecated and has been removed in Guava 16
    • @Synoli Iterator&lt;Integer&gt; range = ContiguousSet.create(Ranges.closedOpen(start, end), DiscreteDomains.integers()).iterator();
    【解决方案5】:

    直接完成你的作业:

    List<Integer> ints = new ArrayList<Integer>();
    for (int i = 0; i < count; i++) {
        ints.add(first + i);
    }
    

    【讨论】:

    • 如果您认真对待“最短”,那是正确的,除了您缺少“return ints.iterator();” ;-)
    • 还有,内存不足。
    • 我觉得把这个叫做作业有点居高临下。这是一个非常常见的用例,如下所示,还有比这更好的实现(尽管在撰写本文时并非所有实现都是可能的)。
    【解决方案6】:

    在 java 8 中使用流 API 的示例:

    int first = 0;
    int count = 10;
    Iterator<Integer> it = IntStream.range(first, first + count).iterator();
    while (it.hasNext()) {
        System.out.println(it.next());
    }
    

    没有迭代器,它可能是:

    int first = 0;
    int count = 10;
    IntStream.range(first, first + count).forEach(i -> System.out.println(i));
    

    【讨论】:

      【解决方案7】:

      通常认为传递Collection 和朋友而不是Iterator(见this FAQ entry)是一种很好的风格,所以我推荐类似

      public final class IntegerRange implements Set<Integer> {
              final LinkedHashSet<Integer> backingList;
              public IntegerRange(final int start, final int count) {
                      backingList = new LinkedHashSet(count, 1.0f);
                      for (int i=0; i < count; i++) {
                              backingList.set(i, start + i);
                      }       
              }       
              /** Insert a bunch of delegation methods here */
      }
      

      然后当您需要将Iterator 传递给您正在使用的任何框架时,只需使用.iterator()

      更新:显然,这段代码并不懒惰。如果您负担不起存储(可能)2^32-1 Integers 的额外内存开销,您应该使用不同的解决方案。此外,关于类型的任何内容都不能保证范围会被排序(即使它是基于实现的)。如果您需要保证排序,您可以考虑实现SortedSet 并用TreeSet 支持它,但构建范围需要更长的时间。老实说,如果您非常关心正确获取详细信息,那么寻找图书馆可能是值得的。例如,Tapestry 有一个internal version

      【讨论】:

      • 为什么要在后备集中实现它?只要您不需要支持 add() 和 remove(),就可以很容易地实现这一点,而无需存储所有整数(这可能会占用大量内存)。我还要补充一点,列表会更合适(因为肯定有一些顺序)
      • RE: List -> cretzel 没有指定 ListIterator,并且 Iterator 不做任何排序保证,所以我选择了最接近的映射(Collection),但回想起来,我会使用 Set。 RE: 懒惰 -> 这似乎需要更多的工作,而且 cretzel 没有提到不需要 remove() 支持。
      • 您所指的常见问题解答条目告诉我们,我们不应该在对象集合周围传递迭代器。但是如果你不是对集合感兴趣,而是对迭代过程感兴趣,传递一个 Iterator 是完全可以的。
      • 我认为 Collection vs. Iterator 真的取决于。 Google 的人似乎更喜欢将迭代器用于他们的应用程序:请参阅“为什么如此强调迭代器和迭代器?”这里:code.google.com/p/google-collections/wiki/Faq
      猜你喜欢
      • 2016-08-29
      • 2017-06-12
      • 1970-01-01
      • 1970-01-01
      • 2021-09-12
      • 2011-01-09
      • 2014-03-23
      • 1970-01-01
      相关资源
      最近更新 更多