Stream API's use Java8

Foreword

The I'd like to introduce Java Stream API usage, recently doing a new project, then finally jump out from the pit old ancestral code for the project came. After the completion of the project set up by the company's own framework, I was thinking about the JDK version also upgraded about it (before the project, will be able to use the highest JDK7), but later found that the company's project to deploy packaged platform only supports up to JDK8. Well, since it supports to JDK8, but also to meet the daily needs (to Han bike), upgrade to JDK8 later, after you build the project architecture, he began to write some basic logic. Which it uses some of JDK8 of Stream. But I look at my colleagues in the code that can not read the time. Indeed, I have to admit this, Lambda expressions Although the code is simple, but not with people will think it's not a very good readability. So this time it is combined with his experience to tell us about some of the features of Java Stream.

Traversal operation from the Stream

Oracle Corporation on March 18, 2014 release Java 8, Java8 mainly to increase the ability of functional programming on the basis of the original object-oriented. This appeared Lambda expressions used in Java, a function as an argument passed to a method. Java8 is a typical example of Stream, Stream API Java programmers can greatly improve the productivity of programmers to write efficient, clean, simple code.

example:

List<Integer> numbers = new ArrayList<>();
numbers.add(3);
numbers.add(4);
numbers.add(8);
numbers.add(16);
numbers.add(19);
numbers.add(27);
numbers.add(23);
numbers.add(99);
numbers.add(15);
numbers.add(32);
numbers.add(5);
numbers.add(232);
numbers.add(56);
int count = 0;
for(Integer i:numbers){
if(i>20){
count++;
}
}
System.out.println("count:"+count);

As traversal API code into the Stream to achieve the following:

long count = numbers.stream().filter(i->i>20).count();
System.out.println("count:"+count);

Stream line with normal traversal can be achieved.

The following is an implementation flow chart Stream API.

Java code is converted into

Integer transactionsIds =
                roomList.stream()
                        .filter(b -> b.getLength() == 10)
                        .sorted((x,y) -> x.getHigh() - y.getHigh())
                        .mapToInt(Room::getWidth).sum();

Creating Stream

Arrays.stream()

When faced with in their daily programming is an array, you can use Arrays.stream () method to use Stream

Integer[] array = new Integer[]{3,4,8,16,19,27,23,99,76,232,33,96};
long count = Arrays.stream(array).filter(i->i>20).count();

Stream.of()

When the face of the array may be used in addition to Arrays.stream () method, the array may also be used Stream Stream need to turn. This method not only support incoming array, the array will turn into Stream, also supports the argument list to pass, will eventually turn into a parameter Stream

Integer[] array = new Integer[]{3,4,8,16,19,27,23,99,76,232,33,96};
long count = Stream.of(array).filter(i->i>20).count();
long sum = Stream.of(12,77,59,3,654).filter(i->i>20).mapToInt(Integer::intValue).sum();
System.out.println("count:"+count+",sum:"+sum);

In fact Stream.of () is called Stream.of () method to achieve.

Stream.generate()

Stream interface has two static methods to create unlimited Stream of. generate () method accepts a parameter of the function, you can use something like the following code to create a Stream you need.

Stream<String> stream = Stream.generate(() -> "test").limit(10);
String[] strArr = stream.toArray(String[]::new);
System.out.println(Arrays.toString(strArr));

operation result

[test, test, test, test, test, test, test, test, test, test]

Stream.iterate()

Stream interface is another to iterate () method to create a static method of infinite Stream. iterate () method also accepts a parameter of the function, you can use code like the following to create a Stream you need.

Stream<BigInteger> bigIntStream = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.TEN)).limit(10);
BigInteger[] bigIntArr = bigIntStream.toArray(BigInteger[]::new);
System.out.println(Arrays.toString(bigIntArr));

operation result

[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]

Collection.stream()

