2016-04-15 7 views
3

Ich habe in einer allgemeinen Situation, wo ich eine Liste von Objekten habe und müssen eine Komma getrennte Zeichenfolge mit einer einzigen Eigenschaft, die dann jeweils von einfachen Anführungszeichen umgeben sind generiert .Wie verallgemeinere Dienstprogrammfunktion mit Methodenreferenzen/Java 8

2 Beispiele

public String partIDsToString(List<Part> parts){ 
    StringBuilder sb = new StringBuilder(); 
    for(Part part : parts) 
     sb.append("'"+part.getPartNumber() + "',"); 
    return sb.substring(0,sb.length()-1); 
} 

public String companyIDsToString(List<Company> parts){ 
    StringBuilder sb = new StringBuilder(); 
    for(Company c : parts) 
     sb.append("'"+c.getId() + "',"); 
    return sb.substring(0,sb.length()-1); 
} 

Ich werde mehr Methoden wie dies in der Zukunft schaffen müssen, und frage mich, ob es eine Möglichkeit, diese Funktionalität zu verallgemeinern war, im für so etwas suchen.

public String objectPropertyToString(List<Object> list, Method getProperty){ 
    StringBuilder sb = new StringBuilder(); 
    for(Object obj: list) 
     sb.append("'"+obj.getProperty() + "',"); 
    return sb.substring(0,sb.length()-1); 
} 

List<Company> companies = getCompaniesList();//not important 
String result = objectPropertyToString(companies , Company::getId); 

List<Part> parts= getPartsList();//not important 
String result = objectPropertyToString(parts, Part::getPartNumber); 

Kann dies mit Methodenreferenzen/Lambdas oder auf andere Weise geschehen?

Antwort

4

Stream.map() und Collectors.joining() sind deine Freunde hier.

companies.stream() 
     .map(Company::getId) 
     .map(s -> "'" + s + "'") 
     .collect(joining(",")); 

Sie können eine Hilfsmethode erstellen, aber in meinem Urteil der oben sind prägnant genug, dass es nicht lohnt:

static <T> String mapAndJoin(Collection<T> c, Function<T,String> f){ 
    return c.stream() 
      .map(f) 
      .map(s -> "'" + s + "'") 
      .collect(joining(",")); 
} 

mapAndJoin(companies, Company::getId); 
+0

Danke, das ist genau das, was ich suchte. –

+1

Es macht keinen Sinn, 'f :: apply' zu verwenden; Damit erstellen Sie eine neue 'Funktion', deren 'apply'-Methode die' apply'-Methode der anderen 'Funktion' aufruft. Verwenden Sie einfach 'f' hier. – Holger

+0

Duh, danke @Holger –

2

Wie in this answer gezeigt, kann Collectors.joining ein Komma zu erzeugen, verwendet werden, Trennliste. Sie können sie aber auch verwenden, um die Elemente auf einmal in einfache Anführungszeichen zu setzen, was effizienter ist als dies in einer separaten String-Operation pro Element vor deren Verknüpfung getan wird.

Die Grundidee ist wie folgt:

public static <T> String objectPropertyToString(
        Collection<? extends T> list, Function<T,String> f) { 
    return list.stream().map(f).collect(Collectors.joining("', '", "'", "'")); 
} 

Anstatt nur die Elemente mit nur ein Komma zu trennen, trennen wir mit einer Schließung Apostroph, gefolgt von Komma und einer Öffnung Apostroph. Außerdem verwenden wir ein öffnendes einzelnes Zitat als Starter vor dem ersten Element und ein schließendes einfaches Zitat nach dem letzten Element.

Dies funktioniert reibungslos, mit einer Ausnahme: Wenn die Liste leer ist, erhalten wir eine einzige '' als Ergebnis, weil die anfängliche Eröffnung Quote und abschließende Schlusskurs immer produziert wird. Um dies zu lösen, müssen wir nachvollziehen, was Collectors.joining intern tut, bekommen die Hände auf den StringJoiner für das Verfahren verwendet, so können wir sie in einer Weise konfigurieren, nicht durch den Einbau-Sammler angeboten:

public static <T> String objectPropertyToString(
        Collection<? extends T> list, Function<T,? extends CharSequence> f) { 
    return list.stream().map(f).collect(
     ()->new StringJoiner("', '", "'", "'").setEmptyValue(""), 
     StringJoiner::add, StringJoiner::merge).toString(); 
} 

Diese im Grunde tut dasselbe wie der vorherige Versuch mit der bemerkenswerten Ausnahme, dass wir jetzt setEmptyValue verwenden können, um das unterschiedliche Ergebnis für den Fall anzugeben, dass keine Elemente vorhanden waren. Als Bonus können wir nun die generische Typ-Signatur lockern, so dass beliebige CharSequence Instanzen anstelle von nur String s verbunden werden können.

Die Nutzung ist nach wie vor:

List<Company> companies = getCompaniesList();//not important 
String result = objectPropertyToString(companies , Company::getId); 

List<Part> parts= getPartsList();//not important 
String result = objectPropertyToString(parts, Part::getPartNumber);