Трансляция в действии

Java

1. Что такое поток?

  • Stream — это поток, который был представлен в Java 8. Это совершенно другое понятие, чем InputStream и OutputStream в пакете java.io. Короче говоря, поток — это коллекция, содержащая ряд элементов данных, но это не контейнер для хранения, а скорее контейнер алгоритмов, связанных с элементами данных.
  • Scala и Groovy демонстрируют, что функции первого класса могут значительно расширить арсенал программиста. Идея функционального программирования также используется в Stream, что значительно улучшает и высвобождает производительность. Если вы умеете пользоваться мощными функциями Stream, вы можете писать более лаконичный и выразительный код и с этого момента попрощаться с 996.

2. Строительство Stream

    1. Через поток() коллекции или parallelStream();
    1. Статические методы потоков, такие как Stream.of();
    1. Поток случайных чисел Random.ints();
    1. Типы-оболочки обычно используются в Stream, но на самом деле примитивные типы также могут использовать Stream. Если вы определяете базовый тип Stream, вы можете использовать статические методы интерфейса, range, empty для IntStream, LongStream и DoubleStream.
    1. Создание из входного потока, такого как файл
//1.集合中构造
	Arrays.asList(1,2,3,4,5).stream()...;

//2.静态构造
	Stream.of(1,2,3,4,5)...
 
//3.随机数流
	//IntStream
 	new Random(100).ints();
	IntStream.of(1,2,3);
  IntStream.range(1,100);
	//LongStream
  new Random(100).longs();
	//DoubleStream
	new Random(100).doubles();

//4.IntStream/LongStream/DoubleStream
//也可以使用Stream<Integer>、Stream<Long>、Stream<Double>构造
	IntStream.of(new int[]{1,2,3});
	
//5.文件输入构造
//通常Stream不需要关闭,仅仅是需要关闭在IO通道上运行的流

	try(final Stream<String> lines=Files.lines(Paths.get("somePath"))){
    lines.forEach(System.out::println);
	}

3. Потоковая передача рабочего процесса

  • Поток подобен итератору, который может перебирать каждый элемент в потоке. Обработка сериализации аналогична обработке итераторов, но функция Stream — это гораздо больше, чем просто итерация.

  • Среди них промежуточная операция и терминальная операция. Обычно после небольшой операции по потоковой обработке мелкий партнер обнаруживает, что IDE стала популярной, и часто не понимает причины, во многих случаях не понимает промежуточную операцию и терминальную операцию.

  • Проще говоря, промежуточная операция вернет поток после выполнения, аналогично возвращению этой операции после build() в режиме проектирования компоновщика.Промежуточная операция возвращает поток обработки, чтобы обеспечить синтаксис вызова цепочки. Терминальная операция является конечной операцией, которая обычно возвращает пустые или непотоковые результаты. Например, часто используемые нами toList(), toSet(), toMap() и toArray не являются потоковыми результатами, а foreach() с только побочными эффектами также недействителен.

  • map, flatmap, filter, peek, limit, skip, different, sorted... — все это промежуточные операции, а foreach, forEachOrdered, collect, findFirst, min, max — терминальные операции.

3.1. Промежуточная операция

3.1.1, карта

  • Применяется к каждому элементу в потоке
//下面两种等价的方式,完成将字符串转大写并排序
//1.函数式接口方式
	()->stream.of("apple","banana","orange","grapes", "melon","blueberry","blackberry")
	          .map(String::toUpperCase)
	          .sorted();

//2.Lambda表达式方式
	()->stream.of("apple","banana","orange","grapes", "melon","blueberry","blackberry")
	          .map(v->v.toUpperCase())
	          .sorted();

3.1.2, плоская карта

  • Подобно карте, функция применяется к каждому элементу в потоке.

  • Как видно из сигнатуры функции: возвращаемое значение map является объектом, а объект формирует новый Stream. И плоская карта возвращает поток. flatmap не создает новый поток, а преобразует исходные элементы в поток. Обычно используется для выравнивания потоков

    //map()签名
    	<R> Stream<R> map(Function<? super T, ? extends R> mapper);
    
    //flatmap()签名
    	<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
    
    //flatmap返回Stream
    	Stream.of(1,22,33).flatMap(v->Stream.of(v*v)).collect(Collectors.toList());
    
    //map返回对象
    	Stream.of(1,22,33).map(v->v*v).collect(Collectors.toList());
    
    //flatMap的扁平化处理
    
      List<Map<String, String>> list = new ArrayList<>();
      Map<String,String> map1 = new HashMap();
      map1.put("1", "one");
      map1.put("2", "two");
    
      Map<String,String> map2 = new HashMap();
      map2.put("3", "three");
      map2.put("4", "four");
      list.add(map1);
      list.add(map2);
    
      Set<String> output= list.stream()  //  Stream<Map<String, String>>
          .map(Map::values)              // Stream<List<String>>
          .flatMap(Collection::stream)   // Stream<String>
          .collect(Collectors.toSet());  //Set<String>
      [one, two, three,four]
    
    

