You can write your collector for this. This option is based on another that I wrote for a similar case :
private static Collector<String, List<String>, String> limitingJoin(String delimiter, int limit, String ellipsis) { return Collector.of( ArrayList::new, (l, e) -> { if (l.size() < limit) l.add(e); else if (l.size() == limit) l.add(ellipsis); }, (l1, l2) -> { l1.addAll(l2.subList(0, Math.min(l2.size(), Math.max(0, limit - l1.size())))); if (l1.size() == limit) l1.add(ellipsis); return l1; }, l -> String.join(delimiter, l) ); }
In this code, we save the ArrayList<String> all related lines. When an element is accepted, the size of the current list is checked at the limit: strictly less than it, an element is added; equal to it, an ellipsis is added. The same thing is done for the combiner part, which is a bit more complicated, because we need to correctly handle the sizes of the sub lists so as not to cross the limit. Finally, the finisher simply joins this list with the specified separator.
This implementation works for parallel threads. It will store the main elements of the stream in a meeting order . Note that it consumes all the elements in the stream, even if no elements are added after reaching the limit.
Working example:
List<String> list = Arrays.asList("foo", "bar", "baz"); System.out.println(list.stream().collect(limitingJoin(", ", 2, "...")));
source share