Как работает комбайнер в методе Stream.collect в java 8?
Я создал простую демонстрацию:
public static void main(String[] args) {
List<String> list2 = Arrays.asList("adf", "bcd", "abc", "hgr", "jyt", "edr", "biu");
String collect = list2.stream().collect(String::new, (res, elem) -> {
res=res.concat(" ").concat(elem);
// System.out.printf("res=%s, elem=%s\n", res.isEmpty(), elem);
}, (res1, res2) -> {
System.out.printf("res1=%s, res2=%s\n", res1, res2);
});
System.out.println("collect=" + collect);
}
Проблема состоит в том, что BiConsumer combiner
часть collect
не запускается вообще.
Выполняется, если я использую parallelStream()
, но два аргумента res1
и res2
равны поставщику String::new
.
Как сделать combiner
работать в методе collect
?
Ответы
Ответ 1
Во-первых, нет необходимости в том, чтобы объединитель выполнялся в непараллельном потоке, так как комбинировать нечего.
Во-вторых, ваша проблема связана с использованием String::new
и String.concat
. Аккумулятор должен изменить первый аргумент, объединив второй аргумент с ним, но поскольку строки в Java неизменяемы, ваш код ничего не даст.
res=res.concat(" ").concat(elem);
создаст новую строку, а затем выбросит ее. Вместо этого вы хотите использовать StringBuilder, чтобы сохранить промежуточные результаты:
public static void main(String[] args) {
List<String> list2 = Arrays.asList("adf", "bcd", "abc", "hgr", "jyt", "edr", "biu");
String collect = list2.stream().collect(StringBuilder::new, (res, elem) -> {
res.append(" ").append(elem);
}, (res1, res2) -> {
res1.append(res2.toString());
System.out.printf("res1=%s, res2=%s\n", res1, res2);
}).toString();
System.out.println("collect=" + collect);
}
Это также будет корректно работать с параллельным потоком
res1 = hgr jyt, res2 = jyt
res1 = bcd abc, res2 = abc
res1 = adf bcd abc, res2 = bcd abc
res1 = edr biu, res2 = biu
res1 = hgr jyt edr biu, res2 = edr biu
res1 = adf bcd abc hgr jyt edr biu, res2 = hgr jyt edr biu
collect = adf bcd abc hgr jyt edr biu
Ответ 2
Я думаю, что combiner
используется только в параллельных потоках (для объединения частичных выходов параллельных вычислений), поэтому сделайте свой поток параллельным.
String collect = list2.parallelStream().collect(...
Ответ 3
Более интересный результат с параллелью и без нее для Raniz:
String collect = list2.stream().collect(StringBuilder::new,
(res, elem) -> {
System.out.printf("ACCUMULATE res=%s, elem=%s\n", res, elem);
res.append(" ").append(elem);
},
(res1, res2) -> {
System.out.printf("COMBINE res1=%s, res2=%s\n", res1, res2);
res1.append(res2.toString());
}).toString();
Без параллельный комбинат никогда не вызывался:
ACCUMULATE res=, elem=adf
ACCUMULATE res= adf, elem=bcd
ACCUMULATE res= adf bcd, elem=abc
ACCUMULATE res= adf bcd abc, elem=hgr
ACCUMULATE res= adf bcd abc hgr, elem=jyt
ACCUMULATE res= adf bcd abc hgr jyt, elem=edr
ACCUMULATE res= adf bcd abc hgr jyt edr, elem=biu
collect= adf bcd abc hgr jyt edr biu
И с параллелью
ACCUMULATE res=, elem=jyt
ACCUMULATE res=, elem=hgr
COMBINE res1= hgr, res2= jyt
ACCUMULATE res=, elem=biu
ACCUMULATE res=, elem=edr
COMBINE res1= edr, res2= biu
ACCUMULATE res=, elem=bcd
COMBINE res1= hgr jyt, res2= edr biu
ACCUMULATE res=, elem=abc
ACCUMULATE res=, elem=adf
COMBINE res1= bcd, res2= abc
COMBINE res1= adf, res2= bcd abc
COMBINE res1= adf bcd abc, res2= hgr jyt edr biu
collect= adf bcd abc hgr jyt edr biu