Подтвердить что ты не робот

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

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

Итак, скажем, я хочу сгенерировать список всех треугольных чисел под 100. Треугольные числа - это числа, которые следуют формуле: (n * n + n)/2

Каков наилучший способ сделать это? В настоящее время у меня есть это:

    Stream.iterate(1, n -> n + 1).limit(100)
            .map(n -> (n * n + n) / 2)
            .filter(a -> a < 100)
            .map(a -> a + "")
            .collect(Collectors.joining(", ", "Numbers: ", "."));

Но это кажется излишне излишним с количеством вычислений. Я повторяю n от 1 до 100 (потому что позволяет предположить, что я не знаю, что такое максимальное значение для n), тогда я сопоставляю функцию номера треугольника этого списка, а затем проверяю, какие числа меньше 100. Есть ли более эффективный способ в этом? Также: могу ли я генерировать числа треугольников, используя только функцию итерации потока вместо использования итерации, предела и затем отображения?

EDIT: Итак, главный смысл здесь: как можно вычислить числа traingle, как только одно из чисел треугольника превышает 100? Обычно я писал бы вот так:

ArrayList<Integer> triangles = new ArrayList<>(); 
for (int n=1;true;n++) {
    int num = (n*n+n)/2;

    if (num>100) break;

    triangles.add(num);
}

который останавливается, как только число треугольников превышает 100, что очень эффективно; как сохранить эту эффективность в лямбда-выражении?

4b9b3361

Ответ 1

В общем случае то, что вы ищете, занимает время. К сожалению, он не имеет реализации по умолчанию в потоках Java 8. См. вопрос о взятии.

Ответ 2

Если все, что вы хотите сделать, это преобразовать данную последовательность в треугольник (как вы описали), это делает его намного проще.

List<Integer> l = IntStream.rangeClosed(1, 100)
            .mapToObj(n -> (n*n + n) / 2)
            .collect(Collectors.toList());

примитивным потоковым оболочкам нужен дополнительный шаг для преобразования в объекты, поэтому метод mapToObj.

Если вы хотите остановить фильтрацию, когда вы нажмете 100, самым простым способом, о котором я могу думать, является

    IntFunction<Integer> calc =n -> (n*n+n) / 2; 
    List<Integer> l = IntStream.rangeClosed(1, 100)
            .filter(n -> calc.apply(n) < 100)
            .mapToObj(calc)
            .collect(Collectors.toList());

Основываясь на изменениях на ваш вопрос, я думаю, что это также очень важно отметить. Если вы хотите отразить то, что вы делали, это будет выглядеть так:

    List<Integer> results = new ArrayList<>(100);
    IntStream.rangeClosed(1, 100).forEach(i -> {
        int tri =calc.apply(i);
        if(tri < 100) {
            results.add(tri);
        }
    });

Стоит отметить, что потоки не обязательно упорядочены (хотя реализация по умолчанию следует за итератором). Если бы это было преобразовано в параллельный поток, вы увидели бы разницу (и мощность потоков). Вы не можете отказаться от выполнения, потому что тогда вы принимаете определенную сумму в отношении порядка обработки. Отфильтровывая ранние (в моей второй форме), вы убедитесь, что в итоге вы получите итоговый поток из 13 записей в нем до окончательного расчета. Возьмите эту параллельную опцию в качестве заметки.

    List<Integer> l = IntStream.rangeClosed(1, 100).parallel()
            .filter(n -> calc.apply(n) < 100)
            .mapToObj(calc)
            .collect(Collectors.toList());

Вы увидите, что они все еще заказываются, но их вычисление выполняется по нескольким потокам.