【问题标题】:How can I get an int[] out of an Iterator?如何从迭代器中获取 int[]?
【发布时间】:2009-07-22 17:55:45
【问题描述】:

我有一个Iterator<Integer>...实际上它是一个class Thing,它接受一个Visitor<SomeObject>,并为它包含的SomeObjects 的一个子集调用visit(),我必须实现@ 987654326@ 所以它做了这样的事情:

// somehow get all the Id's from each of the SomeObject that Thing lets me visit
public int[] myIdExtractor(Thing thing)
{
    SomeCollection c = new SomeCollection();
    thing.visitObjects(new Visitor<SomeObject>()
         {
              public void visit(SomeObject obj) { c.add(obj.getId()); }
         }
    );
    return convertToPrimitiveArray(c);
}

我需要提取包含结果的int[],但我不确定SomeCollectionconvertToPrimitiveArray 使用什么。结果的数量提前未知,并且会很大(10K-500K)。有什么比使用ArrayList&lt;Integer&gt; 代替SomeCollection 更好的选择吗?这个:

public int[] convertToPrimitiveArray(List<Integer> ints)
{
    int N = ints.size();
    int[] array = new int[N];
    int j = 0;
    for (Integer i : ints)
    {
        array[j++] = i;
    }
    return array;
}

效率和内存使用是一些问题。

