Creating a human-readable list from the values of a map I'm creating a human-readable list from the keys of a map object.  I want it to be comma-separated, but include the word "and" at the end before the last element, supplying a given locale for the word "and".

E.g., my map might have the strings:

"AK", "Alaska"
"CA", "California"
"NY", "New York"


I would want as output:

Alaska, California, and New York


for English and

Alaska, California, y New York


for Spanish.

So I have:

public static String getHumanReadableStates(Map&lt;String, String&gt; stateMap) {

    long count = stateMap.values().stream().map(Object::toString).count();
    return stateMap.values().stream().map(Object::toString)
            .limit(count - 1)
            .collect(Collectors.joining(", "))
            .concat(", ")
            .concat(LocaleUtil.getBundleValue("theWordAnd"))
            .concat(" ")
            .concat(Objects.requireNonNull(stateMap.values().stream().reduce((first, second) -&gt; second)
                    .orElse(null)));
}


where LocaleUtil has a method called getBundleValue() to simply get the word "and" from messages.properties.

Is there a simpler way to achieve this?  Or should streams be avoided altogether?
A stream-based approach is still feasible, by materializing your state names into a List first.

By using a List, you can retrieve all the names except the last using List.subList(int, int) (this may be empty, for a single-entry Map), and then retrieving the last (or the only, for a single-entry Map) directly using a simple List.get(int). Afterwards, you just need to handle for an empty Map or String values.

public static String getHumanReadableStates(Map&lt;String, String&gt; stateMap) {
    List&lt;String&gt; names = new ArrayList&lt;&gt;(stateMap.values());
    if (names.isEmpty()) {
        return "";
    }
    String exceptLastPlusBlank = Stream.concat(
            names.subList(0, names.size() - 1).stream(), Stream.of(""))
            .collect(Collectors.joining(", "));
    return Stream.of(exceptLastPlusBlank, names.get(names.size() - 1))
            .filter(v -&gt; !v.isEmpty())
            .collect(Collectors.joining(LocaleUtil.getBundleValue("theWordAnd") + " "));
}


Stream.of("") is introduced to append the final comma.