How to generalize utility function using method references/java 8

后端 未结 2 1984
陌清茗
陌清茗 2021-01-14 18:46

I\'ve run into a common situation where i have a list of objects and need to generate a comma separated string with a single property, which are then each surrounded by sing

相关标签:
2条回答
  • 2021-01-14 19:24

    As shown in this answer, Collectors.joining can be used to produce a comma separator list. But you can also use it to enclose the elements in single quotes in one go, which is more efficient than doing this in a separate String operation per element before joining them.

    The basic idea is as follows:

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

    Instead of just separating the elements with just a comma, we separate with a closing single quote, followed by comma and an opening single quote. Further, we use an opening single quote as starter before the first element and a closing single quote after the last element.

    This works smoothly, with only one exception: if the list is empty, we get a sole '' as result because the initial opening quote and trailing closing quote is always produced. To solve this, we have to retrace what Collectors.joining does internally, to get hands on the StringJoiner used for the process, so we can configure it in a way not offered by the built-in collector:

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

    This basically does the same as the previous attempt with the notable exception that we now can use setEmptyValue to specify the different result for the case that there were no elements. As a bonus we can now relax the generic type signature, allowing arbitrary CharSequence instances to be joined instead of just Strings.

    The usage is as before:

    List<Company> companies = getCompaniesList();//not important
    String result = objectPropertyToString(companies , Company::getId);
    
    List<Part> parts= getPartsList();//not important
    String result = objectPropertyToString(parts, Part::getPartNumber);
    
    0 讨论(0)
  • 2021-01-14 19:45

    Stream.map() and Collectors.joining() are your friends here.

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

    You can create a helper method, but in my judgement the above is succinct enough that it isn't worthwhile:

    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 讨论(0)
提交回复
热议问题