This is the most common of the Stream. Because the parent interface Collection is a set of interfaces in Java, Java collection of inherited or implements this interface. So the collection in Java can use this method to create a Stream;

      /**
        * @see     Set
        * @see     List
        * @see     Map
        * @see     SortedSet
        * @see     SortedMap
        * @see     HashSet
        * @see     TreeSet
        * @see     ArrayList
        * @see     LinkedList
        * @see     Vector
        * @see     Collections
        * @see     Arrays
        * @see     AbstractCollection
        * @since 1.2
        */
        public interface Collection<E> extends Iterable<E> {
            /**
             * Returns a sequential {@code Stream} with this collection as its source.
             *
             * <p>This method should be overridden when the {@link #spliterator()}
             * method cannot return a spliterator that is {@code IMMUTABLE},
             * {@code CONCURRENT}, or <em>late-binding</em>. (See {@link #spliterator()}
             * for details.)
             *
             * @implSpec
             * The default implementation creates a sequential {@code Stream} from the
             * collection's {@code Spliterator}.
             *
             * @return a sequential {@code Stream} over the elements in this collection
             * @since 1.8
             */
            default Stream<E> stream() {
                return StreamSupport.stream(spliterator(), false);
            }
        }

example

List<Integer> numbers = new ArrayList<>();
numbers.add(3);
numbers.add(4);
numbers.add(8);
numbers.add(16);   
numbers.stream().forEach(number->{
    System.out.println(number);
});

StreamSupport.stream()

By looking at Collection.stream () method, we can see, Colleciton.stream () is actually called StreamSupport.stream () to achieve. So we can also use StreamSupport.stream () to create a Stream. When we are faced with it is an iterator when using StreamSupport.stream () can create a Stream. The first parameter is an iterator passed, the second parameter is representative of true to use parallel processing. false means to deal with serial Stream.

List<Integer> numbers = new ArrayList<>();
numbers.add(
3); numbers.add(4); numbers.add(8); numbers.add(16); numbers.add(19); numbers.add(27); numbers.add(23); Spliterator<Integer> integers = numbers.spliterator(); StreamSupport.stream(integers,false).forEach(number->{   System.out.println(number); });

Commutations

filter method

Can tell from the name, this is a conversion filter Stream This method will generate a new stream, which contains all the elements fits a certain criteria.

List<Integer> integerList = Lists.newArrayList();
integerList.add(15);
integerList.add(32);
integerList.add(5);
integerList.add(232);
integerList.add(56);
List<Integer> after = integerList.stream()
                    .filter(i->i>50)
                    .collect(Collectors.toList());

System.out.println(after);

operation result:

[232, 56]

map method

The method refers to a map value stream some form of conversion. A need to convert it to a function as a parameter.

List<Integer> integerList = Lists.newArrayList();
integerList.add(15);
integerList.add(32);
integerList.add(5);
integerList.add(232);
integerList.add(56);
//将Integer类型转换成String类型
List<String> afterString = integerList.stream()
                .map(i->String.valueOf(i)).collect(Collectors.toList());
System.out.println(afterString);

flatMap method

When the above stream conversion performed by the map method, a function is applied to each element, and returns the value to collect a new stream. However, if there is a function that returns a value that is not, but a stream comprising a plurality of values. But you need is a set of elements of multiple streams contain.

E.g

List <Integer> = oneList Lists.newArrayList (), 
twoList Lists.newArrayList = ();
oneList.add (34 is);
oneList.add (23 is);
oneList.add (87);

twoList.add (29);
twoList. the Add (48);
twoList.add (92);
the Map <String, List <Integer >> testMap Maps.newHashMap = ();
testMap.put ( ". 1", oneList);
testMap.put ( "2", twoList) ;
// it returns a collection of streams, but all I need is a collection of List <Integer>
List <stream <Integer >> testlist = testMap.values () stream ().
                    .map (number-> number.stream . ()) collect (Collectors.toList ( ));

This time should be used flatMap multiple streams are combined, and then collected in a collection.

List<Integer> testList = testMap.values().stream()
                .flatMap(number->number.stream()).collect(Collectors.toList());

limit method and skip method

limit (n) method returns a new stream containing n elements (if n is less than the total length of the original stream is returned).

