001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.lang3.stream; 019 020import java.util.Arrays; 021import java.util.Collections; 022import java.util.Objects; 023import java.util.Set; 024import java.util.StringJoiner; 025import java.util.function.BiConsumer; 026import java.util.function.BinaryOperator; 027import java.util.function.Function; 028import java.util.function.Supplier; 029import java.util.stream.Collector; 030import java.util.stream.Collectors; 031import java.util.stream.Stream; 032 033import org.apache.commons.lang3.StringUtils; 034 035/** 036 * Implementations of {@link Collector} that implement various reduction operations. 037 * <p> 038 * This class is called {@code LangCollectors} instead of {@code Collectors} to avoid clashes with {@link Collectors}. 039 * </p> 040 * 041 * @since 3.13.0 042 */ 043public final class LangCollectors { 044 045 /** 046 * Simple implementation class for {@code Collector}. 047 * 048 * @param <T> the type of elements to be collected 049 * @param <R> the type of the result 050 */ 051 private static final class SimpleCollector<T, A, R> implements Collector<T, A, R> { 052 053 private final BiConsumer<A, T> accumulator; 054 private final Set<Characteristics> characteristics; 055 private final BinaryOperator<A> combiner; 056 private final Function<A, R> finisher; 057 private final Supplier<A> supplier; 058 059 private SimpleCollector(final Supplier<A> supplier, final BiConsumer<A, T> accumulator, final BinaryOperator<A> combiner, final Function<A, R> finisher, 060 final Set<Characteristics> characteristics) { 061 this.supplier = supplier; 062 this.accumulator = accumulator; 063 this.combiner = combiner; 064 this.finisher = finisher; 065 this.characteristics = characteristics; 066 } 067 068 @Override 069 public BiConsumer<A, T> accumulator() { 070 return accumulator; 071 } 072 073 @Override 074 public Set<Characteristics> characteristics() { 075 return characteristics; 076 } 077 078 @Override 079 public BinaryOperator<A> combiner() { 080 return combiner; 081 } 082 083 @Override 084 public Function<A, R> finisher() { 085 return finisher; 086 } 087 088 @Override 089 public Supplier<A> supplier() { 090 return supplier; 091 } 092 } 093 094 private static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet(); 095 096 /** 097 * Delegates to {@link Stream#collect(Collector)} for a Stream on the given array. 098 * 099 * @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}