Java 8 API添加了一个新的抽象称为流 Stream,可以让你以一种声明的方式处理数据。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果
(1)数据源
(2)支持聚合操作
(3)Pipelining
(4)内部迭代
(5)延迟执行
(1)使用循环遍历
如果是处理简单数据,进行简单操作,使用循环遍历集合,并没有什么不妥,但是如果是复杂的大型的数据使用循环不仅会使处理效率变得底下,而且会降低代码的可读性
例如下面代码:查找符合条件的学生
class Student implements Comparable<Student> {
int id;
String name;
String className;
int grade;
public Student(int id, String name, String className, int grade) {...}
@Override
public String toString() {...}
//使用成绩决定排名
@Override
public int compareTo(Student o) {
return this.grade-o.grade;
}
}
public class StreamTest {
public static void main(String[] args) {
LinkedList<Student> studentList = new LinkedList<Student>();
studentList.add(new Student(1, "Alice", "1班", 100));
studentList.add(new Student(2, "Bob", "2班", 85));
studentList.add(new Student(3, "Coco", "2班", 46));
studentList.add(new Student(4, "Dav", "3班", 95));
studentList.add(new Student(5, "Elizabeth", "4班", 81));
studentList.add(new Student(6, "Fermat", "4班", 69));
studentList.add(new Student(7, "grace", "4班", 59));
//选取4班,成绩大于60的人,和其他班成绩大于50的人
//由于是链表,为了性能,这里使用iterator进行迭代
Iterator<Student> iter = studentList.listIterator();
while(iter.hasNext()){
Student cur = iter.next();
if("4班".equals(cur.className)){
if(cur.grade>60) {
System.out.println(cur);
}
}
else if(cur.grade>50){
System.out.println(cur);
}
}
}
}
(2)使用Stream操作
以下是通过Stream处理集合实例
studentList.stream().filter(s->{
if("4班".equals(s.className)){
return s.grade > 60;
}else {
return s.grade > 50;
}
}).forEach(s-> System.out.println(s));
(1)流程
当使用一个流时,通常包括三个基本步骤
当一个Stream流执行完终结方法时,就不能再使用该对象了,再次使用会抛出(IllegalStateException
)
(1)Stream接口
Stream接口是定义在java.util.stream.Stream
public interface Stream<T> extends BaseStream<T, Stream<T>> {
...
}
(2)Stream接口实现对象的获取方式
Collection
实现的stream()
与parallelStream()
接口方法,创建一个顺序流:
java8中将Collection接口中加入了stream()
,parallelStream()
默认方法,用于获取Stream对象
public interface Collection<E> extends Iterable<E> {
...
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
...
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
...
}
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流
Arrays.stream(T[] array)
静态方法:
public static <T> Stream<T> stream(T[] array) {
return stream(array, 0, array.length);
}
public static <T> Stream<T> stream(T[] array, int startInclusive, int endExclusive{
return StreamSupport.stream(spliterator(array,startInclusive,endExclusive), false);
}
String[] nums = new String[10];
Stream<String> stream = Arrays.stream(nums);
Stream
中的静态方法:
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
public static<T> Stream<T> generate(Supplier<T> s) {
Objects.requireNonNull(s);
return StreamSupport.stream(
new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
}
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {...}
BufferedReader对象的lines()
方法
public Stream<String> lines(){...}
BufferedReader reader = new BufferedReader(new FileReader("/user/sd/readme.md"));
Stream<String> lineStream = reader.lines();
使用 Pattern.splitAsStream()
方法,将字符串分隔成流
Pattern pattern = Pattern.compile(",");
Stream<String> stringStream = pattern.splitAsStream("a,b,c,d");
stringStream.forEach(System.out::println);
参考
(1)Stream filter(Predicate super T> predicate)
该方法通过传入predicate
接口对象,作为筛选条件
List<String> personList = new ArrayList();
personList.add("Alice");
personList.add("Bob");
personList.add("Coco");
Stream<String> stream = personList.stream()
stream.filter(s->s.startsWith("A")).forEach(System.out::println);//Alice
(2)Stream map(Functionsuper
该接口需要一个Function
函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流
Stream<String> stream = Stream.of("1", "2", "3");
Stream<Integer> result = stream.map(Integer::parseInt);
(3)Stream limit(long maxSize)
该方法可以对流进行截取,只取用前n个
参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作
stream.limit(2).forEach(System.out::println);//Alice Bob
(4)Stream skip(long n)
可以使用 skip 方法获取一个跳过n个数据后的新流
如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流
stream.skip(1).forEach(System.out::println);//Bob Coco
(5)Stream
该方法会将重复元素给去除掉
该方法使用hashCode()
和equals(Object o)
方法来比较元素,只有两者都相同才认为是相同的,只会保留第一个所遇到的元素
public class StreamTest {
public static void main(String[] args){
List<String> list = new ArrayList();
StreamTest s = new StreamTest();
list.add("1");
list.add("2");
list.add("1");
list.add(new String("1"));
list.stream().distinct().forEach(System.out::println);//1 2
}
}
当我们需要去重时也可以使用Set
去重:
public static void main(String[] args) {
users.parallelStream().filter(distinctByKey(User::getId))
.forEach(System.out::println);
}
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Set<Object> seen = ConcurrentHashMap.newKeySet();
return t -> seen.add(keyExtractor.apply(t));
}
Function
接口处理数据后,将处理后的数据添加尝试添加进Set,如果添加不成功就代表已经有改元素了,返回false,此时过滤器函数会将改元素丢弃,实现去重(6)
该方法通过传入Function接口实例将数据元素进行元素转换,构造新的Stream对象
此外还有方法XXStream mapToXX(ToXXFunction super T> mapper)
等一系列方法,来将数据转换为特定类型
List<String> list = new ArrayList();
StreamTest s = new StreamTest();
list.add("String");
list.add("哈哈");
list.add(new String("hello"));
list.stream().map(String::toUpperCase).forEach(System.out::println);//STRING 哈哈 HELLO
(7)Stream
该方法使用传入的Comparator
进行比较,然后排序,构建排序后的新Stream对象
还包含一个无参的构造方法
list.add("String");
list.add("哈哈");
list.add(new String("hello"));
list.stream().sorted(String::compareTo).forEach(System.out::println);//String hello 哈哈
(8)Stream
该方法通过传入Consumer接口对元素进行一些操作,并返回新的Stream对象
class A{
int var = 0;
void display(){
System.out.println(var);
}
}
List<A> list = new ArrayList();
StreamTest s = new StreamTest();
list.add(new A());
list.add(new A());
list.add(new A());
list.stream().peek(o->o.var = 1).forEach(A::display);
(7)static
该静态方法可以将两个流合并成一个新Stream对象
该方法传入的流的泛型类型要一致
stream = Stream.concat(stream,Stream.of("1","2"));
stream.forEach(System.out::println);//Alice Bob Coco 1 2
(1)void forEach(Consumersuper
该方法接收一个 Consumer
接口对象,会将每一个流元素交给该函数进行处理
List<String> personList = new ArrayList();
personList.add("Alice");
personList.add("Bob");
personList.add("Coco");
personList.stream().forEach(s->{
if(s.startsWith("A")){
System.out.println(s);
}
});//打印以A开头的字符串
(2)long count()
该方法返回操作后的元素个数+
System.out.println(stream.filter(s -> s.startsWith("A")).count());//1
(3)Object[] toArray()
该方法将数据转入到数组中,并返回
List<String> list = new ArrayList();
StreamTest s = new StreamTest();
list.add("String");
list.add("哈哈");
list.add(new String("hello"));
System.out.println(Arrays.toString(list.stream().sorted(String::compareTo).toArray()));//[String, hello, 哈哈]