List<Integer> myList = Lists.newArrayList();
myList.add(1);
myList.add(2);
myList.add(3);
myList.add(4);
myList.add(5);
myList.add(6);
List<Integer> afterLimit = myList.stream().limit(4).collect(Collectors.toList());
System.out.println("afterLimit:"+afterLimit);

Skip (n) the opposite method, it will discard the first n elements.

List<Integer> afterSkip = myList.stream().skip(4).collect(Collectors.toList());
System.out.println("afterSkip:"+afterSkip);

operation result:

afterLimit:[1, 2, 3, 4]
afterSkip:[5, 6]

Used in conjunction with limit and skip method can be achieved daily pagination:

List<Integer> pageList = myList.stream()
                  .skip(pageNumber*pageSize)
                  .limit(pageSize).collect(Collectors.toList());

distinct methods and sorted methods

Stream conversion method described above are stateless. When an element that is taken from a stream has been converted, the results are not dependent on the previous element. In addition there are two methods are dependent on the flow required prior to the conversion of elements in the stream. A distinct method is a method is sorted.

distinct method returns the elements from the original stream has a same order to eliminate duplication stream element, the element read operation it is obviously prior to remember.

List<Integer> myTestList = Lists.newArrayList();
myTestList.add(10);
myTestList.add(39);
myTestList.add(10);
myTestList.add(78);
myTestList.add(10);
List<Integer> distinctList = myTestList.stream()
                        .distinct().collect(Collectors.toList());
System.out.println("distinctList:"+distinctList);

operation result:

distinctList:[10, 39, 78]

sorted method is the need to traverse the entire stream, and generating element before any sort it. Because the first element of the collection of the sort likely to be the last one in the collection of unsorted.

List<Integer> myTestList = Lists.newArrayList();
myTestList.add(39);
myTestList.add(78);
myTestList.add(10);
myTestList.add(22);
myTestList.add(56);
List<Integer> sortList = myTestList.stream()
                .sorted(Integer::compareTo).collect(Collectors.toList()); System.out.println(
"sortList:"+sortList);

operation result:

sortList: [10, 22, 39, 56, 78]

Polymerization operation

The foregoing has described the flow of the creation and conversion, the flow of the polymerization described below, aggregation refers to aggregation as a stream value for use in the program. The method of operation is to terminate the polymerization.

The method of max and min Method

The code used in the previous examples count method and sum methods belong to flow from the polymerization process. There are two methods of polymerization is max method and min methods , respectively, the maximum and minimum of the return stream.

 
 
List <Integer> = hearList Lists.newArrayList (); 
hearList.add (15);
hearList.add (32);
hearList.add (5);
hearList.add (232);
hearList.add (56);
hearList.add (29);
hearList.add (94);
Integer = maxItem hearList.stream (). Max (Integer :: compareTo) .get ();
Integer = minItem hearList.stream (). Min (Integer :: compareTo) .get ();
System.out.println ( "max" + + maxItem "poor" + minItem);

operation result:

max: 232, min: 5

findFirst method

findFirst method returns a first value is non-empty, it is usually used together in combination with the filter method.

List <Integer> = hearList Lists.newArrayList (); 
hearList.add (15);
hearList.add (32);
hearList.add (5);
hearList.add (232);
hearList.add (56);
hearList.add (29);
hearList.add (104);
First hearList.stream Integer = (). Filter (i-> i> 100) .findFirst (). Get ();

findAny method

findAny method can be set as long as any of the elements found in a matched, return, this method is very effective (no fragments are found in the first matching element end of the calculation, the serial stream and returned as findFirst) executed in parallel convection .

Integer anyItem = hearList.parallelStream().filter(i->i>100).findAny().get();

anyMatch method

anyMatch method may determine whether the set of elements also match. The result is a boolean value.

boolean isHas = hearList.parallelStream().anyMatch(i->i>100);

allMatch method and noneMatch method

allMatch method and noneMatch method returns true when all the elements and matched elements do not match, respectively.

