лямбда и потоковый API в Java8

Java

предисловие

​ Так как в проекте используется много нового в Java8, я только сначала знал, как это написать, но это было не очень гладко, а потом я нашел хорошее видео о новых возможностях Java8 в Интернете, поэтому я продолжение После некоторого изучения ниже приведены мои заметки и соответствующее понимание лямбда-выражений и Stream API.Адрес видео, можете сами посмотреть, если интересно.

Новые функции в Java8

  1. Быстрее Заменена структура данных, структура памяти (JVM)
  2. *меньше кода (лямбда-выражения)*
  3. *Мощный потоковый API*
  4. Упростить параллельное вилочное соединение (параллельный последовательный коммутатор)
  5. Минимизировать исключения нулевого указателя Необязательно

Лямбда-выражения

Прежде чем говорить о Lambda, в первую очередь стоит поговорить о функциональном интерфейсе. Можно сказать, что это то, что существует для лямбда-выражений. Так что же такое функциональный интерфейс?

функциональный интерфейс

Определение: в интерфейсе есть только один абстрактный интерфейс. Все интерфейсы, такие как java.util.function, являются функциональными интерфейсами. Java 1.8 предоставляет @FunctionalInterface для проверки того, является ли интерфейс функциональным интерфейсом. например: Код пользовательского интерфейса в пакете java.util.function

@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

// jdk 1.8 接口可以有默认实现
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

Как только вы поймете, что такое функциональный интерфейс, вам будет легко понять лямбда-выражения.

«->» — это символ в левой части лямбда-выражения.Список параметров абстрактного метода в функциональном интерфейсе, правая сторона указываетваша реализация этого метода. Например например:

public class Test{
	public static void main(String[] args){
		 Consumer consumer = x-> System.out.println(x);
         consumer.accept(1);
	}
}

выход 1;

Четыре функциональных интерфейса

Когда мы используем функциональные интерфейсы, мы обычно инкапсулируем их.

потребительский интерфейс

Потребитель имеет только один абстрактный метод с именем accept, список параметров имеет только один общий t и не возвращает никакого значения. Тип данных параметра определяется классом например:

/**
 * @ClassName ConsumerTest
 * @Description 消费型接口, 消费字符串字段 打印输出
 * @Author ouyangkang
 * @Date 2019-02-18 15:46
 **/
public class ConsumerTest {

    public static void main(String[] args) {
        test("hello",x-> System.out.println(x));
    }

    public static <T> void test(T t, Consumer<T> consumer) {
        consumer.accept(t);
    }
}

вывод: привет Если вам нужно несколько списков параметров, вы также можете найти соответствующий функциональный интерфейс, такой как ObjLongConsumer, в пакете java.util.function. Остальные можете посмотреть сами

интерфейс питания

У поставщика есть только один абстрактный метод с именем get, список параметров пуст, есть возвращаемое значение и тип данных возвращаемого значения — T.

/**
 * @ClassName SupplerTest
 * @Description 供给型接口 字符串拼接
 * @Author ouyangkang
 * @Date 2019-02-18 15:53
 **/
public class SupplerTest {

    public static void main(String[] args) {
        String hello = test("hello ", () -> "word!");
        System.out.println(hello);
    }

    public static String test(String str,Supplier<String> supplier){
        return str + supplier.get();
    }
}

Вывод: привет, мир! Если возвращаемые данные относятся к базовому типу данных, вы можете найти соответствующий функциональный интерфейс в пакете java.util.function, например: getAsLong Другие можно просмотреть самостоятельно.

функциональный интерфейс

Function имеет только один абстрактный метод с именем apply, только один параметр в списке параметров — T и имеет возвращаемое значение, а тип данных возвращаемого значения — R.

/**
 * @ClassName FunctionTest
 * @Description 函数式接口 将字符串转换成大写的
 * @Author ouyangkang
 * @Date 2019-02-18 16:01
 **/
public class FunctionTest {


    public static void main(String[] args) {
        String test = test("hello", x -> x.toUpperCase());
        System.out.println(test);
    }


    public static String test(String str , Function<String,String> function){
        return function.apply(str);
    }
}

Вывод: ПРИВЕТ Если вам нужно несколько входных параметров, а затем вернуть значение, вы можете найти соответствующий функциональный интерфейс, такой как BiFunction, в пакете java.util.function. Остальные можете посмотреть сами

Напористый интерфейс

Напористый тип также известен как тип суждения. Predicate имеет только один абстрактный метод с именем test, только один параметр в списке параметров — T и имеет возвращаемое значение, а тип возвращаемого значения — логический.

