面试题答案
一键面试可能遇到的问题
在并行流中使用Stream.collect
方法构建StringBuilder
会遇到线程安全问题。因为StringBuilder
不是线程安全的,多个线程同时对其进行操作(比如在并行流中每个线程都尝试追加字符),会导致最终结果不一致,出现数据竞争问题。
原理
并行流会将流中的元素分成多个部分,由不同的线程并行处理。当使用collect
方法时,每个线程会独立地对StringBuilder
进行操作。例如,线程A可能在StringBuilder
的位置1追加字符,同时线程B也在相同或其他位置追加字符,由于没有同步机制,最终结果会混乱。
解决方法
- 使用线程安全的
StringBuffer
:StringBuffer
是线程安全的,它的方法内部使用synchronized
关键字保证线程安全。但是由于同步机制,它的性能相对StringBuilder
较差。 - 使用
Collectors.joining
:这是java.util.stream.Collectors
类提供的静态方法,专门用于拼接字符串。它在内部处理了并行流的情况,能确保结果正确。
代码示例
使用StringBuffer
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
public class ParallelStreamStringBuilderExample {
public static void main(String[] args) {
List<String> words = Arrays.asList("Hello", "World", "Java");
AtomicReference<StringBuffer> result = new AtomicReference<>(new StringBuffer());
words.parallelStream()
.forEach(word -> result.get().append(word));
System.out.println(result.get().toString());
}
}
使用Collectors.joining
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class ParallelStreamJoiningExample {
public static void main(String[] args) {
List<String> words = Arrays.asList("Hello", "World", "Java");
String result = words.parallelStream()
.collect(Collectors.joining());
System.out.println(result);
}
}
在上述代码中,第一个示例使用AtomicReference
包装StringBuffer
,确保在并行流中能正确更新。第二个示例直接使用Collectors.joining
方法,这种方式更加简洁且性能较好。