3.1.3, заглянуть

  • Peek также работает с каждым элементом в потоке.В дополнение к созданию нового потока, содержащего все исходные элементы, он также предоставляет функцию потребления Consumer.

  • По сравнению с map видно, что peek может выполнять некоторый вывод, внешнюю обработку, побочные эффекты и т. д. без возвращаемого значения при потоковой обработке. Создайте новый поток, содержащий все элементы исходного потока, каждый элемент нового потока будет выполнять функцию потребления, заданную функцией peek, перед тем, как будет использован;

    //对每一个元素进行一些副作用
    	List<Integer> list = new ArrayList();
    	List<Integer> result = Stream.of(1, 2, 3, 4)
           .peek(x -> list.add(x))
           .map(x -> x * 2)
           .collect(Collectors.toList());
    
    //1
    //2
    //3
    //[1, 2, 3]
     System.out.println(list);
    
    
    //map()签名,返回值R
    	<R> Stream<R> map(Function<? super T, ? extends R> mapper);
    //peek()签名,返回值void
    	Stream<T> peek(Consumer<? super T> action); 
    

3.1.4, фильтр

  • Задайте условия фильтра, и элементы, удовлетворяющие фильтру, объединятся для создания нового потока.
//筛选出 >0 的元素
	Arrays.asList(1,2,3,4,5)
    		.stream()
    		.filter(v-> v>0)
    		.toArray(Integer[]::new);

//筛选出字母A开头的字符串
	Stream.of("apple","banana","orange","grapes", "melon","blueberry","blackberry")
            .filter(s->s.startWith("A"))
  	    .forEach(System.out::println)

3.1.5 ограничение/пропуск

  • limit возвращает первые несколько элементов потока, а skip отбрасывает первые несколько элементов.

3.1.6, отдельный

  • Простой поток

    //去重
    Stream.of(1,2,3,3,3,2,4,5,6)
      .distinct()
      .collect(Collectors.toSet());
    

3.2 Работа терминала

3.2.1, найти сначала

  • Он всегда возвращает первый элемент потока или пустой. Обратите внимание, что возвращаемое значение является необязательным.

  • Необязательный может содержать или не содержать значения, главное по возможности избегать NPE.

Optional<String> ops = Stream.of("apple","banana","orange","blueberry","blackberry")
	                .filter(s->s.startsWith("b"))
	                .findFirst();
//banana
ops.orElse("apple");

Optional<String> ops = Stream.of("apple","banana","orange","blueberry","blackberry")
                    .filter(s->s.startsWith("c"))
                    findFirst();
//apple
ops.orElse("apple");

3.2.2, мощный сбор

也许很多人经常搞不清Collector、Collection、Collections、Collectors。
1.Collection是Java集合祖先接口;
2.Collections是java.util包下的一个工具,内含有各种处理集合的静态方法。
3.java.util.stream.Stream#collect(java.util.stream.Collector)是Stream的一个函数,负责收集流。
4.java.util.stream.Collector是一个收集函数的接口,声明一个收集器功能。
5.java.util.Collectors是一收集器的工具类,内置了一系列常用收集器的实现,如Collectors.toList()/toSet(),作为上述第3条的collect()参数。
  • toList/toMap

    //toList
    //方式1
    List<String> list = Stream.of("I","love","you","too")
      		.collect(ArrayList::new,ArrayList::add,ArrayList::addAll);
    	//方式2
    	List<String> list = stream.collect(Collections.toList())
             
    //toMap   
      Map<Integer, Integer> collect1 = Stream.of(1, 3, 4)
    				.collect(Collectors.toMap(x -> x, x -> x + 1));
        
    
  • Массив в список

    List list = new ArrayList<>(Arrays.asList("a", "b", "c"));
    Integer [] myArray = { 1, 2, 3 };
    List myList = Arrays.stream(myArray).collect(Collectors.toList());
    //基本类型也可以实现转换(依赖boxed的装箱操作)
    int [] myArray2 = { 1, 2, 3 };
    List myList = Arrays.stream(myArray2).boxed().collect(Collectors.toList());
    
  • Возможно, collect — одна из наиболее часто используемых нами терминальных операций, toList, toSet, toMap за один раз. Но если вы посмотрите на сигнатуру функции сбора, вы обнаружите, что этот сбор не прост.

