2011-09-13 53 views
1

我需要从平面文件中读取记录,其中每个128字节构成一个逻辑记录。下面的这个阅读器的调用模块只是做以下事情。如何重构此IO代码?

while(iterator.hasNext()){ 
    iterator.next(); 
    //do Something 
} 

意味着将有每hasNext()调用后next()电话。

现在,这里是读者。

public class FlatFileiteratorReader implements Iterable<String> { 

    FileChannel fileChannel; 

public FlatFileiteratorReader(FileInputStream fileInputStream) { 
    fileChannel = fileInputStream.getChannel(); 
} 

private class SampleFileIterator implements Iterator<String> { 
    Charset charset = Charset.forName("ISO-8859-1"); 
    ByteBuffer byteBuffer = MappedByteBuffer.allocateDirect(128 * 100); 
    LinkedList<String> recordCollection = new LinkedList<String>(); 
    String record = null; 

    @Override 
    public boolean hasNext() { 
     if (!recordCollection.isEmpty()) { 
      record = recordCollection.poll(); 
      return true; 
     } else { 
      try { 
       int numberOfBytes = fileChannel.read(byteBuffer); 
       if (numberOfBytes > 0) { 
        byteBuffer.rewind(); 
        loadRecordsIntoCollection(charset.decode(byteBuffer) 
          .toString().substring(0, numberOfBytes), 
          numberOfBytes); 
        byteBuffer.flip(); 
        record = recordCollection.poll(); 
        return true; 
       } 
      } catch (IOException e) { 
       // Report Exception. Real exception logging code in place 
      } 
     } 
     try { 
      fileChannel.close(); 
     } catch (IOException e) { 
      // TODO Report Exception. Logging 
     } 
     return false; 

    } 

    @Override 
    public String next() { 
     return record; 
    } 

    @Override 
    public void remove() { 
     // NOT required 

    } 

    /** 
    * 
    * @param records 
    * @param length 
    */ 
    private void loadRecordsIntoCollection(String records, int length) { 
     int numberOfRecords = length/128; 
     for (int i = 0; i < numberOfRecords; i++) { 
      recordCollection.add(records.substring(i * 128, (i + 1) * 128)); 
     } 
    } 

} 

    @Override 
    public Iterator<String> iterator() { 
     return new SampleFileIterator(); 
    } 
} 

该代码读取机上80 MB 1.2秒内的数据与7200转HDD,与Sun JVM和运行Windows XP操作系统。但是我对我写的代码并不满意。有没有其他方法可以更好地编写它(特别是对字符集进行解码,并只读取已读取的字节,我的意思是charset.decode(byteBuffer) .toString().substring(0, numberOfBytes)部分,请忽略//TODO的东西)?

+1

对于http://codereview.stackexchange.com,这可能是一个更好的问题。 –

+2

我同意马特,这应该是codereview。当你发布它时,确保你包含了你不满意的内容。 –

+0

@Matt Ball我害怕我能否在那里得到更好的答案。如果给出一个选项,我希望问题在这里。 – nobody

回答

1
  1. 在这里使用直接缓冲没有特别的优势。您必须将数据通过JNI边界转换为Java-land,所以您最好使用正常的ByteBuffer。直接缓冲区用于复制数据,当你不想亲自看看它时。

  2. 使用512的倍数的ByteBuffer,例如8192,所以你不会驱动I/O系统和磁盘控制器,因为它们跨越扇区边界进行读取。在这种情况下,我会考虑使用128 * 512来同意你的记录长度。

  3. .substring(0, numberOfBytes)是不必要的。在读取和倒带之后,ByteBuffer的位置为零,并且其限制等于numberOfBytes,所以charset.decode()操作已经提供了正确数量的数据。

  4. 你假设你没有从FileChannel.read()中读取简短的内容。你不能认为,Javadoc没有什么可以支持这一假设。您需要阅读,直到缓冲区已满或您获得EOF。

说了这么多,我也想尝试周围环绕的FileInputStream的InputStreamReader一个BufferedReader,只是一次读取128个字符。你可能会惊讶哪个更快。