2010-11-22 3 views
20

У меня проблема, мне нужно быстро сравнить два входа.Быстрый способ сравнения входных потоков

Сегодня у меня есть функция, как это:

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

Но это очень медленно. Я хочу использовать буферизованные чтения, но не придумал хороший способ сделать это.

Некоторые дополнительные вещи, которые делают его более трудным:

  • Я не хочу, чтобы прочитать один из входных потоков в памяти (весь один)
  • Я не хочу, чтобы использовать третью сторону библиотека

Мне нужно практическое решение - код! :)

+0

I не думайте, что вы можете сравнивать что-либо, не читая его в памяти. Вы действительно имеете в виду чтение * всего входного потока * в память, то есть чтение фиксированного количества байтов в порядке? – Patrick

+0

Я имел в виду, что чтение всего входного потока в память не является опцией – dacwe

ответ

15

Нечто подобное может сделать:

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 Мне это нравится. NIO ftw :) – Patrick

+0

Взрыв на цель! – dacwe

+0

@ dacwe, я могу гарантировать его медленнее, чем предлагаемое мной решение. ;) –

8

Использование буферизованных чтений - это всего лишь вопрос обертывания InputStreams с помощью BufferedInputStreams. Однако вы, скорее всего, получите максимальную производительность при чтении больших блоков за раз.

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

Итак, как мне это сделать - например, практическое решение? – dacwe

+0

@ dacwe: Выделить два байтовых буфера 'byte [] buf1 = новый байт [BlockSize]; byte [] buf2 = новый байт [BlockSize]; 'и сравнить buf1 и buf2 после того, как вы прочитали эти два буфера из i1 и i2. – Patrick

+0

@patrick, Peter Lawrey: Ну, это не так просто .. :) sfussenegger подумал, что у него это есть, но он тоже ошибается. – dacwe

2

почему бы не просто обернуть оба потока в самом начале вашего метода:

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

В качестве альтернативы, вы можете просто попробовать чтение обоих потоков в буфер:

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: Теперь я исправил свою реализацию. Вот как это выглядит без DataInputStream или NIO. Кодекс available at GitHub или Sonatype's OSS Snapshot Repository Maven:

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

Как правило, это не сработает из-за сравнения атомных чтений ... – khachik

+1

'read' метод не указан для этого (может возвращать не чтение полного ввода!) – dacwe

+0

Кроме того, предсказуемо, что содержит слово' b1 [1023] 'if' length = 100'? – khachik

Смежные вопросы