boolean allHas hearList.parallelStream = (). Allmatch (i-> i> 100 );
boolean noHas hearList.parallelStream = (). noneMatch (i-> i> 100);

Although these methods will always check the entire flow, but can still improve the speed through parallel execution. 

reduce method

reduce method is a method flow elements for further calculations.

List <Integer> = hearList Lists.newArrayList (); 
hearList.add ( 15 ); 
hearList.add ( 32 ); 
hearList.add ( 5 ); 
hearList.add ( 232 ); 
hearList.add ( 56 ); 
hearList.add ( 29 ); 
hearList.add ( 104 );
// 求和 
Integer hearList.stream = sum (). Reduce ((x, y) -> x + y) .get (); 
System.out.println ( "Sum:" + sum);
// 简化一下,求和 
sum = hearList.stream (). Reduce (Integer :: sum) .get (); 
System.out.println ( "Sum:" + sum);
//Comprising the initial identification, the summation 
SUM = hearList.stream () the reduce (0, (X, Y) -> + X. Y); 
System.out.println ( "SUM:" + SUM);
 // length of the element summing ((total, y) -. > total + y.toString () length (), similar to an accumulator, is called repeatedly) 
SUM = hearList.stream () the reduce (0, (Total, Y. ) -> + y.toString Total () length (), (total1, total2) -> + total1. total2); 
System.out.println ( "SUM:" + SUM);
 // simplify it, the length of the element is evaluated with. 
= SUM . hearList.stream () Map (Objects :: toString) .mapToInt (:: String length) .sum (); 
System.out.println ( "SUM:" SUM +);

operation result

sum:473
sum:473
sum:473
sum:15
sum:15

Collect results

Once processed streams, usually want to look at the results, rather than as a polymerization their value. Collectorts class provides us with a commonly used method of collecting various factory class.

Collection to collection

Examples of the foregoing example with a flow collector To a List, only need be written.

List<Integer> thereList = hereList.stream().collect(Collectors.toList());

Set to be collected in this way with

Set<Integer> thereSet = hereList.stream().collect(Collectors.toSet());

Set the time to collect, control Set type, it can be.

TreeSet<Integer> treeSet = hereList.stream()
                    .collect(Collectors.toCollection(TreeSet::new));

splice

The string concatenation word stream and collected.

String resultString = stringList.stream().collect(Collectors.joining());

When the string concatenation stream and collected, to add a separator element in the intermediary, to transfer a joining method.

String resultString = stringList.stream().collect(Collectors.joining(","));

When the stream is not a string of elements you need to first flow stream then spliced ​​into a string.

String hereResultString = hereList.stream()
                .map(String::valueOf).collect(Collectors.joining(","));

Collection polymerization

The sum of streams were collected, the average, maximum or minimum.

List<Integer> hereList = Lists.newArrayList();
hereList.add(15);
hereList.add(32);
hereList.add(5);
hereList.add(232);
hereList.add(56);
hereList.add(29);
hereList.add(104);

//总和、平均值,最大值,最小值
int sum = hereList.stream().collect(Collectors.summingInt(Integer::intValue));
Double ave = hereList.stream().collect(Collectors.averagingInt(Integer::intValue));
Integer max = hereList.stream().collect(Collectors.maxBy(Integer::compare)).get();
Integer min = hereList.stream().collect(Collectors.minBy(Integer::compare)).get();
System.out.println("sum:"+sum+",ave:"+ave+",max:"+max+",min:"+min);

operation result:

sum:473,ave:67.57142857142857,max:232,min:5

The results were collected in a disposable, polymeric object a sum, average, maximum or minimum.

IntSummaryStatistics summaryStatistics = hereList.stream()
                          .collect(Collectors.summarizingInt(Integer::intValue)); System.out.println(summaryStatistics);

operation result:

IntSummaryStatistics{count=7, sum=473, min=5, average=67.571429, max=232}

The result set is collected Map

When we want to collect elements of the collection to the Map, you can use Collectors.toMap method. This method has two parameters, to generate Map key and value.

Room for example, a high target value as the width as the key

