La documentation recommande l’emploi de flux bufferisés (buffered désolé pas de terme français adéquat) pour atteindre l’efficacité maximale « top efficiency ». L’idée des flux bufferisés, qui est très générale, est la suivante : l’écriture ou la lecture effective de caractères a un coût fixe important, qui est payé quelque soit le nombre de caractères effectivement transférés entre fichier et programme. Plus précisément, écrire ou lire n caractères coûte de l’ordre de K0 + K1·n, où K0 est bien plus grand que K1. Cela peut s’expliquer par divers phénomènes. Par exemple, un appel au système d’exploitation est relativement lent, et une opération d’entrée-sortie signifie un seul appel système, quelque soit le nombre de caractères impliqués. Ou encore, le coût des transferts entre disque et mémoire est largement indépendant du nombre de caractères transférés, jusqu’à une certaine taille, de par la nature même du dispositif physique « disque » qui lit et écrit des données par blocs de taille fixée.
Pour fixer les idées, prenons l’exemple de l’écriture.
L’idée est alors de ne pas écrire effectivement chaque caractère
en réaction à un appel out.write(c)
mais à la place de le ranger dans
une zone mémoire appelée tampon (buffer).
Le tampon a une taille fixe, et les caractères du tampon sont effectivement
transmis au système d’exploitation quand le tampon est plein.
De cette façon le coût fixe K0 est payé moins souvent et l’efficacité totale
est améliorée.
Les transferts vers les fichiers à travers un tampon mémoire
présentent l’inconvénient que le caractère c
peut ne pas se
trouver dans le fichier2
dès que l’on appelle out.write(c)
. L’effet est particulièrement
gênant à la fin du programme. Si le tampon n’est pas vidé
(flushed), son contenu est perdu. En effet, le tampon est de
la mémoire appartenant au programme et qui donc disparaît avec lui.
Il en résulte généralement que la fin du flux ne se retrouve pas dans
le fichier. Il faut donc vider le tampon avant de terminer le
programme, ce que fait la méthode close()
de fermeture des flux,
avant de fermer effectivement le flux.
On peut aussi vider le tampon plus directement en appelant
méthode flush()
des flux bufferisés.
L’existence d’un retard entre ce qui est écrit par le programme
dans le flux et ce qui est effectivement écrit dans le fichier donne
donc une raison supplémentaire de fermer les fichiers.
Pour ce qui est d’un fichier ouvert en lecture, la technique du tampon s’applique également, avec les mêmes bénéfices en terme d’efficacité. Dans ce cas, les lectures (read) se font dans le tampon, qui est rempli à partir du fichier quand une demande de lecture trouve un tampon vide. Il y a alors bien entendu une avance à la lecture mais ce décalage ne pose pas les mêmes problèmes que le retard à l’écriture.
Un flux bufferisé en écriture (resp. lecture) est un objet de la classe
BufferedWriter (resp. BufferedReader),
qui se construit simplement à partir d’un Writer
(resp. Reader
), et qui reste un Writer
(resp. Reader
). Voici une autre version Cp2
(source Cp2.java) de la commande cp écrite en Java, qui
emploie les entrées-sorties bufferisées.
Une mesure rapide des temps d’exécution montre que le programme Cp2
est
à peu près trois fois plus rapide que Cp
.
Les flux bufferisés BufferedWriter
et BufferedReader
offrent aussi une vue des fichier texte comme étant composés de
lignes. Il existe une méthode
Newline pour
écrire une fin le ligne, et une méthode
readLine pour
lire une ligne. On utilise souvent BufferedReader
pour cette
fonctionnalité de lecture ligne à ligne.
Il faut aussi noter que les lignes
sont définies indépendamment de leur réalisation
par les divers systèmes d’exploitation (voir B.3.2.3).