/**
 * @ClassName PredicateTest
 * @Description 断言型接口,判断字符串大小是否大于6
 * @Author ouyangkang
 * @Date 2019-02-18 16:16
 **/
public class PredicateTest {
    public static void main(String[] args) {
        boolean hello = test("hello", x -> x.length() > 6);
        System.out.println(hello);
    }
    public static boolean test(String str, Predicate<String> predicate){
        return predicate.test(str);
    }
}

Вывод: ложь

Stream API

Отличительной чертой Java 8 является то, что Stream тесно связан сjava.ioInputStream и OutputStream в пакете — совершенно разные понятия. Потоковые промежуточные операции. Несколько промежуточных операций могут быть связаны для формирования конвейера. Если в конвейере не будет запущена операция завершения, никакая обработка не будет выполняться в середине! Когда операция завершена, она будет обработана сразу, что называется ленивой обработкой. Чтобы выполнить операцию потока, сначала получите поток. Есть 4 способа получить стрим.

  1. Получить поток Получить поток с помощью метода потока и метода parallelStream() (параллельный поток), предоставляемых серией коллекций.
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        // 常用获取流的方式
        Stream<Integer> stream = list.stream();
    }
  1. Преобразование массива в поток через Arrays.stream()
    public static void main(String[] args) {
        int[] a = new int[]{1,2,3,4};
        IntStream stream = Arrays.stream(a);
        
    }
  1. Создайте поток с помощью метода Stream.of Today
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1, 2, 3);
    }
  1. Создавайте неограниченные потоки
    public static void main(String[] args) {
        Stream<Integer> iterate = Stream.iterate(0, x -> x + 2);
    }

Все операции над потоками можно разделить на 4 типа, а именно фильтрация и сегментация, отображение, сортировка и финализация (редукция, сбор)

Фильтрация и фрагментация

Операции: фильтр, расстояние, ограничение, пропуск. filter : операция фильтра, параметр метода является интерфейсом утверждения например:

 public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1, 2, 3);
        stream.filter(x->x != 2).forEach(x-> System.out.println(x));
    }

вывод:

1
3

different : операция дедупликации, метод не имеет параметров limit : получить первые несколько фрагментов данных, параметр метода длинный skip : Сколько фрагментов данных пропустить раньше, а затем получить все следующие. Параметр метода длинный

карта

Общие операции включают map и flatMap. карта: обработайте исходные данные и верните обработанные данные. Параметр метода представляет собой функциональный интерфейс. например:

  public static void main(String[] args) {
       Stream<Integer> stream = Stream.of(1, 2, 3);
       stream.map(x->x*2).forEach(System.out::println);
    }

вывод:

2
4
6

flatMap : интегрирует исходные данные исходного потока в другой поток один за другим. Параметр метода — это функциональный интерфейс, но возвращаемое значение — это поток. например:

    public static void main(String[] args) {
        List<String> list = Arrays.asList("a", "b", "c");
        List<String> list2 = Arrays.asList("f","d");
        list.stream().flatMap(x->list2.stream().map(y-> x + y)).forEach(System.out::println);
    }

Сортировать

Общие операции включают естественную сортировку сортировки, а параметр сортировки — это пользовательская сортировка сортировщика. Естественная сортировка, например:

public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1, 2, 3);
        stream.sorted().forEach(System.out::println);
    }

вывод:

1
2
3

пользовательская сортировка

    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1, 2, 3);
        stream.sorted((x,y)->-Integer.compare(x,y)).forEach(System.out::println);
    }

вывод:

3
2
1

Завершить операцию

  • allMatch проверяет соответствие всех элементов. Параметр метода является интерфейсом утверждения.
  • anyMatch проверяет соответствие всех элементов. Параметр метода является интерфейсом утверждений.
  • noneMatch проверяет, не совпадают ли все элементы. Параметр метода является интерфейсом утверждения.
  • findFirst возвращает первый элемент без параметров метода
  • findAny возвращает любой элемент текущего потока без параметров метода
  • count возвращает общее количество элементов в потоке, без параметров метода
  • max возвращает максимальное значение потока, без аргументов метода
  • min возвращает минимальное значение в потоке без аргументов метода

уменьшать

уменьшить : сокращение -- вы можете многократно комбинировать элементы в потоке, чтобы получить значение. например:

 public static void main(String[] args) {
        List<Integer> list1 = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        Integer reduce = list1.stream().reduce(11, (x, y) -> x + y);
        System.out.println(reduce);
    }

выход: 66

собирать

