web-dev-qa-db-fra.com

Pourquoi utiliser BufferedInputStream pour lire un fichier octet par octet plus rapidement FileInputStream?

J'essayais de lire un fichier dans un tableau en utilisant FileInputStream, et un fichier d'environ 800 Ko prenait environ 3 secondes pour être lu en mémoire. J'ai ensuite essayé le même code sauf avec FileInputStream intégré dans un BufferedInputStream et cela a pris environ 76 millisecondes. Pourquoi lire un fichier octet par octet est-il tellement plus rapide avec BufferedInputStream même si je le lis toujours octet par octet? Voici le code (le reste du code n'a aucune pertinence). Notez qu'il s'agit du code "rapide". Vous pouvez simplement supprimer le BufferedInputStream si vous voulez le code "lent": 

InputStream is = null;

    try {
        is = new BufferedInputStream(new FileInputStream(file));

        int[] fileArr = new int[(int) file.length()];

        for (int i = 0, temp = 0; (temp = is.read()) != -1; i++) {
            fileArr[i] = temp;
        }

BufferedInputStream est 30 fois plus rapide. Bien plus que ça. Alors, pourquoi cela est-il possible et est-il possible de rendre ce code plus efficace (sans utiliser de bibliothèques externes)?

54
ZimZim

Dans FileInputStream, la méthode read() lit un seul octet. A partir du code source:

/**
 * Reads a byte of data from this input stream. This method blocks
 * if no input is yet available.
 *
 * @return     the next byte of data, or <code>-1</code> if the end of the
 *             file is reached.
 * @exception  IOException  if an I/O error occurs.
 */
public native int read() throws IOException;

Il s’agit d’un appel natif au système d’exploitation qui utilise le disque pour lire l’octet unique. C'est une opération lourde.

Avec BufferedInputStream, la méthode délègue à une méthode read() surchargée qui lit 8192 quantité d'octets et les met en mémoire tampon jusqu'à ce qu'ils soient nécessaires. Il ne renvoie toujours que le seul octet (mais garde les autres en réserve). De cette façon, la BufferedInputStream fait moins d'appels natifs au système d'exploitation pour lire à partir du fichier.

Par exemple, votre fichier a une longueur de 32768 octets. Pour obtenir tous les octets en mémoire avec une FileInputStream, vous aurez besoin d'appels natifs 32768 au système d'exploitation. Avec BufferedInputStream, vous n'aurez besoin que de 4, quel que soit le nombre d'appels read() que vous ferez (toujours 32768).

Pour accélérer le processus, vous pouvez envisager la classe NIO FileChannel de NIO 7 de Java 7, mais je n'ai aucune preuve à l'appui.

99

Un BufferedInputStream entouré d'un FileInputStream, demandera des données du FileInputStream en gros morceaux (512 octets ou plus par défaut, je pense.) Ainsi, si vous lisez 1000 caractères un par un, le FileInputStream n'aura qu'à aller deux fois sur le disque. . Ce sera beaucoup plus rapide!

2
usha

C'est à cause du coût de l'accès au disque. Supposons que vous aurez un fichier de 8 Ko. 8 * 1024 fois d’accès au disque seront nécessaires pour lire ce fichier sans BufferedInputStream. 

À ce stade, BufferedStream arrive sur les lieux et joue le rôle d'intermédiaire entre FileInputStream et le fichier à lire.

D'un coup, les morceaux d'octets par défaut sont de 8 Ko par défaut, puis FileInputStream lit les octets de cet intermédiaire. .__ Cela diminuera la durée de l'opération. 

private void exercise1WithBufferedStream() {
      long start= System.currentTimeMillis();
        try (FileInputStream myFile = new FileInputStream("anyFile.txt")) {
            BufferedInputStream bufferedInputStream = new BufferedInputStream(myFile);
            boolean eof = false;
            while (!eof) {
                int inByteValue = bufferedInputStream.read();
                if (inByteValue == -1) eof = true;
            }
        } catch (IOException e) {
            System.out.println("Could not read the stream...");
            e.printStackTrace();
        }
        System.out.println("time passed with buffered:" + (System.currentTimeMillis()-start));
    }


    private void exercise1() {
        long start= System.currentTimeMillis();
        try (FileInputStream myFile = new FileInputStream("anyFile.txt")) {
            boolean eof = false;
            while (!eof) {
                int inByteValue = myFile.read();
                if (inByteValue == -1) eof = true;
            }
        } catch (IOException e) {
            System.out.println("Could not read the stream...");
            e.printStackTrace();
        }
        System.out.println("time passed without buffered:" + (System.currentTimeMillis()-start));
    }
0
huseyin