J'ai la boucle for suivante qui parcourt une liste de chaînes et stocke le premier caractère de chaque mot dans un StringBuilder
. Je voudrais savoir comment puis-je transformer cela en une expression lambda
StringBuilder chars = new StringBuilder();
for (String l : list) {
chars.append(l.charAt(0));
}
En supposant que vous appeliez ensuite toString()
sur la StringBuilder
, je pense que vous recherchez simplement Collectors.joining()
, après avoir mappé chaque chaîne sur une sous-chaîne à un caractère:
String result = list
.stream()
.map(s -> s.substring(0, 1))
.collect(Collectors.joining());
Exemple de code:
import Java.util.*;
import Java.util.stream.*;
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("foo");
list.add("bar");
list.add("baz");
String result = list
.stream()
.map(s -> s.substring(0, 1))
.collect(Collectors.joining());
System.out.println(result); // fbb
}
}
Notez l'utilisation de substring
au lieu de charAt
, nous avons donc toujours un flux de chaînes avec lequel travailler.
Des tonnes de façons de faire cela - l’option la plus simple: tenez-vous à ajouter à un StringBuilder
et faites ceci:
final StringBuilder chars = new StringBuilder();
list.forEach(l -> chars.append(l.charAt(0)));
Sans créer de nombreux objets String intermédiaires, vous pouvez le faire comme ceci:
StringBuilder sb = list.stream()
.mapToInt(l -> l.codePointAt(0))
.collect(StringBuilder::new,
StringBuilder::appendCodePoint,
StringBuilder::append);
Notez que l'utilisation de codePointAt
est bien meilleure que charAt
comme si votre chaîne commençait par une paire de substitution. Si vous utilisiez charAt
, vous obtiendrez peut-être un résultat imprévisible.
Voici trois solutions différentes à ce problème. Chaque solution filtre les chaînes vides en premier, sinon StringIndexOutOfBoundsException
peut être lancé.
Cette solution est la même que celle de Tagir avec le code ajouté pour le filtrage des chaînes vides. Je l'ai inclus ici principalement pour comparer les deux autres solutions que j'ai fournies.
List<String> list =
Arrays.asList("the", "", "quick", "", "brown", "", "fox");
StringBuilder builder = list.stream()
.filter(s -> !s.isEmpty())
.mapToInt(s -> s.codePointAt(0))
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append);
String result = builder.toString();
Assert.assertEquals("tqbf", result);
La seconde solution utilise Eclipse Collections et utilise un type de conteneur relativement nouveau appelé CodePointAdapter
ajouté dans la version 7.0.
MutableList<String> list =
Lists.mutable.with("the", "", "quick", "", "brown", "", "fox");
LazyIntIterable iterable = list.asLazy()
.reject(String::isEmpty)
.collectInt(s -> s.codePointAt(0));
String result = CodePointAdapter.from(iterable).toString();
Assert.assertEquals("tqbf", result);
La troisième solution utilise à nouveau Eclipse Collections, mais avec injectInto
et StringBuilder
au lieu de CodePointAdapter
.
MutableList<String> list =
Lists.mutable.with("the", "", "quick", "", "brown", "", "fox");
StringBuilder builder = list.asLazy()
.reject(String::isEmpty)
.collectInt(s -> s.codePointAt(0))
.injectInto(new StringBuilder(), StringBuilder::appendCodePoint);
String result = builder.toString();
Assert.assertEquals("tqbf", result);
Remarque: je suis un partisan des collections Eclipse.
Manière simple en utilisant la référence de la méthode:
List<String> list = Arrays.asList("ABC", "CDE");
StringBuilder sb = new StringBuilder();
list.forEach(sb::append);
String concatString = sb.toString();