Не говорите об этих бесполезных сериях: цикл forEach

Java задняя часть внешний интерфейс программист
Не говорите об этих бесполезных сериях: цикл forEach

последовательность

Программисты, которые пишут Java-код, обход коллекций - обычное дело. Они привыкли к циклам for, while и циклам while. Давайте сделаем что-нибудь еще. JDK8 использует новый механизм forEach в сочетании с потоками, чтобы ваш код выглядит более лаконично и качественно, что удобно для последующего обслуживания и чтения. Ну, не будем об этом, "говорить дешево, покажи код", перейдем непосредственно к коду, придерживаясь выдержанного стиля. скр~скр~

1. Обход общих коллекций

forEach в JDK8 может не только перемещаться по элементам коллекции, но и выполнять некоторые действия в соответствии со значением элементов коллекции, например выносить суждения, такие как фильтрация. Возьмем в качестве примера обычно используемые List и Map.

Обход коллекции Map:

/**
 * 对Map的遍历
 */
Map<String, Integer> map = Maps.newHashMap();
map.put("天猫双11", 1024);
map.put("京东双11", 1024);
// ①简写式
map.forEach((k, v) -> System.out.println("key:" + k + ", value:" + v));
// ②判断式
map.forEach((k, v) -> {
    System.out.println("key:" + k + ", value:" + v);
    if (StringUtils.contains(k, "京东")) {
        System.out.println("skr~");
    }
});

Результаты:

key:京东双11, value:1024
key:天猫双11, value:1024
key:京东双11, value:1024
skr~
key:天猫双11, value:1024

Коллекция обхода списка:

/**
 * 对List的遍历
 */
List<String> list = Lists.newArrayList();
list.add("买买买");
list.add("剁剁剁");
// ①简写式
list.forEach(item -> System.out.println(item));
// ②判断式
list.forEach(item -> {
    if (StringUtils.contains(item, "买")) {
       System.out.println("不如再用两个肾换个iPhone XS Max Pro Plus也无妨啊~");
    }
});

Результат выполнения следующий:

买买买
剁剁剁
不如再用两个肾换个iPhone XS Max Pro Plus也无妨啊~

Во-вторых, использование двойных двоеточий в JDK8.

Использование двойных двоеточий в JDK8 заключается в передаче метода в качестве параметра в поток, чтобы каждый элемент потока передавался в метод для выполнения.

Например, приведенный вышеList<String>для печати мы можем написать:

// JDK8 双冒号的用法
list.forEach(System.out::println);

Результат выполнения тот же самый:

买买买
剁剁剁

В JDK8 интерфейс Iterable 8 реализует метод forEach по умолчанию, вызывает метод accept в интерфейсе Consumer, добавленном в JDK8, и выполняет входящие параметры метода. Исходный код JDK выглядит следующим образом:

/**
     * Performs the given action for each element of the {@code Iterable}
     * until all elements have been processed or the action throws an
     * exception.  Unless otherwise specified by the implementing class,
     * actions are performed in the order of iteration (if an iteration order
     * is specified).  Exceptions thrown by the action are relayed to the
     * caller.
     *
     * @implSpec
     * <p>The default implementation behaves as if:
     * <pre>{@code
     *     for (T t : this)
     *         action.accept(t);
     * }</pre>
     *
     * @param action The action to be performed for each element
     * @throws NullPointerException if the specified action is null
     * @since 1.8
     */
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

3. Сборка пользовательских типов

Я думаю, что это использование более практично и более распространено. Сначала мы определяем два POJO, один называется Track, который является сущностью и сопоставляется со структурой таблицы нашей базы данных; другой называется TrackVo, который является Vo и возвращается на интерфейсный дисплей на уровне интерфейса. Здесь, чтобы упростить количество кода, мы используем плагин lombok. Что ж, давайте сначала определим их:

Track.java

/**
 * @author huangzx
 * @date 2018/11/13
 */
@AllArgsConstructor
@Data
@Builder
public class Track {
    private Long id;
    private String name;
    private String anchor;
}

TrackVo.java

/**
 * @author huangzx
 * @date 2018/11/13
 */
@AllArgsConstructor
@Data
@Builder
public class TrackVo {
    private Long trackId;
    private String trackName;
}

Часто встречающийся сценарий: я извлекаю данные через слой Dao, который являетсяList<Track>, но то, что нужно переднему концу, этоList<TrackVo>, но поля Track и TrackVo разные. В соответствии с предыдущей практикой можно напрямую использовать цикл for или цикл while дляList<Track>Пересекая объект, назначенный Trackvo, вы быстро нажимаете на клавиатуру, сопровождаемую дрожанием экрана, было создано десять строк кода, Чанг Шу Ишэн, очень хорошо!

