2010-11-22 24 views
20

J'ai un problème, j'ai besoin de comparer deux entréestreams rapidement.Façon rapide de comparer les flux d'entrées

Aujourd'hui, j'ai une fonction comme ceci:

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

Mais il est vraiment lent. Je veux utiliser des lectures tamponnées mais je n'ai pas trouvé une bonne façon de le faire.

Quelques trucs supplémentaires qui le rend plus difficile:

  • Je ne veux pas lire l'un des flux d'entrée dans la mémoire (l'entier)
  • Je ne veux pas utiliser un tiers bibliothèque

J'ai besoin d'une solution pratique - code! :)

+0

I ne pensez pas que vous pouvez comparer n'importe quoi sans le lire dans la mémoire. Voulez-vous réellement lire le * inputtream * entier en mémoire, ce qui signifie que lire un nombre fixe d'octets est correct? – Patrick

+0

Je voulais dire que la lecture de l'intégralité du flux d'entrée en mémoire n'est pas une option – dacwe

Répondre

15

Quelque chose comme cela peut faire:

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 Je l'aime. NIO ftw :) – Patrick

+0

Frapper la cible! – dacwe

+0

@dacwe, je peux garantir que c'est plus lent que la solution que j'ai fournie. ;) –

8

L'utilisation de lectures en mémoire tampon est juste une question d'encapsulation des InputStreams avec BufferedInputStreams. Cependant, vous obtiendrez probablement la meilleure performance en lisant de gros blocs à la fois.

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

Alors, comment faire cela - par ex. une solution pratique? – dacwe

+0

@dacwe: Allouer deux tampons octets 'byte [] buf1 = nouvel octet [BlockSize]; byte [] buf2 = new byte [BlockSize]; 'et comparez buf1 et buf2 après avoir lu dans ces deux tampons depuis i1 et i2. – Patrick

+0

@patrick, Peter Lawrey: Eh bien, ce n'est pas si facile .. :) sfussenegger pensait qu'il l'avait, mais il a aussi tort. – dacwe

2

pourquoi ne pas simplement envelopper les deux cours d'eau au début de votre méthode:

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

Sinon, vous pouvez simplement essayer de lire les deux cours d'eau dans un tampon:

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: J'ai corrigé ma mise en œuvre maintenant. Voilà à quoi cela ressemble sans DataInputStream ou NIO. Code est available at GitHub ou de Sonatype's OSS Snapshot Repository Maven:

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

Généralement, cela ne fonctionnera pas à cause de la comparaison des lectures atomiques ... – khachik

+1

La méthode 'read' n'est pas spécifiée pour cela (peut retourner ne pas lire la pleine entrée!) – dacwe

+0

Aussi, est-ce prévisible ce qui contient dire' b1 [1023] 'si' length = 100'? – khachik