//collect1
<R> R collect(
            Supplier<R> supplier,
            BiConsumer<R,? super T> accumulator,
            BiConsumer<R,R> combiner)

//collect2
/**
    @param1: supplier为结果存放容器
    @param2: accumulator为结果如何添加到容器的操作
    @param3: combiner则为多个容器的聚合策略
*/
<R,A> R collect(collector<? super T,A,R> collector);

  • Из сигнатуры функции видно, что мы используем только второй метод сбора, то есть toList, toSet и toMap Collector, предоставленный jdk. Поскольку эти операции широко используются, эти сборщики предоставляются непосредственно в jdk.

  • Мы также можем реализовать свой собственный Collector

    /*
    
    	T:流中要收集的对象的泛型
    	A:累加器的类型,累加器是在收集过程中用于累加部分结果的对象
    	R:收集操作得到的对象(通常但不一定是集合)的类型。
    */
    public interface Collector<T,A,R> {
    
    	//结果容器
        Supplier<A> supplier();
    
    	//累加器执行累加的具体实现
        BiConsumer<A, T> accumulator();
    
        //合并2个结果的容器
        BinaryOperator<A> combiner();
    
        //对结果容器应用最终转换finisher
        Function<A, R> finisher();
    
        //characteristics
        Set<Characteristics> characteristics();
    }
    
    
    //自定义Collector
    //1.建立新的结果容器supplier(),返回值必须是一个空Supplier,供数据收集过程使用
    
      //toList返回一个空List<>,toSet、toMap类似
          @Override
          public Supplier<List<T>> supplier() {
              return ArrayList::new;
          }
    
    //2.累加器执行累加的具体实现accumulator()
    //从BiConsumer看出返回值void,接受2个参数第一个是累计值,第二个是当期处理的第n个元素
          @Override
        	public BiConsumer<List<T>, T> accumulator() {
            //可以看出是个拼接list的操作
              return List::add;
        	}
    	
    	//3.转换最终结果容器的finisher()
    	//流遍历完成后,有时需要对结果处理,可以借助finisher。finisher()须返回累加过程中的最后一个调用函数,用于将累加器对象转换为集合。	
    	//接收2个参数,第一个参数是累加值,第二个参数是返回值,返回值就是我们最终要的东西。
    	@Override
    	public Function<List<T>, List<T>> finisher() {
             //原样输出不额外处理
             //其实就是Function.identity()
        	    return (i) -> i;
    	}
    
    	//4.合并容器的combiner()
    	//Stream支持并行操作,但并行的子部分处理规约如何处理?combiner()就是指明了各个子任务如何合并。
    	@Override
    	public BinaryOperator<List<T>> combiner() {	
            //每个子任务是一个List,2个子任务结果合并累加到第一个子任务上
            return (list1, list2) -> {
            	list1.addAll(list2);
    	        return list1;
        	    };
    	}
    
    	//5.characteristics()
    	
    
  • Реализация Collector для toList строк, ядром является понимание T, A, R

public class MyCollector<String> implements Collector<String, List<String>, List<String>> {
    @Override
    public Supplier<List<String>> supplier() {
      return ArrayList::new;
    }

    @Override
    public BiConsumer<List<String>, String> accumulator() {
      return (List<String> l, String s) -> {
        l.add(s);
      };
    }

    @Override
    public BinaryOperator<List<String>> combiner() {
      return (List<String> l, List<String> r) -> {
        List<String> list = new ArrayList<>(l);
        list.addAll(r);
        return list;
      };
    }

    @Override
    public Function<List<String>, List<String>> finisher() {
      return Function.identity();
    }

    @Override
    public Set<Characteristics> characteristics() {
      return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH));
    }
  }



  Stream<String> apple = Stream
        .of("apple","banana", "orange", "grapes", "melon", "blueberry", "blackberry");

  System.out.println(apple.collect(new MyCollector<>()));



//字符串的拼接concat

String concat = Stream
        .of("apple", "apple","banana", "orange", "grapes", "melon", "berry", 	"blary")
  			.collect(StringBuilder::new,
  					 StringBuilder::append,
  					 StringBuilder::append)
  			.toString();
//等价于上面,这样看起来应该更加清晰
String concat = stringStream.collect(() -> new StringBuilder(),(l, x) -> l.append(x), (r1, r2) -> r1.append(r2)).toString();

3.2.3, уменьшить работу

  • Основной целью этого метода является объединение элементов потока. Он предоставляет начальное значение (seed), а затем объединяет правило операции (BinaryOperator) с первым, вторым и n-м элементами предыдущего потока. В этом смысле конкатенация строк, сумма, минимальное, максимальное и среднее значение массивов являются специальными сокращениями.