Это очень распространенная операция. Переключите потоковую загрузку на другую форму. Получает реализацию интерфейса Collector, метод для суммирования элементов в Stream. Используйте метод collect для сбора. Параметр метода — Collector. Collector может быть реализован статическими методами, такими как toList(), toSet(), toMap(Function(T,R) key, Function(T,R)) value) в Collectors.

  • toList() возвращает коллектор, который принимает список входных элементов в новый список.
  • toMap(Function super T,? extends K> keyMapper, Function super T,? extends U> valueMapper) возвращает коллектор, который накапливает элементы в карту, чьи ключи и значения являются предоставленными функциями сопоставления, примененными к результату входной элемент.
  • toSet() возвращает Collector, который устанавливает входные элементы в новый Set. например:

Класс пользователя

@Data
@ToString
public class User {
    private String name;

    private Integer age;

    private Integer salary;
}
   public static void main(String[] args) {
         List<User> users = Arrays.asList(new User("张三", 19, 1000),
                new User("张三", 58, 2000),
                new User("李四", 38, 3000),
                new User("赵五", 48, 4000)
        );
        List<String> collect = users.stream().map(x -> x.getName()).collect(Collectors.toList());
        Set<String> collect1 = users.stream().map(x -> x.getName()).collect(Collectors.toSet());
        Map<Integer, String> collect2 = users.stream().collect(Collectors.toMap(x -> x.getAge(), x -> x.getName()));
        System.out.println(collect);
        System.out.println(collect1);
        System.out.println(collect2);
    }

вывод:

[张三, 张三, 李四, 赵五]
[李四, 张三, 赵五]
{48=赵五, 19=张三, 38=李四, 58=张三}

группировка

Метод Collectors.groupingBy() — это реализация операции с входными элементами, которая возвращает Collector «по группе» типа T , группируя элементы в соответствии с функцией сортировки. Это очень распространенная операция. Например, вы хотите сгруппировать людей с одинаковым именем. groupingBy(Function super T,? расширяет классификатор K>) например:

    public static void main(String[] args) {
        List<User> users = Arrays.asList(new User("张三", 19, 1000),
                new User("张三", 58, 2000),
                new User("李四", 38, 3000),
                new User("赵五", 48, 4000)
        );
        Map<String, List<User>> collect3 = users.stream().collect(Collectors.groupingBy(x -> x.getName()));
        System.out.println(collect3);
    }

Вывод: {Ли Си=[Пользователь{имя='Ли Си', возраст=38, зарплата=3000}], Чжан Сан=[Пользователь{имя='Чжан Сан', возраст=19, зарплата=1000}, Пользователь{ name='Чжан Сан', возраст=58, зарплата=2000}], Чжао Ву=[Пользователь{name='Чжао Ву', возраст=48, зарплата=4000}]}

Конечно, есть и другие, более сложные операции группирования, и фактический код зависит от того, какой бизнес нужно реализовать.

Суммировать

Поначалу лямбда-выражение в java8 может быть не очень знакомым, но пока вы с ним знакомы, вы обнаружите, что его очень легко использовать, а лямбда-выражение в сочетании с потоковым API может написать ваш собственный инструментальный класс. В обычных проектах это может сэкономить время и повысить эффективность написания кода. Теперь я даю класс инструментов List to Map.

public class CollectionStream {
    public static void main(String[] args) {
        List<User> users = Arrays.asList(new User("张三", 19, 1000),
                new User("张三", 58, 2000),
                new User("李四", 38, 3000),
                new User("赵五", 48, 4000)
        );
        Map<Integer, Integer> map = listToMap(users, x -> x.getAge(), x -> x.getSalary());
        System.out.println(map);
    }

    /**
     * @Author ouyangkang
     * @Description list 转map key不能相同 ,如果相同会报错。 方法对 源数据,key,value过滤null。
     * @Date 9:27 2019/2/19
     * @param source 源数据
     * @param key
     * @param value
     * @return java.util.Map<K,V>
    **/
    public static <DTO, K, V> Map<K, V> listToMap(List<DTO> source, Function<? super DTO, ? extends K> key, Function<? super DTO, ? extends V> value) {
        Objects.requireNonNull(source, "source not null");
        Objects.requireNonNull(key, "key not null");
        Objects.requireNonNull(value, "value not null");
        Map<K, V> map = source.stream()
                .filter(dto -> dto != null)
                .filter(dto -> key.apply(dto) != null)
                .filter(dto -> value.apply(dto) != null)
                .collect(Collectors.toMap(key, value));
        return map;
    }
}