Map<Integer,Integer> hwMap = roomList.stream()
                        .collect(Collectors.toMap(Room::getHigh, Room::getWidth));

In particular it generally the case as the value of the multi-element, may be used Function.identity () to get the actual element.

Map<Integer,Room> roomMap = roomList.stream()
                        .collect(Collectors.toMap(Room::getHigh, Function.identity()));

If multiple elements have the same key, it will result in the collection of java.lang.IllegalStateException throw an exception. May be used to solve the third parameter, the third parameter is used to determine when a key conflict occurs, the process the results, a reservation only if the collision occurs, and when the key retention value is already present, is in the following manner.

Map<Integer,Room> rMap = roomList.stream()
                .collect(Collectors.toMap(Room::getHigh, Function.identity(),(nowValue,newValue)->nowValue));

If you want to specify the type Map generated, you also need the third argument.

TreeMap<Integer,Room> roomTreeMap = roomList.stream()
                .collect(Collectors.toMap(Room::getHigh, 
            Function.identity(),(nowValue,newValue)
->newValue,TreeMap::new));

Note: Each toMap method, there will be a corresponding toConCurrentMap method used to generate a concurrent Map.

Packet fragmentation

In one set, the value of the group with the same characteristics is a common feature in the Stream API also provides a corresponding method.

Packet

Or the above example, a collection of objects in accordance with the height Room packet.

List<Room> roomList = Lists.newArrayList(
new Room(11,23,56),
new Room(11,84,48),
new Room(22,46,112),
new Room(22,75,62),
new Room(22,56,75),
new Room(33,92,224));

Map<Integer,List<Room>> groupMap = roomList.stream().collect(Collectors.groupingBy(Room::getHigh));
System.out.println("groupMap:"+groupMap);

operation result:

groupMap:{33=[Room(high=33, width=92, length=224)], 
22=[Room(high=22, width=46, length=112), Room(high=22, width=75, length=62), Room(high=22, width=56, length=75)],
11=[Room(high=11, width=23, length=56), Room(high=11, width=84, length=48)]}

Fragmentation 

When the classification function is a function returns a Boolean value, the stream is divided into two list elements: Returns a set of elements is set to true, another set of false set of elements is returned. This applies partitoningBy method is more efficient than groupingBy.

For example, we will set of a room divided into two groups, one group is the height of the room 22, the other is the other room.

Map<Boolean,List<Room>> partitionMap = roomList.stream()
                .collect(Collectors.partitioningBy(room->room.getHigh()==22));

operation result:

partitionMap:{false=[Room(high=11, width=23, length=56), Room(high=11, width=84, length=48), Room(high=33, width=92, length=224)],
true=[Room(high=22, width=46, length=112), Room(high=22, width=75, length=62), Room(high=22, width=56, length=75)]}

extensions

These methods to introduce the following features, either groupingBy method or partitioningBy methods are supported.

counting method returns the total number of elements of the collection.

Map<Integer,Long> countMap = roomList.stream()
           .collect(Collectors.groupingBy(Room::getHigh,Collectors.counting()));

summing (Int | Long | Double) method accepts a parameter value as a function to calculate the sum.

Map<Integer,Integer> sumMap = roomList.stream().
                collect(Collectors.groupingBy(Room::getHigh,Collectors.summingInt(Room::getWidth)));

maxBy methods and methods minBy receiving comparator as the parameters to calculate maximum and minimum values.

Remove the packet width maximum and minimum room.

Map<Integer, Optional<Room>> maxMap = roomList.stream().
                collect(Collectors.groupingBy(Room::getHigh,
                        Collectors.maxBy(Comparator.comparing(Room::getWidth))
                ));
Map<Integer, Optional<Room>> minMap = roomList.stream().
                collect(Collectors.groupingBy(Room::getHigh,
                        Collectors.maxBy(Comparator.comparing(Room::getWidth))
                ));

System.out.println("maxMap:"+ JSON.toJSONString(maxMap));
System.out.println("minMap:"+JSON.toJSONString(minMap));

operation result:

