2015-07-16 2 views
14

Ich habe die folgende for-Schleife, die durch eine Liste von Strings iteriert und speichert das erste Zeichen jedes Wortes in einem StringBuilder. Ich würde gerne wissen, wie kann ich diesen Ausdruck auf einen Lambda-TransformationKonvertiere eine for-Schleife in concat String in einen Lambda-Ausdruck

StringBuilder chars = new StringBuilder(); 
for (String l : list) { 
    chars.append(l.charAt(0)); 
} 

Antwort

27

Sie toString() auf den StringBuilder nennen Angenommen danach, ich glaube, Sie suchen nur für Collectors.joining() nach zu einer Teileinzelzeichen jede Zeichenfolge abbildet :

String result = list 
    .stream() 
    .map(s -> s.substring(0, 1)) 
    .collect(Collectors.joining()); 

Beispielcode:

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 
    } 
} 

Beachten Sie die Verwendung von substring instea d von charAt, so haben wir immer noch einen Strom von Strings mit zu arbeiten.

+0

Setzt dies nur das erste Zeichen jedes Strings an? – SamTebbs33

+1

@ SamTebbs33: Whoops, nicht. Wird reparieren. –

12

ohne viele Zwischen String-Objekte schaffen Sie es wie folgt zu tun:

StringBuilder sb = list.stream() 
         .mapToInt(l -> l.codePointAt(0)) 
         .collect(StringBuilder::new, 
           StringBuilder::appendCodePoint, 
           StringBuilder::append); 

Beachten Sie, dass codePointAt Verwendung ist viel besser als charAt als ob die Zeichenfolge mit Ersatzpaar beginnt, charAt verwenden, können Sie ein unvorhersehbares Ergebnis haben .

15

Tonnen von Möglichkeiten, dies zu tun - die einfachste Option: halten Sie sich an ein StringBuilder zum Hinzufügen und dies tun:

final StringBuilder chars = new StringBuilder(); 

    list.forEach(l -> chars.append(l.charAt(0))); 
+1

dann können Sie nur 'list.forEach (...)' ohne '.stream()' – Holger

+0

richtig Sie sind - danke! Heute etwas Neues gelernt. –

+0

Danke, das gefällt mir. Nice One Liner, einfach, elegant –

3

Hier sind drei verschiedene Lösungen für dieses Problem. Jede Lösung filtert zuerst leere Zeichenfolgen, da andernfalls StringIndexOutOfBoundsException ausgelöst werden kann.

Diese Lösung ist die gleiche wie die Lösung von Tagir mit dem hinzugefügten Code zum Filtern leerer Strings. Ich habe es hier hauptsächlich aufgenommen, um es mit den anderen beiden Lösungen zu vergleichen, die ich zur Verfügung gestellt habe.

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

Die zweite Lösung verwendet Eclipse Collections, und nutzt einen relativ neuen Containertyp namens CodePointAdapter, die in Version 7.0 hinzugefügt wurde.

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

Die dritte Lösung nutzt Eclipse-Kollektionen wieder, aber mit injectInto und StringBuilder statt 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); 

Hinweis: Ich bin ein Committer für Eclipse Collections.