Как всем известно, с момента появления нового forEach в JDK8 в сочетании с потоками эти десять строк кода можно сжать в одну строку, что очень лаконично. Посмотри:

/**
 * 对自定义类型的组装
 */
List<Track> trackList = Lists.newArrayList();
Track trackFirst = Track.builder().id(1L).name("我的理想").anchor("方清平").build();
Track trackSecond = Track.builder().id(2L).name("台上台下").anchor("方清平").build();
trackList.add(trackFirst);
trackList.add(trackSecond);

List<TrackVo> trackVoList = trackList.parallelStream().map(track -> TrackVo.builder().trackId(track.getId()).trackName(track.getName()).build()).collect(Collectors.toList());
System.out.println(JSON.toJSONString(trackVoList));

Результат выполнения следующий:

[{"trackId":1,"trackName":"我的理想"},{"trackId":2,"trackName":"台上台下"}]

Это похоже на то, что вы ожидали?

В-четвертых, принцип

хорошо, придерживаясь принципа, что программисты знают одно - "знание должно знать почему". Разберем принцип реализации forEach.

Во-первых, нам нужно понять концепцию потоков (streams), использованную выше, и пассивных итераторов.

Основной новой функцией Java 8 являются лямбда-выражения и связанные с ними функции, такие как потоки, ссылки на методы и функциональные интерфейсы. Именно благодаря этим новым функциям мы можем использовать пассивные итераторы вместо традиционных активных итераторов, особенно интерфейс Iterable предоставляет метод по умолчанию для пассивных итераторов, называемый forEach(). Метод по умолчанию — это еще одна новая функция Java 8, это реализация метода интерфейса по умолчанию, в этом случае метод forEach() фактически реализован в виде активного итератора, аналогичного Java 5.

Классы коллекций, которые реализуют интерфейс Iterable (например, все списки List, карта коллекции), теперь имеют метод forEach(), который принимает параметр функционального интерфейса, который на самом деле является лямбда-выражением, передаваемым в метод forEach(). Давайте напишем фрагмент кода, который использует потоки:

/**
 * @author huangzx
 * @date 2018/11/13
 */
public class StreamCountsTest {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("natural", "flow", "of", "water", "narrower");
        long count = words.stream().filter(w -> w.length() > 5).count();
        System.out.println(count);
    }
}

Показанный выше код использует Java 8 для написания кода для реализации вычислений потокового конвейера и подсчета количества слов, длина символа которых превышает 5. Слова списка используются для создания потока, а затем набор данных фильтруется с помощью фильтра.Метод filter() отфильтровывает только длину символов слов.Параметром этого метода является лямбда-выражение. Наконец, метод count() потока действует как последняя операция для получения результата приложения.

были правы自定义类型的组装Код анализируется следующим образом:

图解streams

В дополнение к filter() промежуточные операции включают в себя отдельные(), sorted(), map() и т. д., которые обычно представляют собой сортировку набора данных, а возвращаемое значение обычно представляет собой набор данных. Мы можем примерно просмотреть, какие методы у него есть, следующим образом:

Streams提供的方法

В общем, поток следует принципу «Что делать, а не как это сделать». В нашем примере мы описываем, что нужно сделать, например, получать длинные слова и подсчитать их количество. Мы не указывали в каком порядке, либо в каком потоке, чтобы сделать это. Вместо этого цикл должен указать, как выполнить вычисление в самом начале.

5. Зачем это использовать?

Многие люди в Интернете говорят, что производительность лямбда-выражений JDK8 не так хороша, как производительность традиционных методов записи. Тогда почему он все еще появляется? Новые API и новый синтаксис JDK иногда не оптимизированы для повышения производительности. Теоретически, производительность объектно-ориентированного программирования определенно ниже, чем у процессно-ориентированного, но удобство сопровождения и ясность значительно улучшились.

Таким образом, используется ли функция или нет, зависит от того, на что вы обращаете внимание.Когда компания дает вам 3 месяца на реализацию функции, очевидно, что никто не будет тратить 1 месяц на оптимизацию производительности.В это время более понятный и разумный код очень важен. Важно, в большинстве случаев проблемы с производительностью возникают не из-за посредственной работы алгоритмов и API, а из-за ошибок в различных системах.

6. Резюме

Подводя итог вышесказанному? Во-первых, как мы используем forEach для работы с общими наборами, и вводится использование двойных двоеточий; затем на основе существующего набора, как использовать его для создания нового набора для инкапсуляции данных, которые мы хотим; наконец, вводится его Реализуйте принцип и объясните, почему он используется. Ладно, выходи из класса окончено. . . (Учитель ~ пока~~)

(Заканчивать)