973 return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_NOID);
974 }
975 }
976
977 /**
978 * Returns a concurrent {@code Collector} implementing a "group by"
979 * operation on input elements of type {@code T}, grouping elements
980 * according to a classification function.
981 *
982 * <p>This is a {@link Collector.Characteristics#CONCURRENT concurrent} and
983 * {@link Collector.Characteristics#UNORDERED unordered} Collector.
984 *
985 * <p>The classification function maps elements to some key type {@code K}.
986 * The collector produces a {@code ConcurrentMap<K, List<T>>} whose keys are the
987 * values resulting from applying the classification function to the input
988 * elements, and whose corresponding values are {@code List}s containing the
989 * input elements which map to the associated key under the classification
990 * function.
991 *
992 * <p>There are no guarantees on the type, mutability, or serializability
993 * of the {@code Map} or {@code List} objects returned, or of the
994 * thread-safety of the {@code List} objects returned.
995 * @implSpec
996 * This produces a result similar to:
997 * <pre>{@code
998 * groupingByConcurrent(classifier, toList());
999 * }</pre>
1000 *
1001 * @param <T> the type of the input elements
1002 * @param <K> the type of the keys
1003 * @param classifier a classifier function mapping input elements to keys
1004 * @return a concurrent, unordered {@code Collector} implementing the group-by operation
1005 *
1006 * @see #groupingBy(Function)
1007 * @see #groupingByConcurrent(Function, Collector)
1008 * @see #groupingByConcurrent(Function, Supplier, Collector)
1009 */
1010 public static <T, K>
1011 Collector<T, ?, ConcurrentMap<K, List<T>>>
1012 groupingByConcurrent(Function<? super T, ? extends K> classifier) {
1013 return groupingByConcurrent(classifier, ConcurrentHashMap::new, toList());
1014 }
1015
1016 /**
1017 * Returns a concurrent {@code Collector} implementing a cascaded "group by"
1018 * operation on input elements of type {@code T}, grouping elements
1019 * according to a classification function, and then performing a reduction
1020 * operation on the values associated with a given key using the specified
1021 * downstream {@code Collector}.
1022 *
1023 * <p>This is a {@link Collector.Characteristics#CONCURRENT concurrent} and
1024 * {@link Collector.Characteristics#UNORDERED unordered} Collector.
1025 *
1026 * <p>The classification function maps elements to some key type {@code K}.
1027 * The downstream collector operates on elements of type {@code T} and
1028 * produces a result of type {@code D}. The resulting collector produces a
1029 * {@code Map<K, D>}.
1030 *
1031 * <p>For example, to compute the set of last names of people in each city,
1032 * where the city names are sorted:
1033 * <pre>{@code
1034 * ConcurrentMap<City, Set<String>> namesByCity
1035 * = people.stream().collect(groupingByConcurrent(Person::getCity,
1036 * mapping(Person::getLastName, toSet())));
1037 * }</pre>
1038 *
1039 * @param <T> the type of the input elements
1040 * @param <K> the type of the keys
1041 * @param <A> the intermediate accumulation type of the downstream collector
1042 * @param <D> the result type of the downstream reduction
1043 * @param classifier a classifier function mapping input elements to keys
1044 * @param downstream a {@code Collector} implementing the downstream reduction
1045 * @return a concurrent, unordered {@code Collector} implementing the cascaded group-by operation
1046 *
1047 * @see #groupingBy(Function, Collector)
1048 * @see #groupingByConcurrent(Function)
1049 * @see #groupingByConcurrent(Function, Supplier, Collector)
1050 */
1126 }
1127 else {
1128 @SuppressWarnings("unchecked")
1129 Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher();
1130 Function<ConcurrentMap<K, A>, M> finisher = intermediate -> {
1131 intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));
1132 @SuppressWarnings("unchecked")
1133 M castResult = (M) intermediate;
1134 return castResult;
1135 };
1136 return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_CONCURRENT_NOID);
1137 }
1138 }
1139
1140 /**
1141 * Returns a {@code Collector} which partitions the input elements according
1142 * to a {@code Predicate}, and organizes them into a
1143 * {@code Map<Boolean, List<T>>}.
1144 *
1145 * There are no guarantees on the type, mutability,
1146 * serializability, or thread-safety of the {@code Map} returned.
1147 *
1148 * @param <T> the type of the input elements
1149 * @param predicate a predicate used for classifying input elements
1150 * @return a {@code Collector} implementing the partitioning operation
1151 *
1152 * @see #partitioningBy(Predicate, Collector)
1153 */
1154 public static <T>
1155 Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
1156 return partitioningBy(predicate, toList());
1157 }
1158
1159 /**
1160 * Returns a {@code Collector} which partitions the input elements according
1161 * to a {@code Predicate}, reduces the values in each partition according to
1162 * another {@code Collector}, and organizes them into a
1163 * {@code Map<Boolean, D>} whose values are the result of the downstream
1164 * reduction.
1165 *
1166 * <p>There are no guarantees on the type, mutability,
1195 }
1196 else {
1197 Function<Partition<A>, Map<Boolean, D>> finisher = par ->
1198 new Partition<>(downstream.finisher().apply(par.forTrue),
1199 downstream.finisher().apply(par.forFalse));
1200 return new CollectorImpl<>(supplier, accumulator, merger, finisher, CH_NOID);
1201 }
1202 }
1203
1204 /**
1205 * Returns a {@code Collector} that accumulates elements into a
1206 * {@code Map} whose keys and values are the result of applying the provided
1207 * mapping functions to the input elements.
1208 *
1209 * <p>If the mapped keys contains duplicates (according to
1210 * {@link Object#equals(Object)}), an {@code IllegalStateException} is
1211 * thrown when the collection operation is performed. If the mapped keys
1212 * may have duplicates, use {@link #toMap(Function, Function, BinaryOperator)}
1213 * instead.
1214 *
1215 * @apiNote
1216 * It is common for either the key or the value to be the input elements.
1217 * In this case, the utility method
1218 * {@link java.util.function.Function#identity()} may be helpful.
1219 * For example, the following produces a {@code Map} mapping
1220 * students to their grade point average:
1221 * <pre>{@code
1222 * Map<Student, Double> studentToGPA
1223 * students.stream().collect(toMap(Function.identity(),
1224 * student -> computeGPA(student)));
1225 * }</pre>
1226 * And the following produces a {@code Map} mapping a unique identifier to
1227 * students:
1228 * <pre>{@code
1229 * Map<String, Student> studentIdToStudent
1230 * students.stream().collect(toMap(Student::getId,
1231 * Function.identity());
1232 * }</pre>
1233 *
1234 * @implNote
1254 */
1255 public static <T, K, U>
1256 Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
1257 Function<? super T, ? extends U> valueMapper) {
1258 return new CollectorImpl<>(HashMap::new,
1259 uniqKeysMapAccumulator(keyMapper, valueMapper),
1260 uniqKeysMapMerger(),
1261 CH_ID);
1262 }
1263
1264 /**
1265 * Returns a {@code Collector} that accumulates elements into a
1266 * {@code Map} whose keys and values are the result of applying the provided
1267 * mapping functions to the input elements.
1268 *
1269 * <p>If the mapped
1270 * keys contains duplicates (according to {@link Object#equals(Object)}),
1271 * the value mapping function is applied to each equal element, and the
1272 * results are merged using the provided merging function.
1273 *
1274 * @apiNote
1275 * There are multiple ways to deal with collisions between multiple elements
1276 * mapping to the same key. The other forms of {@code toMap} simply use
1277 * a merge function that throws unconditionally, but you can easily write
1278 * more flexible merge policies. For example, if you have a stream
1279 * of {@code Person}, and you want to produce a "phone book" mapping name to
1280 * address, but it is possible that two persons have the same name, you can
1281 * do as follows to gracefully deals with these collisions, and produce a
1282 * {@code Map} mapping names to a concatenated list of addresses:
1283 * <pre>{@code
1284 * Map<String, String> phoneBook
1285 * people.stream().collect(toMap(Person::getName,
1286 * Person::getAddress,
1287 * (s, a) -> s + ", " + a));
1288 * }</pre>
1289 *
1290 * @implNote
1291 * The returned {@code Collector} is not concurrent. For parallel stream
1292 * pipelines, the {@code combiner} function operates by merging the keys
1293 * from one map into another, which can be an expensive operation. If it is
1365 Function<? super T, ? extends U> valueMapper,
1366 BinaryOperator<U> mergeFunction,
1367 Supplier<M> mapSupplier) {
1368 BiConsumer<M, T> accumulator
1369 = (map, element) -> map.merge(keyMapper.apply(element),
1370 valueMapper.apply(element), mergeFunction);
1371 return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
1372 }
1373
1374 /**
1375 * Returns a concurrent {@code Collector} that accumulates elements into a
1376 * {@code ConcurrentMap} whose keys and values are the result of applying
1377 * the provided mapping functions to the input elements.
1378 *
1379 * <p>If the mapped keys contains duplicates (according to
1380 * {@link Object#equals(Object)}), an {@code IllegalStateException} is
1381 * thrown when the collection operation is performed. If the mapped keys
1382 * may have duplicates, use
1383 * {@link #toConcurrentMap(Function, Function, BinaryOperator)} instead.
1384 *
1385 * @apiNote
1386 * It is common for either the key or the value to be the input elements.
1387 * In this case, the utility method
1388 * {@link java.util.function.Function#identity()} may be helpful.
1389 * For example, the following produces a {@code Map} mapping
1390 * students to their grade point average:
1391 * <pre>{@code
1392 * Map<Student, Double> studentToGPA
1393 * students.stream().collect(toMap(Function.identity(),
1394 * student -> computeGPA(student)));
1395 * }</pre>
1396 * And the following produces a {@code Map} mapping a unique identifier to
1397 * students:
1398 * <pre>{@code
1399 * Map<String, Student> studentIdToStudent
1400 * students.stream().collect(toConcurrentMap(Student::getId,
1401 * Function.identity());
1402 * }</pre>
1403 *
1404 * <p>This is a {@link Collector.Characteristics#CONCURRENT concurrent} and
1418 * @see #toConcurrentMap(Function, Function, BinaryOperator)
1419 * @see #toConcurrentMap(Function, Function, BinaryOperator, Supplier)
1420 */
1421 public static <T, K, U>
1422 Collector<T, ?, ConcurrentMap<K,U>> toConcurrentMap(Function<? super T, ? extends K> keyMapper,
1423 Function<? super T, ? extends U> valueMapper) {
1424 return new CollectorImpl<>(ConcurrentHashMap::new,
1425 uniqKeysMapAccumulator(keyMapper, valueMapper),
1426 uniqKeysMapMerger(),
1427 CH_CONCURRENT_ID);
1428 }
1429
1430 /**
1431 * Returns a concurrent {@code Collector} that accumulates elements into a
1432 * {@code ConcurrentMap} whose keys and values are the result of applying
1433 * the provided mapping functions to the input elements.
1434 *
1435 * <p>If the mapped keys contains duplicates (according to {@link Object#equals(Object)}),
1436 * the value mapping function is applied to each equal element, and the
1437 * results are merged using the provided merging function.
1438 *
1439 * @apiNote
1440 * There are multiple ways to deal with collisions between multiple elements
1441 * mapping to the same key. The other forms of {@code toConcurrentMap} simply use
1442 * a merge function that throws unconditionally, but you can easily write
1443 * more flexible merge policies. For example, if you have a stream
1444 * of {@code Person}, and you want to produce a "phone book" mapping name to
1445 * address, but it is possible that two persons have the same name, you can
1446 * do as follows to gracefully deals with these collisions, and produce a
1447 * {@code Map} mapping names to a concatenated list of addresses:
1448 * <pre>{@code
1449 * Map<String, String> phoneBook
1450 * people.stream().collect(toConcurrentMap(Person::getName,
1451 * Person::getAddress,
1452 * (s, a) -> s + ", " + a));
1453 * }</pre>
1454 *
1455 * <p>This is a {@link Collector.Characteristics#CONCURRENT concurrent} and
1456 * {@link Collector.Characteristics#UNORDERED unordered} Collector.
1457 *
|
973 return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_NOID);
974 }
975 }
976
977 /**
978 * Returns a concurrent {@code Collector} implementing a "group by"
979 * operation on input elements of type {@code T}, grouping elements
980 * according to a classification function.
981 *
982 * <p>This is a {@link Collector.Characteristics#CONCURRENT concurrent} and
983 * {@link Collector.Characteristics#UNORDERED unordered} Collector.
984 *
985 * <p>The classification function maps elements to some key type {@code K}.
986 * The collector produces a {@code ConcurrentMap<K, List<T>>} whose keys are the
987 * values resulting from applying the classification function to the input
988 * elements, and whose corresponding values are {@code List}s containing the
989 * input elements which map to the associated key under the classification
990 * function.
991 *
992 * <p>There are no guarantees on the type, mutability, or serializability
993 * of the {@code ConcurrentMap} or {@code List} objects returned, or of the
994 * thread-safety of the {@code List} objects returned.
995 * @implSpec
996 * This produces a result similar to:
997 * <pre>{@code
998 * groupingByConcurrent(classifier, toList());
999 * }</pre>
1000 *
1001 * @param <T> the type of the input elements
1002 * @param <K> the type of the keys
1003 * @param classifier a classifier function mapping input elements to keys
1004 * @return a concurrent, unordered {@code Collector} implementing the group-by operation
1005 *
1006 * @see #groupingBy(Function)
1007 * @see #groupingByConcurrent(Function, Collector)
1008 * @see #groupingByConcurrent(Function, Supplier, Collector)
1009 */
1010 public static <T, K>
1011 Collector<T, ?, ConcurrentMap<K, List<T>>>
1012 groupingByConcurrent(Function<? super T, ? extends K> classifier) {
1013 return groupingByConcurrent(classifier, ConcurrentHashMap::new, toList());
1014 }
1015
1016 /**
1017 * Returns a concurrent {@code Collector} implementing a cascaded "group by"
1018 * operation on input elements of type {@code T}, grouping elements
1019 * according to a classification function, and then performing a reduction
1020 * operation on the values associated with a given key using the specified
1021 * downstream {@code Collector}.
1022 *
1023 * <p>This is a {@link Collector.Characteristics#CONCURRENT concurrent} and
1024 * {@link Collector.Characteristics#UNORDERED unordered} Collector.
1025 *
1026 * <p>The classification function maps elements to some key type {@code K}.
1027 * The downstream collector operates on elements of type {@code T} and
1028 * produces a result of type {@code D}. The resulting collector produces a
1029 * {@code Map<K, D>}.
1030 *
1031 * <p>There are no guarantees on the type, mutability, or serializability
1032 * of the {@code ConcurrentMap} returned.
1033 *
1034 * <p>For example, to compute the set of last names of people in each city,
1035 * where the city names are sorted:
1036 * <pre>{@code
1037 * ConcurrentMap<City, Set<String>> namesByCity
1038 * = people.stream().collect(groupingByConcurrent(Person::getCity,
1039 * mapping(Person::getLastName, toSet())));
1040 * }</pre>
1041 *
1042 * @param <T> the type of the input elements
1043 * @param <K> the type of the keys
1044 * @param <A> the intermediate accumulation type of the downstream collector
1045 * @param <D> the result type of the downstream reduction
1046 * @param classifier a classifier function mapping input elements to keys
1047 * @param downstream a {@code Collector} implementing the downstream reduction
1048 * @return a concurrent, unordered {@code Collector} implementing the cascaded group-by operation
1049 *
1050 * @see #groupingBy(Function, Collector)
1051 * @see #groupingByConcurrent(Function)
1052 * @see #groupingByConcurrent(Function, Supplier, Collector)
1053 */
1129 }
1130 else {
1131 @SuppressWarnings("unchecked")
1132 Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher();
1133 Function<ConcurrentMap<K, A>, M> finisher = intermediate -> {
1134 intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));
1135 @SuppressWarnings("unchecked")
1136 M castResult = (M) intermediate;
1137 return castResult;
1138 };
1139 return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_CONCURRENT_NOID);
1140 }
1141 }
1142
1143 /**
1144 * Returns a {@code Collector} which partitions the input elements according
1145 * to a {@code Predicate}, and organizes them into a
1146 * {@code Map<Boolean, List<T>>}.
1147 *
1148 * There are no guarantees on the type, mutability,
1149 * serializability, or thread-safety of the {@code Map} or {@code List}
1150 * returned.
1151 *
1152 * @param <T> the type of the input elements
1153 * @param predicate a predicate used for classifying input elements
1154 * @return a {@code Collector} implementing the partitioning operation
1155 *
1156 * @see #partitioningBy(Predicate, Collector)
1157 */
1158 public static <T>
1159 Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
1160 return partitioningBy(predicate, toList());
1161 }
1162
1163 /**
1164 * Returns a {@code Collector} which partitions the input elements according
1165 * to a {@code Predicate}, reduces the values in each partition according to
1166 * another {@code Collector}, and organizes them into a
1167 * {@code Map<Boolean, D>} whose values are the result of the downstream
1168 * reduction.
1169 *
1170 * <p>There are no guarantees on the type, mutability,
1199 }
1200 else {
1201 Function<Partition<A>, Map<Boolean, D>> finisher = par ->
1202 new Partition<>(downstream.finisher().apply(par.forTrue),
1203 downstream.finisher().apply(par.forFalse));
1204 return new CollectorImpl<>(supplier, accumulator, merger, finisher, CH_NOID);
1205 }
1206 }
1207
1208 /**
1209 * Returns a {@code Collector} that accumulates elements into a
1210 * {@code Map} whose keys and values are the result of applying the provided
1211 * mapping functions to the input elements.
1212 *
1213 * <p>If the mapped keys contains duplicates (according to
1214 * {@link Object#equals(Object)}), an {@code IllegalStateException} is
1215 * thrown when the collection operation is performed. If the mapped keys
1216 * may have duplicates, use {@link #toMap(Function, Function, BinaryOperator)}
1217 * instead.
1218 *
1219 * <p>There are no guarantees on the type, mutability, serializability,
1220 * or thread-safety of the {@code Map} returned.
1221 *
1222 * @apiNote
1223 * It is common for either the key or the value to be the input elements.
1224 * In this case, the utility method
1225 * {@link java.util.function.Function#identity()} may be helpful.
1226 * For example, the following produces a {@code Map} mapping
1227 * students to their grade point average:
1228 * <pre>{@code
1229 * Map<Student, Double> studentToGPA
1230 * students.stream().collect(toMap(Function.identity(),
1231 * student -> computeGPA(student)));
1232 * }</pre>
1233 * And the following produces a {@code Map} mapping a unique identifier to
1234 * students:
1235 * <pre>{@code
1236 * Map<String, Student> studentIdToStudent
1237 * students.stream().collect(toMap(Student::getId,
1238 * Function.identity());
1239 * }</pre>
1240 *
1241 * @implNote
1261 */
1262 public static <T, K, U>
1263 Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
1264 Function<? super T, ? extends U> valueMapper) {
1265 return new CollectorImpl<>(HashMap::new,
1266 uniqKeysMapAccumulator(keyMapper, valueMapper),
1267 uniqKeysMapMerger(),
1268 CH_ID);
1269 }
1270
1271 /**
1272 * Returns a {@code Collector} that accumulates elements into a
1273 * {@code Map} whose keys and values are the result of applying the provided
1274 * mapping functions to the input elements.
1275 *
1276 * <p>If the mapped
1277 * keys contains duplicates (according to {@link Object#equals(Object)}),
1278 * the value mapping function is applied to each equal element, and the
1279 * results are merged using the provided merging function.
1280 *
1281 * <p>There are no guarantees on the type, mutability, serializability,
1282 * or thread-safety of the {@code Map} returned.
1283 *
1284 * @apiNote
1285 * There are multiple ways to deal with collisions between multiple elements
1286 * mapping to the same key. The other forms of {@code toMap} simply use
1287 * a merge function that throws unconditionally, but you can easily write
1288 * more flexible merge policies. For example, if you have a stream
1289 * of {@code Person}, and you want to produce a "phone book" mapping name to
1290 * address, but it is possible that two persons have the same name, you can
1291 * do as follows to gracefully deals with these collisions, and produce a
1292 * {@code Map} mapping names to a concatenated list of addresses:
1293 * <pre>{@code
1294 * Map<String, String> phoneBook
1295 * people.stream().collect(toMap(Person::getName,
1296 * Person::getAddress,
1297 * (s, a) -> s + ", " + a));
1298 * }</pre>
1299 *
1300 * @implNote
1301 * The returned {@code Collector} is not concurrent. For parallel stream
1302 * pipelines, the {@code combiner} function operates by merging the keys
1303 * from one map into another, which can be an expensive operation. If it is
1375 Function<? super T, ? extends U> valueMapper,
1376 BinaryOperator<U> mergeFunction,
1377 Supplier<M> mapSupplier) {
1378 BiConsumer<M, T> accumulator
1379 = (map, element) -> map.merge(keyMapper.apply(element),
1380 valueMapper.apply(element), mergeFunction);
1381 return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
1382 }
1383
1384 /**
1385 * Returns a concurrent {@code Collector} that accumulates elements into a
1386 * {@code ConcurrentMap} whose keys and values are the result of applying
1387 * the provided mapping functions to the input elements.
1388 *
1389 * <p>If the mapped keys contains duplicates (according to
1390 * {@link Object#equals(Object)}), an {@code IllegalStateException} is
1391 * thrown when the collection operation is performed. If the mapped keys
1392 * may have duplicates, use
1393 * {@link #toConcurrentMap(Function, Function, BinaryOperator)} instead.
1394 *
1395 * <p>There are no guarantees on the type, mutability, or serializability
1396 * of the {@code ConcurrentMap} returned.
1397 *
1398 * @apiNote
1399 * It is common for either the key or the value to be the input elements.
1400 * In this case, the utility method
1401 * {@link java.util.function.Function#identity()} may be helpful.
1402 * For example, the following produces a {@code Map} mapping
1403 * students to their grade point average:
1404 * <pre>{@code
1405 * Map<Student, Double> studentToGPA
1406 * students.stream().collect(toMap(Function.identity(),
1407 * student -> computeGPA(student)));
1408 * }</pre>
1409 * And the following produces a {@code Map} mapping a unique identifier to
1410 * students:
1411 * <pre>{@code
1412 * Map<String, Student> studentIdToStudent
1413 * students.stream().collect(toConcurrentMap(Student::getId,
1414 * Function.identity());
1415 * }</pre>
1416 *
1417 * <p>This is a {@link Collector.Characteristics#CONCURRENT concurrent} and
1431 * @see #toConcurrentMap(Function, Function, BinaryOperator)
1432 * @see #toConcurrentMap(Function, Function, BinaryOperator, Supplier)
1433 */
1434 public static <T, K, U>
1435 Collector<T, ?, ConcurrentMap<K,U>> toConcurrentMap(Function<? super T, ? extends K> keyMapper,
1436 Function<? super T, ? extends U> valueMapper) {
1437 return new CollectorImpl<>(ConcurrentHashMap::new,
1438 uniqKeysMapAccumulator(keyMapper, valueMapper),
1439 uniqKeysMapMerger(),
1440 CH_CONCURRENT_ID);
1441 }
1442
1443 /**
1444 * Returns a concurrent {@code Collector} that accumulates elements into a
1445 * {@code ConcurrentMap} whose keys and values are the result of applying
1446 * the provided mapping functions to the input elements.
1447 *
1448 * <p>If the mapped keys contains duplicates (according to {@link Object#equals(Object)}),
1449 * the value mapping function is applied to each equal element, and the
1450 * results are merged using the provided merging function.
1451 *
1452 * <p>There are no guarantees on the type, mutability, or serializability
1453 * of the {@code ConcurrentMap} returned.
1454 *
1455 * @apiNote
1456 * There are multiple ways to deal with collisions between multiple elements
1457 * mapping to the same key. The other forms of {@code toConcurrentMap} simply use
1458 * a merge function that throws unconditionally, but you can easily write
1459 * more flexible merge policies. For example, if you have a stream
1460 * of {@code Person}, and you want to produce a "phone book" mapping name to
1461 * address, but it is possible that two persons have the same name, you can
1462 * do as follows to gracefully deals with these collisions, and produce a
1463 * {@code Map} mapping names to a concatenated list of addresses:
1464 * <pre>{@code
1465 * Map<String, String> phoneBook
1466 * people.stream().collect(toConcurrentMap(Person::getName,
1467 * Person::getAddress,
1468 * (s, a) -> s + ", " + a));
1469 * }</pre>
1470 *
1471 * <p>This is a {@link Collector.Characteristics#CONCURRENT concurrent} and
1472 * {@link Collector.Characteristics#UNORDERED unordered} Collector.
1473 *
|