2015-06-09 2 views
8

Я строй объекта с помощью простого цикла:Builder шаблон с Java 8 потоком

WebTarget target = getClient().target(u); 

for (Entry<String, String> queryParam : queryParams.entrySet()) { 
    target = target.queryParam(queryParam.getKey(), queryParam.getValue()); 
} 

Я хочу сделать то же самое, используя API Java8 потока, но я не могу понять, как это сделать. Что заставляет меня бороться, так это то, что цель переназначается каждый раз, поэтому простой .forEach() не будет работать. Я думаю, мне нужно использовать .collect() или reduce(), так как я ищу одно возвращаемое значение, но я потерян в данный момент!

ответ

6

К сожалению, в потоковом API отсутствует метод foldLeft. Причина этого объясняется Stuart Marks в this answer:

[...] Наконец, Java не обеспечивает foldLeft и foldRight операций, поскольку они подразумевают определенный порядок операций, которые по своей сути является последовательным. Это противоречит описанному выше принципу проектирования предоставления API, которые поддерживают последовательную и параллельную работу в равной степени.

В конечном итоге то, что вы пытаетесь сделать здесь, является процедурным/последовательным, поэтому я не думаю, что потоковый API подходит для этого варианта использования. Я думаю, что для каждого цикла, который вы разместили сами, так же хорошо, как и получается.

Update:

Как @TagirValeev указывает below вы можете на самом деле решить с потоком API (с помощью forEachOrdered Ваш код будет выглядеть примерно так

WebTarget[] arr = { getClient().target(u) }; 
queryParams.entrySet() 
      .stream() 
      .forEachOrdered(e -> arr[0] = arr[0].queryParam(e.getKey(), 
                  e.getValue())); 
WebTarget target = arr[0]; 

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

+0

Я нервничаю о том, что последний объединителя, даже если вы добавили 'последовательный()'. –

+1

Я тоже. Я не уверен, что это правильно. Без какого-либо эквивалента 'foldLeft', я сомневаюсь, что API потока подходит в этой ситуации. – aioobe

+2

Я думаю, что предпочел бы ответ, который прямо заявил, что потоки на самом деле не поддерживают это. –

8

Это n Ot очень трудно осуществить правильный foldLeft для Java 8 потоков:

@SuppressWarnings("unchecked") 
public static <T, U> U foldLeft(Stream<T> stream, 
           U identity, BiFunction<U, ? super T, U> accumulator) { 
    Object[] result = new Object[] { identity }; 
    stream.forEachOrdered(t -> result[0] = accumulator.apply((U) result[0], t)); 
    return (U) result[0]; 
} 

Или в типобезопасного образом:

public static <T, U> U foldLeft(Stream<T> stream, 
           U identity, BiFunction<U, ? super T, U> accumulator) { 
    class Box { 
     U value; 
     Box(U value) { this.value = value; } 
    } 
    Box result = new Box(identity); 
    stream.forEachOrdered(t -> result.value = accumulator.apply(result.value, t)); 
    return result.value; 
} 

Это правильно работает для последовательных и параллельных потоков. У вас может даже быть коэффициент усиления по скорости с использованием параллельных потоков, если в вашем потоке есть промежуточные операции без учета состояния, такие как map: в этом случае следующий элемент может обрабатываться map параллельно с текущим элементом, обработанным foldLeft. Я не согласен с тем, что такая операция не подходит для Stream API, потому что она может быть правильно выражена через уже существующий forEachOrdered.

я эту операцию в моей StreamEx библиотеке, так что вы можете использовать его как это:

WebTarget target = EntryStream.of(queryParams).foldLeft(getClient().target(u), 
     (t, entry) -> t.queryParam(entry.getKey(), entry.getValue())) 
+1

Nice. Тем не менее, ваши 3 строки потока кода выглядят чрезвычайно сложными для меня, по сравнению с циклом OP for. Предмет контейнера 'Object []' на самом деле не выглядит лямбда-идиоматическим, может быть, я ошибаюсь. – aioobe

+3

Вместо этого можно использовать AtomicReference , если вы хотите иметь безопасность типа, хотя я не уверен, что это приведет к тому, что результирующая лямбда-идиоматика будет либо: AtomicReference result = new AtomicReference <> (identity); stream.forEachOrdered (t -> result.updateAndGet (u -> accumulator.apply (u, t)); return result.get(); – srborlongan

+1

@aioobe: реализация 'foldLeft' является частью низкоуровневого библиотечного кода, так что для меня это нормально, если это выглядит не очень красиво. См., например, класс «Collectors»: там есть много похожих вещей. Для безопасности типа дополнительный класс может быть создан как «class Box {U value; Box (U v) {value = v;}} '. –

Смежные вопросы