2010-11-22 41 views
20

Tôi gặp sự cố, tôi cần so sánh hai luồng đầu vào nhanh chóng.Cách nhanh chóng để so sánh inputstreams

Hôm nay tôi có một chức năng như thế này:

private boolean isEqual(InputStream i1, InputStream i2) throws IOException { 

    try { 
     // do the compare 
     while (true) { 
      int fr = i1.read(); 
      int tr = i2.read(); 

      if (fr != tr) 
       return false; 

      if (fr == -1) 
       return true; 
     } 

    } finally { 
     if (i1 != null) 
      i1.close(); 
     if (i2 != null) 
      i2.close(); 
    } 
} 

Nhưng nó thực sự chậm. Tôi muốn sử dụng bộ đệm đã đọc nhưng chưa đưa ra cách làm tốt.

Một số công cụ bổ sung mà làm cho nó khó khăn hơn:

  • Tôi không muốn đọc một trong những đầu vào suối vào bộ nhớ (toàn bộ một)
  • Tôi không muốn sử dụng một bên thứ ba thư viện

Tôi cần một giải pháp thực tế - mã! :)

+0

tôi đừng nghĩ rằng bạn có thể so sánh bất cứ điều gì mà không cần đọc nó vào bộ nhớ. Bạn có thực sự có nghĩa là đọc * toàn bộ inputstream * vào bộ nhớ, có nghĩa là đọc một số byte cố định là ok? – Patrick

+0

Tôi có nghĩa là đọc toàn bộ đầu vào vào bộ nhớ không phải là một tùy chọn – dacwe

Trả lời

15

Something như thế này có thể làm:

private static boolean isEqual(InputStream i1, InputStream i2) 
     throws IOException { 

    ReadableByteChannel ch1 = Channels.newChannel(i1); 
    ReadableByteChannel ch2 = Channels.newChannel(i2); 

    ByteBuffer buf1 = ByteBuffer.allocateDirect(1024); 
    ByteBuffer buf2 = ByteBuffer.allocateDirect(1024); 

    try { 
     while (true) { 

      int n1 = ch1.read(buf1); 
      int n2 = ch2.read(buf2); 

      if (n1 == -1 || n2 == -1) return n1 == n2; 

      buf1.flip(); 
      buf2.flip(); 

      for (int i = 0; i < Math.min(n1, n2); i++) 
       if (buf1.get() != buf2.get()) 
        return false; 

      buf1.compact(); 
      buf2.compact(); 
     } 

    } finally { 
     if (i1 != null) i1.close(); 
     if (i2 != null) i2.close(); 
    } 
} 
+0

+1 Tôi thích nó. NIO ftw :) – Patrick

+0

Bang trên mục tiêu! – dacwe

+0

@dacwe, tôi có thể bảo đảm nó chậm hơn so với giải pháp mà tôi đã cung cấp. ;) –

8

Sử dụng tính năng đọc đệm chỉ là vấn đề gói InputStream với BufferedInputStreams. Tuy nhiên, bạn có khả năng nhận được hiệu suất tốt nhất khi đọc các khối lớn tại một thời điểm.

private boolean isEqual(InputStream i1, InputStream i2) throws IOException { 
    byte[] buf1 = new byte[64 *1024]; 
    byte[] buf2 = new byte[64 *1024]; 
    try { 
     DataInputStream d2 = new DataInputStream(i2); 
     int len; 
     while ((len = i1.read(buf1)) > 0) { 
      d2.readFully(buf2,0,len); 
      for(int i=0;i<len;i++) 
       if(buf1[i] != buf2[i]) return false; 
     } 
     return d2.read() < 0; // is the end of the second file also. 
    } catch(EOFException ioe) { 
     return false; 
    } finally { 
     i1.close(); 
     i2.close(); 
    } 
} 
+0

Vì vậy, làm cách nào để làm điều đó - ví dụ: một giải pháp thực tế? – dacwe

+0

@dacwe: Phân bổ hai bộ đệm byte 'byte [] buf1 = new byte [BlockSize]; byte [] buf2 = new byte [BlockSize]; 'và so sánh buf1 và buf2 sau khi bạn đọc vào hai bộ đệm này từ i1 và i2. – Patrick

+0

@patrick, Peter Lawrey: À, điều đó không dễ như thế .. :) sfussenegger nghĩ rằng anh ta có nó, nhưng anh ta cũng sai. – dacwe

2

tại sao không chỉ đơn giản là quấn cả hai con suối ở đầu rất của phương pháp của bạn:

i1 = new BufferedInputStream(i1); 
i2 = new BufferedInputStream(i2); 

Ngoài ra, bạn chỉ đơn giản là có thể thử đọc cả suối vào một bộ đệm:

public static boolean equals(InputStream i1, InputStream i2, int buf) throws IOException { 
    try { 
     // do the compare 
     while (true) { 
      byte[] b1 = new byte[buf]; 
      byte[] b2 = new byte[buf]; 

      int length = i1.read(b1); 
      if (length == -1) { 
       return i2.read(b2, 0, 1) == -1; 
      } 

      try { 
       StreamUtils.readFully(i2, b2, 0, length); 
      } catch (EOFException e) { 
       // i2 is shorter than i1 
       return false; 
      } 

      if (!ArrayUtils.equals(b1, b2, 0, length)) { 
       return false; 
      } 
     } 
    } finally { 
     // simply close streams and ignore (log) exceptions 
     StreamUtils.close(i1, i2); 
    } 
} 

// StreamUtils.readFully(..) 
public static void readFully(InputStream in, byte[] b, int off, int len) throws EOFException, IOException { 
    while (len > 0) { 
     int read = in.read(b, off, len); 
     if (read == -1) { 
      throw new EOFException(); 
     } 
     off += read; 
     len -= read; 
    } 
} 

// ArrayUtils.equals(..) 
public static boolean equals(byte[] a, byte[] a2, int off, int len) { 
    if (off < 0 || len < 0 || len > a.length - off || len > a2.length - off) { 
     throw new IndexOutOfBoundsException(); 
    } else if (len == 0) { 
     return true; 
    } 

    if (a == a2) { 
     return true; 
    } 
    if (a == null || a2 == null) { 
     return false; 
    } 

    for (int i = off; i < off + len; i++) { 
     if (a[i] != a2[i]) { 
      return false; 
     } 
    } 

    return true; 
} 

EDIT: Tôi đã sửa lỗi triển khai của mình ngay bây giờ. Đó là cách nó trông giống như không có DataInputStream hoặc NIO. Mã là available at GitHub hoặc từ Sonatype's OSS Snapshot Repository Maven:

<dependency> 
    <groupId>at.molindo</groupId> 
    <artifactId>molindo-utils</artifactId> 
    <version>1.0-SNAPSHOT</version> 
</dependency> 
+0

Nói chung, điều này sẽ không hoạt động vì so sánh số lần đọc nguyên tử ... – khachik

+1

Phương thức 'read' không được chỉ định cho điều đó (có thể trả về không đọc đầu vào đầy đủ!) – dacwe

+0

Ngoài ra, có thể dự đoán được những gì có chứa' b1 [1023] 'nếu' length = 100' không? – khachik

56

Cho đến nay yêu thích của tôi là sử dụng lớp org.apache.commons.io.IOUtils helper từ Apache Commons IO library:

IOUtils.contentEquals(is1, is2); 
Các vấn đề liên quan