Integer sum = integers.reduce(0,(a,b)->a+b);
或者
Integer sum = integers.reduce(0,Integer::sum);

  • Если нет начального значения, то первые два элемента Stream будут объединены, а возвращаемое значение является необязательным (поскольку нет начального сокращения(), элементов может не хватить, поэтому дизайн возвращает необязательный)
//原生操作
final Integer[] integers = Lists.newArrayList(1, 2, 3, 4, 5)
        .stream()
        .collect(() -> new Integer[]{0}, (a, x) -> a[0] += x, (a1, a2) -> a1[0] += a2[0]);
//reducing操作
final Integer collect = Lists.newArrayList(1, 2, 3, 4, 5)
        .stream()
        .collect(Collectors.reducing(0, Integer::sum));    
//当然Stream也提供了reduce操作
final Integer collect = Lists.newArrayList(1, 2, 3, 4, 5)
        .stream().reduce(0, Integer::sum)
  • Уменьшить объединенные строки
String concat = Stream.of("A","B","C","D")
  							.reduce("",String::concat);
  • уменьшить, чтобы найти минимальное значение
double minValue = Stream.of(-1.5,1.0,-3.0,-2.0)
  								.reduce(Double.MAX_VALUE,Double::min);
  • уменьшить суммирование
//有起始值
int sumValue = Stream.of(1,2,3,4)
  						.reduce(0,Integer::sum);

//无起始值
int sumValue = Stream.of(1,2,3,4)
  						.reduce(Integer::sum).get();

4. Обычно используется в разработке

4.1. Создайте поток самостоятельно

  • Реализуя интерфейс Supplier, вы можете самостоятельно управлять генерацией потоков.
  • Передайте экземпляр Supplier в поток, сгенерированный Stream.generate(), который по умолчанию является последовательным (относительно параллельного), но неупорядоченным (относительно упорядоченного). Поскольку он бесконечен, в конвейере размер Stream должен быть ограничен такими операциями, как limit.
Random seed = new Random();
Supplier<Integer> random = seed::nextInt;
Stream.generate(random).limit(10).forEach(System.out::println);
//Another way
IntStream.generate(() -> (int) (System.nanoTime() % 100)).
limit(10).forEach(System.out::println);

4.2, генерация арифметической последовательности

//步长为3
Stream.iterate(0, n -> n + 3)
  .limit(10)
  .forEach(x -> System.out.print(x + " "));

4.3, уменьшить, собрать реализовать функцию фильтра

 1.collect实现方式更简单,效率也更高。
 2.reduce每次需要new ArrayList是因为reduce规定第二个参数:
    BiFunction accumulator表达式不能改变其自身参数acc原有值,所以每次都要new ArrayList(acc),再返回新的list。
//reduce 方式
public static <T> List<T> filter(Stream<T> stream,Predicate<T> predicate){
    
    return stream.reduce(new ArrayList<T>(),(acc,t)->{
        if(predicate.test(t)){
            List<T> lists = new ArrayList<T>(acc);
            lists.add(t);
            return lists;
        }
        return acc;
    }, (List<T> left,List<T> right)->{
        List<T> lists= new ArrayList<T>(left);
        lists.addAll(right);
        return lists;
    }
}


//collect

public static <T> List<T> filter(Stream<T> stream, Predicate<T> predicate) {
   return stream.collect(ArrayList::new, (acc, t) -> {
       if (predicate.test(t))
           acc.add(t);
   }, ArrayList::addAll);
}

4.4, возвращаемый тип спецификации

//使用toCollection()指定规约容器的类型
ArrayList<String> arrayList = stream.collect(Collectors.toCollection(ArrayList::new));// (3)
HashSet<String> hashSet = stream.collect(Collectors.toCollection(HashSet::new));// (4)

4.5, группировкаПо вышестоящим и нижестоящим коллекторам

  • Коллектор, созданный с помощью partitioningBy(), подходит для разделения элементов в потоке на две взаимодополняющие и пересекающиеся части в соответствии с определенной бинарной логикой (удовлетворительно, неудовлетворительно), такой как пол, годен или не годен
  • groupingBy() группирует данные по определенному атрибуту, и элементы с таким же атрибутом будут сопоставлены с ключом Map
//partitioningBy()
Map<Boolean, List<Student>> passingFailing = students.stream()
         .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));
         
//groupingBy
Map<Department, List<Employee>> byDept = employees.stream()
            .collect(Collectors.groupingBy(Employee::getDepartment));       


//mapping的下游
//按照部门对员工进行分组,并且只保留员工的名字
Map<Department,List<String>> byDept=employees.stream()
            .collect(Collectors.groupingBy(Employee::getDepartment,
                Collectors.mapping(Employee::getName,   //下游收集器
                    Collectors.toList())));             //更下游收集器