поток Java найти совпадение или последний? - PullRequest
0 голосов
/ 06 декабря 2018

Как найти первое совпадение или последний элемент в списке, используя поток Java?

Что означает, что если ни один элемент не соответствует условию ,, тогда вернуть последний элемент.

Например:

OptionalInt i = IntStream.rangeClosed(1,5)
                         .filter(x-> x == 7)
                         .findFirst();
System.out.print(i.getAsInt());

Что мне нужно сделать, чтобы оно вернулось 5;

Ответы [ 5 ]

0 голосов
/ 06 декабря 2018

В основном я бы использовал один из следующих двух методов или их отклонений:

Вариант потока:

<T> T getFirstMatchOrLast(List<T> list, Predicate<T> filter, T defaultValue) {
    return list.stream()
            .filter(filter)
            .findFirst()
            .orElse(list.isEmpty() ? defaultValue : list.get(list.size() - 1));
}

вариант не потока:

<T> T getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter, T defaultValue) {
    T relevant = defaultValue;
    for (T entry : iterable) {
        relevant = entry;
        if (filter.test(entry))
            break;
    }
    return relevant;
}

Илиа также Илмари Каронен , предложенный в комментарии к Iterable<T>, вы можете даже позвонить stream::iterator, если вы действительно имеете дело с Stream вместо List.Вызов показанных методов будет выглядеть следующим образом:

getFirstMatchOrLast(Arrays.asList(1, 20, 3), i -> i == 20, 1); // returns 20
getFirstMatchOrLast(Collections.emptyList(), i -> i == 3, 20); // returns 20
getFirstMatchOrLast(Arrays.asList(1, 2, 20), i -> i == 7, 30); // returns 20
// only non-stream variant: having a Stream<Integer> stream = Stream.of(1, 2, 20)
getFirstMatchOrLast(stream::iterator, i -> i == 7, 30); // returns 20

Я бы не использовал здесь reduce, потому что это звучит неправильно для меня в том смысле, что он также проходит через все записи, даже если первая записьможет уже соответствовать, то есть он больше не закорачивается.Более того, для меня это не так читабельно, как filter.findFirst.orElse ... (но это, вероятно, только мое мнение)

Я бы тогда, возможно, даже что-то закончил бы следующим образом:

<T> Optional<T> getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter) {
    T relevant = null;
    for (T entry : iterable) {
        relevant = entry;
        if (filter.test(entry))
            break;
    }
    return Optional.ofNullable(relevant);
}
// or transform the stream variant to somethinng like that... however I think that isn't as readable anymore...

чтобы звонки выглядели так:

getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseThrow(...)
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElse(0);
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseGet(() -> /* complex formula */);
getFirstMatchOrLast(stream::iterator, i -> i == 5).ifPresent(...)
0 голосов
/ 06 декабря 2018

Я не уверен, почему вы действительно хотите использовать для этого потоки, простого for -цикла будет достаточно:

public static <T> T getFirstMatchingOrLast(List<? extends T> source, Predicate<? super T> predicate){
    // handle empty case
    if(source.isEmpty()){
        return null;
    }
    for(T t : source){
        if(predicate.test(t)){
            return t;
        }
    }
    return source.get(source.size() -1);
} 

Который затем можно назвать как:

Integer match = getFirstMatchingOrLast(ints, i -> i == 7);
0 голосов
/ 06 декабря 2018

Учитывая список

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

Вы можете просто сделать:

int value = list.stream().filter(x -> x == 2)
                         .findFirst()
                         .orElse(list.get(list.size() - 1));

Здесь, если фильтр оценивается как true, элемент извлекается, иначе возвращается последний элемент в последнем.

Если список пуст , вы можете вернуть значение по умолчанию, например -1.

int value = list.stream().filter(x -> x == 2)
                         .findFirst()
                         .orElse(list.isEmpty() ? -1 : list.get(list.size() - 1));
0 голосов
/ 06 декабря 2018

Вы можете использовать функцию reduce() следующим образом:

OptionalInt i = IntStream.rangeClosed(1, 5)
        .reduce((first, second) -> first == 7 ? first : second);
System.out.print(i.getAsInt());
0 голосов
/ 06 декабря 2018

если вы хотите сделать это в одном конвейере, то вы можете сделать:

int startInc = 1;
int endEx = 5;
OptionalInt first = 
       IntStream.concat(IntStream.range(startInc, endEx)
                .filter(x -> x == 7), endEx > 1 ? IntStream.of(endEx) : IntStream.empty())
                .findFirst();

но вам, вероятно, лучше собирать сгенерированные числа в список, а затем работать с ним следующим образом:

// first collect the numbers into a list
List<Integer> result = IntStream.rangeClosed(startInc,endEx)
                                   .boxed()
                                   .collect(toList());
    // then operate on it 
int value = result.stream()
                  .filter(x -> x == 7)
                  .findFirst()
                  .orElse(result.get(result.size() - 1)); 

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

List<Integer> result = IntStream.rangeClosed(startInc,endEx)
                                .boxed()
                                .collect(toList());

Optional<Integer> first = 
         Stream.concat(result.stream().filter(x -> x == 7), result.isEmpty() ? 
                Stream.empty() : Stream.of(result.get(result.size() - 1)))
                .findFirst();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...