Ответ 1
В общем случае то, что вы ищете, занимает время. К сожалению, он не имеет реализации по умолчанию в потоках 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, что очень эффективно; как сохранить эту эффективность в лямбда-выражении?
В общем случае то, что вы ищете, занимает время. К сожалению, он не имеет реализации по умолчанию в потоках Java 8. См. вопрос о взятии.
Если все, что вы хотите сделать, это преобразовать данную последовательность в треугольник (как вы описали), это делает его намного проще.
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());
Вы увидите, что они все еще заказываются, но их вычисление выполняется по нескольким потокам.