Envoyé par
lanasandra
Du coup, j'avais une autre petite question
: si je veux obtenir une liste ligne par ligne, est-ce que ce que j'ai fait est aussi obsolète ?
1 2
| sb.append(System.lineSeparator());
line = br.readLine(); |
Je ne suis pas entré dans tout le détail de ton code effectivement. Si tu cherches à construire une chaîne qui concatène toutes les lignes, on peut faire ce que tu as fait, en résumé :
Créer un StringBuffer
StringBuffer sb = new StringBuffer();
Parcourir les lignes en ajoutant au StringBuffer en intercalant un séparateur de ligne par sb.append(System.lineSeparator()).
Seulement il manque une chose indispensable dans ton code d'origine : ajouter chaque ligne dans le StringBuffer.
Ensuite, StringBuffer n'est pas obsolète : simplement cette classe est dite "synchronisée". Elle est prévue pour fonctionner en multithread. Ce qui n'est pas utile dans ton cas. L'équivalent en monothread est StringBuilder et s'utilise exactement de la même manière. A une époque, choisir un StringBuilder était avantageux par rapport à un StringBuffer lorsqu'on était en monothread, pour des raisons de performances. Je n'ai pas d'informations sur les différences de performances sur des environnements récents avec des versions de jvm récentes qui sont beaucoup plus rapides qu'à une époque. Mais bon, autant utiliser un StringBuilder quand un StringBuffer n'est pas nécessaire.
Ensuite, il y a des moyens beaucoup plus simples de faire la même chose, d'autant plus qu'avec le code que je t'ai donné, on a une liste de lignes désormais.
Tout d'abord, s'il s'agit juste de l'envoyer dans la console, on peut simplement parcourir les lignes :
1 2 3
| for(String line : lines) {
System.out.println(line);
} |
Ou encore le faire directement sans la boucle for, avec un code très concis :
lines.forEach(System.out::println);
On peut même le faire de la même manière en lisant le fichier lignes à lignes sans le charger dans une List en mémoire :
Files.lines(Paths.get(FILENAME)).forEach(System.out::println);
On peut même écrire les lignes dans un fichier directement :
Files.write(Paths.get(FILENAME), lines);
On peut faire des choses similaires à partir d'une Map, mais j'en parlerai après avoir répondu à ta seconde question.
[QUOTE=lanasandra;11306241]
J'ai aussi lu qu'on ne peut pas ensuite sortir un fichier texte issu d'une Map qui serait triée par ordre alphabétique car les Map ne le permettent pas : est-ce que tu pourrais juste me le confirmer ]
Quand on parle de Map, on sous entends parler d'objets de classe qui implémente l'interface java.util.Map. La plus couramment utilisée c'est la HashMap. Et l'une de ses caractéristiques et qu'elle ne garantit aucun ordre particulier (c'est dû au stockage optimisé par une table de hashage dont le but est d'optimiser l'opération pour retrouver une valeur en fonction d'une clef.
Mais il existe des implémentations de l'interface Map qui sont "ordonnées" :
- java.util.LinkedHashMap garantit que l'ordre des associations (donc des clefs) respectent l'ordre d'insertion
- java.util.TreeMap permet d'avoir un tri selon l'ordre qu'on veut, donc alphabétique par exemple. On peut même trier selon les valeurs.
Pour la LinkedHashMap, dans les cas où je crée explicitement la HashMap, il suffit de remplacer la partie instanciation :
Map<String, Long> compteurs = new LinkedHashMap<>();
Pour le "grouping", il faut indiquer au Collector une fabrique (dans le code suivant c'est le LinkedHashMap::new:
1 2 3 4 5
| Map<String, Long> compteurs = list.stream()
.collect(Collectors.groupingBy(Function.identity(),
LinkedHashMap::new,
Collectors.counting())
); |
Pour avoir une Map triée, le plus simple est de créer une nouvelle TreeMap puis d'ajouter toutes les associations de la Map d'origine dedans :
1 2
| TreeMap<String, Long> compteurstri = new TreeMap<>();
compteurstri.putAll( compteurs ); |
Ici l'ordre de tri est l'ordre de tri (par défaut) dit naturel des clefs, donc de String. Cela ressemble à un tri alphabétique, mais ce n'est pas tout à fait exactement un tri alphabétique au sens commun. Ce tri se base sur l'ordre des codes de caractères. Par exemple, les accents ne seront pas triés selon un ordre correspondant à une attente classique (le f suit le e, mais le é, le è etc ne sont pas entre le e et le f, du coup, les chaînes comprenant des mots avec accents ne seront pas correctement triées). Il y a également le problème des nombres : avec un tri naturel, Machin10 est avant Machin2 alors que naturellement on s'attend à ce que Machin10 soit plutôt après.
Pour configurer l'ordre qu'on veut, on peut indiquer un Comparator en paramètre de l'instanciation de la TreeMap.
Pour trier des chaînes en tenant compte des accents, on peut utiliser un Comparateur spécial, implémenté par la classe Collator.
TreeMap<String, Long> compteurstri = new TreeMap<>(Collator.getInstance());
Pour le tri tenant compte de l'ordre des caractères, c'est plus compliqué. Si tu en as besoin, je te dirais comment faire.
Enfin, pour le parcours et l'éventuel sortie en console, ou vers un fichier.
Je t'ai dit qu'on pouvait procéder de façon similaire à une List. On fait ça en prenant la liste des associations de la map. Les associations sont représentées par la classe Map.Entry qui a deux méthodes : une pour récupérer la clef et une pour récupérer la valeur.
Donc, par exemple, pour afficher les lignes avec leur compteur associé (concaténation de la ligne, de :, puis du compteur)
1 2 3
| for( Map.entry<String,Long> compteur : compteurstri.entrySet() ) {
System.out.println(compteur.getKey() + ": " + compteur.getValue());
} |
On peut retransformer la map en liste, ce qui permet de l'écrire dans un fichier :
1 2 3 4 5
| List<String> finallines = compteurstri.entrySet()
.stream()
.map(entry-> entry.getKey()+": "+entry.getValue())
.collect(Collectors.toList());
Files.write(Paths.get(OUTPUTFILENAME), finallines); |
Si on veut ne retrouver que les lignes sans la valeur, on peut passer directement par la liste des clefs :
1 2
|
Files.write(Paths.get(OUTPUTFILENAME), compteurs.keySet()); |
Ceci permet d'obtenir le fichier dédoublonné sans altération de l'ordre si compteurs est une LinkedHashMap.
On peut même exclure les lignes qui ne sont pas en double (ou plus), :
1 2 3 4 5 6
| List<String> finallines = compteurstri.entrySet()
.stream()
.filter( entry-> entry.getValue()>1 ) // on ne garde que les lignes dont le compteur est supérieur à un
.map(entry-> entry.getKey()+": "+entry.getValue())
.collect(Collectors.toList());
Files.write(Paths.get(OUTPUTFILENAME), finallines); |
Si on veut ne retrouver que les lignes sans la valeur :
1 2 3 4 5 6
| List<String> finallines = compteurstri.entrySet()
.stream()
.filter( entry-> entry.getValue()>1 ) // on ne garde que les lignes dont le compteur est supérieur à un
.map(Map.entry::getKey) // on récupère que la clef (donc la ligne)
.collect(Collectors.toList()); // on transforme en List
Files.write(Paths.get(OUTPUTFILENAME), finallines); |
Partager