In Java8 (7) — Stream (1), we learned about the common methods and usage of Stream objects. Let’s take a closer look at the use of the stream.collect () method

Collect basic expressions

Collect means collect, which collects and summarizes the elements in Stream and returns a new collection object. Let’s start with a simple example:

public class CollectTest {

	@Data
	@AllArgsConstructor
	static class Goods {
		private String goodsName;
		private int price;
	}
	
	public static void main(String[] args) {
		List<Goods> list = Arrays.asList(
				new Goods("iphoneX".4000),new Goods("mate30 pro".5999),new Goods("redmek20".2999)); List<String> nameList = list.stream() .map(Goods::getGoodsName) .collect(Collectors.toList()); }}Copy the code

In this example, the name of the item is returned using the Map method, and all of the item names are placed in a List object.

The collect method consists of two overloaded methods.

  • Method 1:
<R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);
Copy the code
  • Method 2:
<R, A> R collect(Collector<? super T, A, R> collector);
Copy the code

The most commonly used method is method 2, which is a shortcut to method 1 because the Collector also provides Supplier

Supplier, BiConsumer

accumulator, BiConsumer

combiner.
,>
,?>

Collect (Collector
collector) and use this to learn more about method 1.

Collectors

Stream.collect(Collector
Collector) method parameters Collector objects are provided by the Collectors class. The Collectors class contains a series of static methods used to return a Collector object. The following lists the common methods:

Method names describe
averagingXX averaging
counting Find the number of elements in the set
groupingBy Group collections
joining Concatenate collection elements
mapping Values can be mapped again during grouping
maxBy For maximum
minBy For the minimum
partitioningBy Partition elements
reducing inductive
summarizingXX summary
toCollection To a collection object
toConcurrentMap Convert the ConcurrentMap
toList Into the List
toMap Into the Map
toSet Into the Set

Here’s how each method works in turn.

averagingXX

AveragingXX includes averagingDouble, averagingInt, averagingLong. They mean taking an average.

double averagingInt = Stream.of(1, 2, 3)
		.collect(Collectors.averagingInt(val -> val));
System.out.println("averagingInt:" + averagingInt);

double averagingLong = Stream.of(10L, 21L, 30L)
		.collect(Collectors.averagingLong(val -> val));
System.out.println("averagingLong:"+ averagingLong); Double averagingDouble = Stream) of (0.1, 0.2, 0.3). Collect (Collectors. AveragingDouble (val - > val)); System.out.println("averagingDouble:" + averagingDouble);
Copy the code

Their argument is a functional interface that can be written using Lambda expressions, where the argument is the element in Stream and returns the value to be averaged. The following example is to find the average of goods:

List<Goods> list = Arrays.asList(
				new Goods("iphoneX".4000),new Goods("mate30 pro".5999),new Goods("redmek20".2999));double avgPrice = list.stream()
	.collect(Collectors.averagingInt(goods -> goods.getPrice()));
System.out.println("Average price of goods:" + avgPrice);
Copy the code

summingXX

Similar to averagingXX, the summingXX method is used to find the sum of the values of the elements in a collection.

double summingInt = Stream.of(1, 2, 3)
		.collect(Collectors.summingInt(val -> val));
System.out.println("summingInt:" + summingInt);

double summingLong = Stream.of(10L, 21L, 30L)
		.collect(Collectors.summingLong(val -> val));
System.out.println("summingLong:"+ summingLong); Double summingDouble = stream. of(0.1, 0.2, 0.3). Collect (Collectors. SummingDouble (val -> val)); System.out.println("summingDouble:" + summingDouble);
Copy the code

Print:

SummingDouble: 61.0 summingLong summingInt: 6.0:0.6Copy the code

counting()

Counting () returns the number of elements in the set.

long count = Stream.of(1.2.3.4.5)
		.collect(Collectors.counting());
System.out.println("count:" + count); / / 5
Copy the code

summarizingXX

What do I do if I want to get the summarizingXX, summingXX, and counting at the same time? I can use summarizingXX.

IntSummaryStatistics summarizingInt = Stream.of(1.2.3)
				.collect(Collectors.summarizingInt(val -> val));
System.out.println("Average :" + summarizingInt.getAverage());
System.out.println("Total number :" + summarizingInt.getCount());
System.out.println("The sum." + summarizingInt.getSum());
System.out.println("Maximum value :" + summarizingInt.getMax());
System.out.println("Minimum :" + summarizingInt.getMin());
Copy the code

Print:

Average value :2.0 Total Numbers :3 Total number :6 Maximum value :3 Minimum value :1Copy the code

SummarizingInt puts the statistics into an IntSummaryStatistics object, where you can get different statistics.

groupingBy()

GroupingBy () groups the elements in a collection and consists of three overloaded methods

  • Overloading 1: groupingBy (Function)
  • Reload 2: groupingBy(Function, Collector)
  • Reload 3: groupingBy(Function, Supplier, Collector)

So overload 1 calls overload 2, and overload 2 calls overload 3, so they’re all going to be executed in overload 3.

