Let's learn Java8 together (7) - Stream (middle)

In 一起来学Java8(七)——Stream(上)this, we learned about the common methods and usage of Stream objects. Now let's take a closer look at Stream.collect()the use of the following method

basic usage of collect

collect means collection, it collects and summarizes the elements in the 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());
	}

}

In this example, the product name is returned through the map method, and then all the product names are put into the List object.

Looking at the source code, it is found that 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);
  • Method 2:
<R, A> R collect(Collector<? super T, A, R> collector);

The most used method is method 2. This method can be regarded as a shortcut to method 1. Because Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combinerthese three parameters are also provided in Collector, it is not difficult to guess that the corresponding implementation of method 1 is still used at the bottom layer.

We can start from collect(Collector<? super T, A, R> collector)the beginning, and then slowly understand the usage of method 1 through this.

Collectors

Stream.collect(Collector<? super T, A, R> collector)The parameter Collector object of the method is mainly provided by the Collectorsclass. The Collectors class contains a series of static methods to return Collector objects. The commonly used methods are shown in the following list:

method name describe
averagingXX average
counting Find the number of elements in a set
groupingBy group the collection
joining concatenate set elements
mapping Value mapping can be done again during the grouping process
maxBy find the maximum value
my city find the minimum value
partitioningBy partition elements
reducing induction
summarizingXX Summary
toCollection Convert to collection object
toConcurrentMap Convert to ConcurrentMap
toList Convert to List
toMap Convert to Map
toSet Convert to Set

The benefits of each method are explained below.

averagingXX

averagingXX includes averagingDouble, averagingInt, averagingLong. They mean averaging.

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);

Their parameter is a functional interface, which can be written using a lambda expression, where the parameters in the lambda expression are the elements in the Stream, and the returned value is the value to be averaged. The following example is to find the average value of commodities:

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("商品的平均价格:" + avgPrice);

summingXX

Similar to averagingXX, the summingXX method is used to sum the values ​​of the elements in the 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);

Print:

summingInt:6.0
summingLong:61.0
summingDouble:0.6

counting()

counting() returns the number of elements in the collection.

long count = Stream.of(1,2,3,4,5)
		.collect(Collectors.counting());
System.out.println("count:" + count); // 5

summarizingXX

The above mentioned averagingXX (for averaging), summingXX (for summing), and counting (for total number). If I want to obtain these three numbers at the same time, what should I do? SummarizingXX can be used.

IntSummaryStatistics summarizingInt = Stream.of(1, 2, 3)
				.collect(Collectors.summarizingInt(val -> val));
System.out.println("平均值:" + summarizingInt.getAverage());
System.out.println("总个数:" + summarizingInt.getCount());
System.out.println("总和:" + summarizingInt.getSum());
System.out.println("最大值:" + summarizingInt.getMax());
System.out.println("最小值:" + summarizingInt.getMin());

Print:

平均值:2.0
总个数:3
总和:6
最大值:3
最小值:1

summarizingInt puts the statistical results into an IntSummaryStatistics object, where different statistical information can be obtained.

groupingBy()

groupingBy() is to group the elements in the collection and consists of three overloaded methods

  • Overload 1: groupingBy(Function)
  • Overload 2: groupingBy(Function, Collector)
  • 重载3: groupingBy(Function, Supplier, Collector)

Among them, overload 1 calls overload 2, and overload 2 calls overload 3, so it will eventually be executed in overload 3.

First, let's look at groupingBy(Function)the usage of overload 1. This method is grouped into a new List by default. The following example groups the product types, and puts the same type of products into a List.

@Data
@AllArgsConstructor
static class Goods {
	private String goodsName;
	// 类型,1:手机,2:电脑
	private int type;
	@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)
			);
	
	Map<Integer, List<Goods>> goodsListMap = list.stream()
		.collect(Collectors.groupingBy(Goods::getType));
	goodsListMap.forEach((key, value) -> {
		System.out.println("类型" + key + ":" + value);
	});
}

Print:

类型1:[iphoneX, mate30 pro]
类型2:[thinkpad T400, macbook pro]

As mentioned above , it groupingBy(Function)is actually called groupingBy(Function, Collector), and the second parameter Collectordetermines where to convert. The default is toList(), see groupingBy(Function)the source code:

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

So we can call to groupingBy(Function, Collector)manually specify the Collector. Suppose we want to put the transformed elements into the Set, we can write:

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

Looking at the source code of the overload 2 method, I found that it 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);
    }

Which Goods::getTypecorresponds to classifier, Collectors.toSet()corresponding to downstream. The meaning of the parameter in the middle HashMap::newis obvious, that is, which is the specific implementation class of the returned Map. If you want to change it to LinkedHashMap, you can write it like this:

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

This is exactly how overload 3 is used.

