2010-11-22 6 views
20

Tengo un problema, necesito comparar dos entradas de intensidad rápidamente.Forma rápida de comparar los flujos de entrada

Hoy tengo una función como esta:

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

pero es muy lento. Quiero usar lecturas en búfer pero no he encontrado una buena manera de hacerlo.

Algunas cosas extra que hace que sea más difícil:

  • no quiero leer uno de los flujos de entrada en la memoria (todo el uno)
  • que no quieren utilizar a un tercero biblioteca

Necesito una solución práctica - ¡código! :)

+0

I no piense que puede comparar nada sin leerlo en la memoria. ¿En realidad quiere decir leer * wholestarstream * en la memoria, lo que significa que leer un número fijo de bytes está bien? – Patrick

+0

Me refiero a leer todo el flujo de entrada en la memoria no es una opción – dacwe

Respuesta

15

Algo como esto puede hacer:

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 Me gusta. NIO ftw :) – Patrick

+0

¡Golpe en el objetivo! – dacwe

+0

@dacwe, puedo garantizar que sea más lento que la solución que proporcioné. ;) –

8

El uso de lecturas en búfer es solo una cuestión de ajustar InputStreams con BufferedInputStreams. Sin embargo, es probable que obtengas el mejor rendimiento leyendo bloques grandes a la vez.

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

Entonces, ¿cómo hago eso, por ejemplo? una solución práctica? – dacwe

+0

@dacwe: Asigna búfers de dos bytes 'byte [] buf1 = new byte [BlockSize]; byte [] buf2 = new byte [BlockSize]; 'y compare buf1 y buf2 después de leer en esos dos buffers de i1 e i2. – Patrick

+0

@patrick, Peter Lawrey: Bueno, eso no es tan fácil ... :) sfussenegger pensó que lo tenía, pero también está equivocado. – dacwe

2

por qué no simplemente envolver ambas corrientes en el comienzo mismo de su método:

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

Como alternativa, puede simplemente tratar de leer ambas corrientes en un búfer:

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: He arreglado mi implementación ahora. Así es como se ve sin DataInputStream o NIO. Código es available at GitHub o desde Sonatype's OSS Snapshot Repository Maven:

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

En general, esto no funcionará debido a la comparación de lecturas atómicas ... – khachik

+1

'read' método no está especificado para eso (podría devolver no leyendo la entrada completa!) – dacwe

+0

Además, ¿es predecible qué contiene decir' b1 [1023] 'si' longitud = 100'? – khachik

Cuestiones relacionadas