View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.lang3.stream;
19  
20  import java.util.Arrays;
21  import java.util.Collections;
22  import java.util.Objects;
23  import java.util.Set;
24  import java.util.StringJoiner;
25  import java.util.function.BiConsumer;
26  import java.util.function.BinaryOperator;
27  import java.util.function.Function;
28  import java.util.function.Supplier;
29  import java.util.stream.Collector;
30  import java.util.stream.Collectors;
31  import java.util.stream.Stream;
32  
33  import org.apache.commons.lang3.StringUtils;
34  
35  /**
36   * Implementations of {@link Collector} that implement various reduction operations.
37   * <p>
38   * This class is called {@code LangCollectors} instead of {@code Collectors} to avoid clashes with {@link Collectors}.
39   * </p>
40   *
41   * @since 3.13.0
42   */
43  public final class LangCollectors {
44  
45      /**
46       * Simple implementation class for {@code Collector}.
47       *
48       * @param <T> the type of elements to be collected
49       * @param <R> the type of the result
50       */
51      private static final class SimpleCollector<T, A, R> implements Collector<T, A, R> {
52  
53          private final BiConsumer<A, T> accumulator;
54          private final Set<Characteristics> characteristics;
55          private final BinaryOperator<A> combiner;
56          private final Function<A, R> finisher;
57          private final Supplier<A> supplier;
58  
59          private SimpleCollector(final Supplier<A> supplier, final BiConsumer<A, T> accumulator, final BinaryOperator<A> combiner, final Function<A, R> finisher,
60              final Set<Characteristics> characteristics) {
61              this.supplier = supplier;
62              this.accumulator = accumulator;
63              this.combiner = combiner;
64              this.finisher = finisher;
65              this.characteristics = characteristics;
66          }
67  
68          @Override
69          public BiConsumer<A, T> accumulator() {
70              return accumulator;
71          }
72  
73          @Override
74          public Set<Characteristics> characteristics() {
75              return characteristics;
76          }
77  
78          @Override
79          public BinaryOperator<A> combiner() {
80              return combiner;
81          }
82  
83          @Override
84          public Function<A, R> finisher() {
85              return finisher;
86          }
87  
88          @Override
89          public Supplier<A> supplier() {
90              return supplier;
91          }
92      }
93  
94      private static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet();
95  
96      /**
97       * Delegates to {@link Stream#collect(Collector)} for a Stream on the given array.
98       *
99       * @param <T>       The type of the array elements.
100      * @param <R>       the type of the result.
101      * @param <A>       the intermediate accumulation type of the {@code Collector}.
102      * @param collector the {@code Collector} describing the reduction.
103      * @param array     The array, assumed to be unmodified during use.
104      * @return the result of the reduction
105      * @see Stream#collect(Collector)
106      * @see Arrays#stream(Object[])
107      * @see Collectors
108      * @since 3.16.0
109      */
110     public static <T, R, A> R collect(final Collector<? super T, A, R> collector, final T... array) {
111         return Arrays.stream(array).collect(collector);
112     }
113 
114     /**
115      * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, in encounter order.
116      * <p>
117      * This is a variation of {@link Collectors#joining()} that works with any element class, not just {@code CharSequence}.
118      * </p>
119      * <p>
120      * For example:
121      * </p>
122      *
123      * <pre>
124      * Stream.of(Long.valueOf(1), Long.valueOf(2), Long.valueOf(3))
125      *    .collect(LangCollectors.joining())
126      * returns "123"
127      * </pre>
128      *
129      * @return A {@code Collector} which concatenates Object elements, separated by the specified delimiter, in encounter order.
130      */
131     public static Collector<Object, ?, String> joining() {
132         return new SimpleCollector<>(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString, CH_NOID);
133     }
134 
135     /**
136      * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, in encounter order.
137      * <p>
138      * This is a variation of {@link Collectors#joining(CharSequence)} that works with any element class, not just {@code CharSequence}.
139      * </p>
140      * <p>
141      * For example:
142      * </p>
143      *
144      * <pre>
145      * Stream.of(Long.valueOf(1), Long.valueOf(2), Long.valueOf(3))
146      *   .collect(LangCollectors.joining("-"))
147      * returns "1-2-3"
148      * </pre>
149      *
150      * @param delimiter the delimiter to be used between each element.
151      * @return A {@code Collector} which concatenates Object elements, separated by the specified delimiter, in encounter order.
152      */
153     public static Collector<Object, ?, String> joining(final CharSequence delimiter) {
154         return joining(delimiter, StringUtils.EMPTY, StringUtils.EMPTY);
155     }
156 
157     /**
158      * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, with the
159      * specified prefix and suffix, in encounter order.
160      * <p>
161      * This is a variation of {@link Collectors#joining(CharSequence, CharSequence, CharSequence)} that works with any
162      * element class, not just {@code CharSequence}.
163      * </p>
164      * <p>
165      * For example:
166      * </p>
167      *
168      * <pre>
169      * Stream.of(Long.valueOf(1), Long.valueOf(2), Long.valueOf(3))
170      *   .collect(LangCollectors.joining("-", "[", "]"))
171      * returns "[1-2-3]"
172      * </pre>
173      *
174      * @param delimiter the delimiter to be used between each element
175      * @param prefix the sequence of characters to be used at the beginning of the joined result
176      * @param suffix the sequence of characters to be used at the end of the joined result
177      * @return A {@code Collector} which concatenates CharSequence elements, separated by the specified delimiter, in
178      *         encounter order
179      */
180     public static Collector<Object, ?, String> joining(final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix) {
181         return joining(delimiter, prefix, suffix, Objects::toString);
182     }
183 
184     /**
185      * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, with the specified prefix and suffix, in
186      * encounter order.
187      * <p>
188      * This is a variation of {@link Collectors#joining(CharSequence, CharSequence, CharSequence)} that works with any element class, not just
189      * {@code CharSequence}.
190      * </p>
191      * <p>
192      * For example:
193      * </p>
194      *
195      * <pre>{@code
196      * Stream.of(Long.valueOf(1), null, Long.valueOf(3))
197      *   .collect(LangCollectors.joining("-", "[", "]", o -> Objects.toString(o, "NUL")))
198      * returns "[1-NUL-3]"
199      * }</pre>
200      *
201      * @param delimiter the delimiter to be used between each element
202      * @param prefix    the sequence of characters to be used at the beginning of the joined result
203      * @param suffix    the sequence of characters to be used at the end of the joined result
204      * @param toString  A function that takes an Object and returns a non-null String.
205      * @return A {@code Collector} which concatenates CharSequence elements, separated by the specified delimiter, in encounter order
206      */
207     public static Collector<Object, ?, String> joining(final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix,
208         final Function<Object, String> toString) {
209         return new SimpleCollector<>(() -> new StringJoiner(delimiter, prefix, suffix), (a, t) -> a.add(toString.apply(t)), StringJoiner::merge,
210             StringJoiner::toString, CH_NOID);
211     }
212 
213     private LangCollectors() {
214         // No instance
215     }
216 
217 }