The groupingByConcurrent method in Collectors is based on overload 3, and the code in the middle is changed 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());
    }

The Collector parameter in the groupingBy method can not only be toList(), toSet(), but also has more flexible usage. Previously, we converted all Map<Integer, List<Goods>>forms, and the value stored in the collection object. If you don’t want so many attributes, only If we want the commodity name in the object, that is to say, we want to get Map<Integer, List<String>>it, where the key is the commodity type, and the value is the commodity name collection.

This time Collectors.mapping()it comes in handy, we use the groupingBy(Function, Collector)method, the second parameter is passedCollectors.mapping()

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

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

joining

The joining method can join elements in a Stream.

List<String> list = Arrays.asList("hello", "world");
String str = list.stream().collect(Collectors.joining());
System.out.println(str); // 打印:helloworld

Delimiters can also be specified:

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

In addition, the Stringclass provides a join method, the function is the same

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

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("最贵的商品:" + maxPriceGoods);
}

The above example demonstrates finding the most expensive item. The Collectors.maxBy() method needs to pass in a comparator, which needs to be compared according to the price of the item.

In the same way, to find the cheapest item, just maxByreplace it with minBy.

partitioningBy

The partitioningBy method represents partitioning. It will divide the elements in the Stream into two parts according to the conditions, and put them into a Map respectively. The key of the Map is Boolean type, the key is true, and the part that satisfies the condition is stored, and the key is false to store the unsatisfied elements. element of the condition.

{
    true -> 符合条件的元素
    false -> 不符合条件的元素
}

The partitioningBy method consists of two overloaded methods

  • Overload 1: partitioningBy(Predicate)
  • Overload 2: partitioningBy(Predicate, Collector)

Among them, overload 1 will call overload 2, so the overload 2 method is finally called. Let's look at the overload 1 method first.

The following example divides the products into mobile phone products and non-mobile phone products according to the product type.

@Data
@AllArgsConstructor
static class Goods {
	private String goodsName;
	// 类型,1:手机,2:电脑
	private int type;
	@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)
			);
	
	// 手机归为一类,非手机商品归为一类
	// true -> 手机类商品
	// false -> 非手机类商品
	Map<Boolean, List<Goods>> goodsMap = list.stream()
		.collect(
		    Collectors.partitioningBy(goods -> goods.getType() == 1)
		);
	// 获取手机类商品
	List<Goods> mobileGoods = goodsMap.get(true);
	System.out.println(mobileGoods);
}

The second parameter of the partitioningBy(Predicate, Collector) method can be used to specify the collection elements, which are stored in List by default. If you want to use Set storage, you can write:

Map<Boolean, Set<Goods>> goodsMap = list.stream()
	.collect(
		Collectors.partitioningBy(
			goods -> goods.getType() == 1
			// 指定收集类型
			, Collectors.toSet())
	);

toList & toSet & toCollection

toList and toSet can convert elements in Stream into List and Set collections, which are the two most used 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());

By default, toList returns ArrayList, and toSet returns HashSet. If you want to return other types of collections, such as LinkedList, you can use them toCollection. It allows developers to specify which collection they need.

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

toConcurrentMap

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

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

Among them, overload 1 calls overload 2, overload 2 calls overload 3, and eventually the overload 3 method is executed.

First look at overload 1, which provides two parameters

  • keyMapper: Specify the key value in ConcurrentMap
  • valueMapper: specifies the value corresponding to the key

The following example uses the name of the product as the key and the price as the 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);

Print:

{mate30 pro=5999, iphoneX=4000, redmek20=2999}

Note: This method requires that the key cannot be repeated. If there is a repeated key, an IllegalStateException will be thrown. If there is a repeated key, it needs to be used toConcurrentMap(Function, Function, BinaryOperator), that is, overload 2

Let's look at overload 2: toConcurrentMap(Function, Function, BinaryOperator), the first two parameters of this method are the same as overload 1, and the third parameter is used to deal with key conflicts, allowing developers to choose a value to return.

List<Goods> list = Arrays.asList(
		new Goods("iphoneX", 4000)
		, new Goods("mate30 pro", 5999)
		, new Goods("mate30 pro", 6000) // 这里有两个冲突了
		, 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) {
						// 选择价格贵的返回
						return Math.max(price1, price2);
					}
				})
		);
System.out.println(goodsMap);

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

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

Finally, look at overload 3. Compared with overload 2, there is one more parameter Supplier, which allows developers to specify a returnConcurrentMap

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

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

subsection

This article mainly explains the Stream.collectusage and Collectorsthe use of static methods in the class. In the next article, we will explain reducethe related usage in detail.

Regularly share technical dry goods, learn together, and progress together! WeChat public account: Ape knocks the moon under the code

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324210208&siteId=291194637