Let’s take a look at the use of the 1groupingBy(Function) overload. This method groups new lists by default. This example groups items of the same type into a List.

@Data
@AllArgsConstructor
static class Goods {
	private String goodsName;
	// Type, 1: mobile phone, 2: computer
	private int type;
	@Override
	public String toString(a) {
		returngoodsName; }}public static void main(String[] args) {
	List<Goods> list = Arrays.asList(
			new Goods("iphoneX".1),new Goods("mate30 pro".1),new Goods("thinkpad T400".2),new Goods("macbook pro".2)); Map<Integer, List<Goods>> goodsListMap = list.stream() .collect(Collectors.groupingBy(Goods::getType)); goodsListMap.forEach((key, value) -> { System.out.println("Type" + key + ":" + value);
	});
}
Copy the code

Print:

Type 1:[iphoneX, Mate30 Pro] Type 2:[ThinkPad T400, macbook Pro]Copy the code

GroupingBy (Function) calls groupingBy(Function, Collector), where the second parameter Collector determines where to convert, default is toList(), GroupingBy (Function)

public static<T, K> Collector<T, ? , Map<K, List<T>>> groupingBy(Function<?super T, ? extends K> classifier) {
        return groupingBy(classifier, toList());
    }
Copy the code

So we can manually specify the Collector by calling groupingBy(Function, Collector). Suppose we want to place the converted elements in a Set, we could say:

Map<Integer, Set<Goods>> goodsListMap = list.stream()
        .collect(Collectors.groupingBy(Goods::getType, Collectors.toSet()));
Copy the code

The overload 2 method calls overload 3:

public static<T, K, A, D> Collector<T, ? , Map<K, D>> groupingBy(Function<?super T, ? extends K> classifier,
                                          Collector<? super T, A, D> downstream) {
        return groupingBy(classifier, HashMap::new, downstream);
    }
Copy the code

Goods::getType corresponds to classifier, and Collectors. ToSet () corresponds to downstream. HashMap::new = LinkedHashMap = HashMap::new = HashMap::new = HashMap::new = HashMap::new = HashMap::new = HashMap::new = LinkedHashMap

LinkedHashMap<Integer, Set<Goods>> goodsListMap = list.stream()
        .collect(Collectors.groupingBy(Goods::getType, LinkedHashMap::new, Collectors.toSet()));
        
Copy the code

This is exactly how overload 3 is used.

The groupingByConcurrent method in Collectors is based on overload 3, with the code changed to ConcurrentHashMap::new.

public static<T, K> Collector<T, ? , ConcurrentMap<K, List<T>>> groupingByConcurrent(Function<?super T, ? extends K> classifier) {
        return groupingByConcurrent(classifier, ConcurrentHashMap::new, toList());
    }
Copy the code

The Collector parameter in the groupingBy method can be used not only toList() and toSet(), but also can be used more easily. Previously, we converted Map

>, and value to store collection objects. If we don’t want that many attributes, we just want the name of the item in the object, that is, we want Map

>, where key is the type of the item and value is the set of item names.
,>
,>

The groupingBy(Function, Collector) method is used. The second parameter is passed to Collectors.

Map<Integer, List<String>> goodsListMap = 
list.stream()
	.collect(
		 Collectors.groupingBy(
		    Goods::getType, 
		    Collectors.mapping(Goods::getGoodsName, Collectors.toList())
		 )
	);
Copy the code

The mapping() method takes two parameters. The first parameter specifies which attribute to return, and the second parameter specifies which collection to return.

joining

The joining method concatenates elements in a Stream.

List<String> list = Arrays.asList("hello"."world");
String str = list.stream().collect(Collectors.joining());
System.out.println(str); // Print: helloWorld
Copy the code

You can also specify delimiters:

List<String> list = Arrays.asList("hello"."world");
String str = list.stream().collect(Collectors.joining(","));
System.out.println(str); // Print: hello,world
Copy the code

In addition, the String class provides a join method that does the same thing

String str2 = String.join(",", list);
System.out.println(str2);
Copy the code

maxBy&minBy

  • MaxBy: Find the largest element in the Stream
@Data
@AllArgsConstructor
static class Goods {
	private String goodsName;
	private int price;
}

public static void main(String[] args) {
	List<Goods> list = Arrays.asList(
			new Goods("iphoneX".4000),new Goods("mate30 pro".5999),new Goods("redmek20".2999)); Goods maxPriceGoods = list.stream() .collect( Collectors.maxBy( Comparator.comparing(Goods::getPrice) ) ) .orElse(null);
	System.out.println("Most expensive item:" + maxPriceGoods);
}
Copy the code

The example above demonstrates that finding the most expensive item, the Collectors. MaxBy () method requires passing in a comparator that compares the item based on its price.

Similarly, to find the cheapest item, replace maxBy with minBy.

partitioningBy

The partitioningBy method represents a partition. It divides the elements in the Stream into two parts based on the criteria and places them in a Map. The Map has a Boolean key of true for the elements that meet the criteria and a Boolean key for the elements that do not meet the criteria.