【问题讨论】:

    标签: java collections


    【解决方案1】:

    想出一个在数组中收集ints 的类并不难(即使你没有使用为你做这件事的库)。

    public class IntBuffer {
        private int[] values = new int[10];
        private int size = 0;
        public void add(int value) {
            if (!(size < values.length)) {
                values = java.util.Arrays.copyOf(values, values.length*2);
            }
            values[size++] = value;
        }
        public int[] toArray() {
            return java.util.Arrays.copyOf(values, size);
        }
    }
    

    (免责声明:这是stackoverflow,我什至没有尝试编译此代码。)

    作为替代方案,您可以使用DataOutputStreamints 存储在ByteArrayOutputStream 中。

    final ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
    final DataOutputStream out = new DataOutputStream(byteOut);
    ...
        out.writeInt(value);
    ...
    out.flush();
    final byte[] bytes = byteOut.toByteArray();
    final int[] ints = new int[bytes.length/4];
    final ByteArrayInputStream byteIn = new ByteArrayInputStream(bytes);
    final DataInputStream in = new DataOutputStream(byteIn);
    for (int ct=0; ct<ints.length; ++ct) {
        ints[ct] = in.readInt();
    }
    

    (免责声明:这是stackoverflow,我什至没有尝试编译此代码。)

    【讨论】:

    • 你打错了,顺便说一句:“values[size++] = value”,而不是“-value”
    【解决方案2】:

    您可以查看 pjc 之类的内容来处理此问题。那是为原语制作的集合框架。

    【讨论】:

      【解决方案3】:

      为了进行基准测试,我使用 LFSR 生成器编写了一个测试程序,以防止编译器优化测试数组。无法下载 pjc,但我认为时间应该类似于 Tom 的 IntBuffer 课程,这是迄今为止的赢家。 ByteArrayOutputStream 方法与我原来的ArrayList&lt;Integer&gt; 方法的速度差不多。我在 3GHz Pentium 4 上运行 J2SE 6u13,大约 220 值,在 JIT 运行之后,IntBuffer 方法大约需要 40 毫秒(每个项目只需 40 纳秒!)使用“健忘”集合的参考实现,该集合仅将最后一个参数存储到visit()(因此编译器不会对其进行优化)。其他两种方法大约需要 300 毫秒,大约慢 8 倍。

      编辑:我怀疑 Stream 方法的问题是我必须捕获的潜在异常,不确定。

      (对于参数运行 PrimitiveArrayTest 1 2)

      package com.example.test.collections;
      
      import java.io.ByteArrayInputStream;
      import java.io.ByteArrayOutputStream;
      import java.io.DataInputStream;
      import java.io.DataOutputStream;
      import java.io.IOException;
      import java.util.ArrayList;
      import java.util.Collections;
      import java.util.List;
      
      public class PrimitiveArrayTest {
          interface SomeObject {
              public int getX();
          }
          interface Visitor {
              public void visit(SomeObject obj);
          }
      
          public static class PlainObject implements SomeObject
          {
              private int x;
              public int getX() { return this.x; }
              public void setX(int x) { this.x = x; }  
          }
      
          public static class Thing
          {
              /* here's a LFSR        
               * see http://en.wikipedia.org/wiki/Linear_feedback_shift_register
               * and http://www.ece.cmu.edu/~koopman/lfsr/index.html
               */
              private int state;
              final static private int MASK = 0x80004;
              private void _next()
              {
                  this.state = (this.state >>> 1) 
                  ^ (-(this.state & 1) & MASK);
              }
              public Thing(int state) { this.state = state; }     
              public void setState(int state) { this.state = state; }
      
              public void inviteVisitor(Visitor v, int terminationPoint)
              {
                  PlainObject obj = new PlainObject();
                  while (this.state != terminationPoint)
                  {
                      obj.setX(this.state);
                      v.visit(obj);
                      _next();
                  }
              }
          }
      
          static public abstract class Collector implements Visitor
          {
              abstract public void initCollection();
              abstract public int[] getCollection();
              public int[] extractX(Thing thing, int startState, int endState)
              {
                  initCollection();
                  thing.setState(startState);
                  thing.inviteVisitor(this, endState);
                  return getCollection();
              }
              public void doit(Thing thing, int startState, int endState)
              {
                  System.out.printf("%s.doit(thing,%d,%d):\n",
                          getClass().getName(),
                          startState,
                          endState);
                  long l1 = System.nanoTime();
                  int[] result = extractX(thing,startState,endState);
                  long l2 = System.nanoTime();
                  StringBuilder sb = new StringBuilder();
                  sb.append(String.format("%d values calculated in %.4f msec ",
                          result.length, (l2-l1)*1e-6));
                  int N = 3;
                  if (result.length <= 2*N)
                  {
                      sb.append("[");
                      for (int i = 0; i < result.length; ++i)
                      {
                          if (i > 0)
                              sb.append(", ");
                          sb.append(result[i]);
                      }
                      sb.append("]");
                  }
                  else
                  {
                      int sz = result.length;
                      sb.append(String.format("[%d, %d, %d... %d, %d, %d]",
                              result[0], result[1], result[2], 
                              result[sz-3], result[sz-2], result[sz-1]));
                  }
                  System.out.println(sb.toString());          
              }
          }
      
          static public class Collector0 extends Collector
          {
              int lastint = 0;
              @Override public int[] getCollection() { return new int[]{lastint}; }
              @Override public void initCollection() {}
              @Override public void visit(SomeObject obj) {lastint = obj.getX(); }
          }
          static public class Collector1 extends Collector
          {
              final private List<Integer> ints = new ArrayList<Integer>();
      
              @Override public int[] getCollection() {
                  int N = this.ints.size();
                  int[] array = new int[N];
                  int j = 0;
                  for (Integer i : this.ints)
                  {
                      array[j++] = i;
                  }
                  return array;           
              }
              @Override public void initCollection() { }
              @Override public void visit(SomeObject obj) { ints.add(obj.getX()); }
          }
      
          static public class Collector2 extends Collector
          {
              /*
               * adapted from http://stackoverflow.com/questions/1167060
               * by Tom Hawtin
               */
              private int[] values;
              private int size = 0;
              @Override public void visit(SomeObject obj) { add(obj.getX()); }
              @Override public void initCollection() { values = new int[32]; }
              private void add(int value) {
                  if (!(this.size < this.values.length)) {
                      this.values = java.util.Arrays.copyOf(
                              this.values, this.values.length*2);
                  }
                  this.values[this.size++] = value;
              }
              @Override public int[] getCollection() {
                  return java.util.Arrays.copyOf(this.values, this.size);
              }       
          }
      
          static public class Collector3 extends Collector
          {
              /*
               * adapted from http://stackoverflow.com/questions/1167060
               * by Tom Hawtin
               */
              final ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
              final DataOutputStream out = new DataOutputStream(this.byteOut);
              int size = 0;
              @Override public int[] getCollection()  {
                  try
                  {
                      this.out.flush();
                      final int[] ints = new int[this.size];
                      final ByteArrayInputStream byteIn 
                          = new ByteArrayInputStream(this.byteOut.toByteArray());
                      final DataInputStream in = new DataInputStream(byteIn);
      
                      for (int ct=0; ct<ints.length; ++ct) {
                          ints[ct] = in.readInt();
                      }
                      return ints;
                  }
                  catch (IOException e) { /* gulp */ }
      
                  return new int[0]; // failure!?!??!
              }
      
              @Override public void initCollection() { }
              @Override public void visit(SomeObject obj) {
                  try {
                      this.out.writeInt(obj.getX());
                      ++this.size;
                  }
                  catch (IOException e) { /* gulp */ }
              }       
          }
          public static void main(String args[])
          {
              int startState = Integer.parseInt(args[0]);
              int endState = Integer.parseInt(args[1]);
              Thing thing = new Thing(0);
              // let JIT do its thing
              for (int i = 0; i < 20; ++i)
              {
                  Collector[] collectors = {new Collector0(), new Collector1(), new Collector2(), new Collector3()};
                  for (Collector c : collectors)
                  {
                      c.doit(thing, startState, endState);
                  }
                  System.out.println();
              }
          }
      }
      

      【讨论】:

        【解决方案4】:

        您可以使用 List.toArray(T[] a) 代替 convertToPrimitiveArray:

        ArrayList<int> al = new ArrayList<int>();
        // populate al
        int[] values = new int[al.size()];
        al.toArray(values);
        

        考虑到您事先不知道结果集的大小,LinkedList 可能比 ArrayList 稍好一些。

        如果性能确实是个问题,您最好自己手动管理一个 int[],并在每次增长时使用 System.arraycopy();任何 Collection 所需的从 int 到 Integer 的装箱/拆箱可能会受到伤害。

        当然,与任何与性能相关的问题一样,在花太多时间进行优化之前,先进行测试并确保它真的很重要。

        【讨论】:

        • ArrayList&lt;int&gt; 是非法的。因此您不能将int[] 传递给toArray(T[])
        • 您不能拥有原始整数的 ArrayList。它必须是整数。
        • 我几乎提出了同样的建议。但后来我想起了mmyers的观点。
        猜你喜欢
        • 2014-05-20
        • 2020-01-15
        • 1970-01-01
        • 2017-10-30
        • 1970-01-01
        • 1970-01-01
        • 2022-11-20
        • 2011-07-03
        • 1970-01-01
        相关资源
        最近更新 更多