maxMap:{33:{"high":33,"length":224,"width":92},22:{"high":22,"length":62,"width":75},11:{"high":11,"length":48,"width":84}}
minMap:{33:{"high":33,"length":224,"width":92},22:{"high":22,"length":62,"width":75},11:{"high":11,"length":48,"width":84}}

mapping method will result to another collector.

The maximum width of the packet width room removed.

Map<Integer, Optional<Integer>> collect = roomList.stream().collect(Collectors.groupingBy(Room::getHigh,
                Collectors.mapping(Room::getWidth,
                        Collectors.maxBy(Comparator.comparing(Integer::valueOf)))));

System.out.println("collect:"+JSON.toJSONString(collect));

运行结果:

collect:{33:92,22:75,11:84}

无论groupingBy或是mapping函数,如果返回类型是int、long、double都可以将元素收集到一个summarystatistics对象中,然后从每组的summarystatistics对象中取出函数值的总和、平均值、总数、最大值和最小值。

Map<Integer,IntSummaryStatistics> summaryStatisticsMap = roomList.stream()
                .collect(Collectors.groupingBy(Room::getHigh,
                Collectors.summarizingInt(Room::getWidth)));

System.out.println("summaryStatisticsMap:"+summaryStatisticsMap);

运行结果:

summaryStatisticsMap:{33=IntSummaryStatistics{count=1, sum=92, min=92, average=92.000000, max=92}, 
22=IntSummaryStatistics{count=3, sum=177, min=46, average=59.000000, max=75},
11=IntSummaryStatistics{count=2, sum=107, min=23, average=53.500000, max=84}}

多级分组

上面的例子我们都是按一个条件进行的一级分组,其实groupingBy是支持多级分组的。

例如第一级我们将房间按照高度分组,第二级按照宽度分组。

Map<Integer,Map<Integer,List<Room>>> multistageMap = roomList.stream().collect(
          Collectors.groupingBy(Room::getHigh,Collectors.groupingBy(Room::getWidth))); System.out.println(
"multistageMap:"+JSON.toJSONString(multistageMap));

运行结果:

{
    "11": {
        "23": [
            {"high": 11,"length": 56,"width": 23}
        ],
        "84": [
            {"high": 11,"length": 48,"width": 84}
        ]
    },
    "22": {
        "46": [
            {"high": 22,"length": 112,"width": 46}
        ],
        "56": [
            {"high": 22,"length": 75,"width": 56}
        ],
        "75": [
            {"high": 22,"length": 62,"width": 75}
        ]
    },
    "33": {
        "92": [
            {"high": 33,"length": 224,"width": 92}
        ]
    }
}

并行流

Stream的建立,使得并行计算变得容易,但是并行流在使用的时候也是需要注意的。

首先,必须是一个并行流,只要在终止方法执行时,流处于并行模式,那么所有的流操作就都会并行执行。

Stream.of(roomList).parallel();

parallel方法可以将任意的串行流转换为一个并行流。

其次要确保传递给并行流操作的函数是线程安全的。

int[] words = new int[23];
Stream.of(roomList).parallel().forEach(s->{
     if(s.size()<10){
           words[s.size()]++;
     }
});

上面这个例子中的代码就是错误的,传递给并行流的操作并不是线程安全的。可以改为AtomicInteger的对象数组来作为计数器。

我们使在处理集合数据量较大的时候才能体现出并行流的优势,并且目的是为了在保证线程安全的情况下,提升效率,利用多核CPU的资源。

 

小扩展

使用Stream的API时,在遍历或处理流的过程中当引用外部变量的时候会默认的将变量当成fianl变量来处理。所以有些同学就会觉得在遍历的过程中取不出来集合的索引。其实可以换一种思想可以只遍历集合索引,然后在遍历中取值。

IntStream.range(0,roomList.size()).forEach(i->{
       System.out.println(roomList.get(i));
});

 

 

 

 

文章会同步到我的公众号上面,欢迎关注。

 

Guess you like

Origin www.cnblogs.com/jimoer/p/10995574.html