{
    true-> Qualifying elementsfalse-> Unqualified element}Copy the code

The partitioningBy method consists of two overloaded methods

  • Reload 1: partitioningBy(Predicate)
  • Reloading 2: partitioningBy(Predicate, Collector)

So overload 1 calls overload 2, so we end up calling overload 2 anyway, so let’s look at overload 1.

The following example divides goods into mobile and non-mobile goods based on the type of goods.

@Data @AllArgsConstructor static class Goods { private String goodsName; // Type, 1: mobile phone, 2: computer private inttype;
	@Override
	public String toString() {
		return goodsName;
	}
}

public static void main(String[] args) {
	List<Goods> list = Arrays.asList(
			new Goods("iphoneX", 1)
			, new Goods("mate30 pro", 1)
			, new Goods("thinkpad T400", 2)
			, new Goods("macbook pro", 2)); // Mobile phones fall into the same category and non-mobile goods fall into the same categorytrue-> Mobile phone products //falseMap<Boolean, List<Goods>> goodsMap = list.stream() .collect( Collectors.partitioningBy(goods -> goods.getType() == 1) ); List<Goods> mobileGoods = goodsmap.get (true);
	System.out.println(mobileGoods);
}
Copy the code

The second argument to the partitioningBy(Predicate, Collector) method can be used to specify the collection elements. By default, the List is used for storage. If you want to use Set, you can write:

Map<Boolean, Working Set < Goods > > goodsMap = list. The stream () collect (Collectors. PartitioningBy (Goods - > Goods. GetType () = = 1 / / specified collection types, Collectors.toSet()) );Copy the code

toList & toSet & toCollection

ToList and toSet convert elements in a Stream to lists and sets. These are two of the more common methods.

Stream<Goods> stream = Stream.of(
		new Goods("iphoneX".4000),new Goods("mate30 pro".5999),new Goods("redmek20".2999)); List<Goods> list = stream.collect(Collectors.toList()); Set<Goods> set = stream.collect(Collectors.toSet());Copy the code

By default, toList returns ArrayList, toSet returns HashSet, and toCollection can be used to return other types of collections, such as LinkedList, which allows the developer to specify which collection is required.

LinkedList<Goods> linkedList = stream.collect(Collectors.toCollection(LinkedList::new));
Copy the code

toConcurrentMap

The toConcurrentMap method converts a Stream to a ConcurrentMap, which consists of three overloaded methods

  • Overloading 1:toConcurrentMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper)
  • Overloading 2:toConcurrentMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction)
  • Overloading 3:toConcurrentMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)

Overload 1 calls overload 2, and overload 2 calls overload 3, and eventually overload 3 gets executed.

Overload 1 provides two arguments

  • KeyMapper: specifies the key value in ConcurrentMap
  • ValueMapper: Specifies the value corresponding to the key

The following example uses the name of the item as key and the price as value

List<Goods> list = Arrays.asList(
		new Goods("iphoneX".4000),new Goods("mate30 pro".5999),new Goods("redmek20".2999)); ConcurrentMap<String, Integer> goodsMap = list.stream() .collect( Collectors.toConcurrentMap(Goods::getGoodsName, Goods::getPrice) ); System.out.println(goodsMap);Copy the code

Print:

{mate30 pro=5999, iphoneX=4000, redmek20=2999}
Copy the code

If there are duplicate keys, IllegalStateException is thrown. If there are duplicate keys, IllegalStateException is throwntoConcurrentMap(Function, Function, BinaryOperator)So reload 2

ToConcurrentMap (Function, Function, BinaryOperator); toConcurrentMap(Function, Function, BinaryOperator); toConcurrentMap(Function, BinaryOperator);

List<Goods> list = Arrays.asList(
		new Goods("iphoneX".4000),new Goods("mate30 pro".5999),new Goods("mate30 pro".6000) // There are two conflicts
		, new Goods("redmek20".2999)); ConcurrentMap<String, Integer> goodsMap = list.stream() .collect( Collectors.toConcurrentMap(Goods::getGoodsName, Goods::getPrice,new BinaryOperator<Integer>() {
					@Override
					public Integer apply(Integer price1, Integer price2) {
						// Select the expensive return
						returnMath.max(price1, price2); }})); System.out.println(goodsMap);Copy the code

Print: {mate30 pro=6000, iphoneX=4000, Redmek20 =2999}

In this example, mate30 Pro is repeated as the key. In the BinaryOperator, we select the one with the highest price to return.

Finally, overload 3 adds an extra parameter to overload 2, which allows the developer to specify that a ConcurrentMap is returned

Overload 2 calls overload 3, which uses ConcurrentMap::new by default.

Note: The fourth parameter must be ConcurrentMap or a subclass of ConcurrentMap

section

This article focuses on stream. collect and the use of static methods in Collectors. In the next article, we will explain reduce in more detail.

Regularly share technical dry goods, learn together, progress together! Wechat official account: Monkey Knock on the moon code