设计模式 - 迭代器模式
迭代器模式:为集合提供一套统一的遍历方式,而不暴露集合内部的数据结构细节
核心:实现Iterator
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {throw new UnsupportedOperationException("remove");}
}
ArrayList 和 LinkedList遍历性能对比
测试类型:for循环遍历,foreach遍历和iterator遍历 - foreach遍历底层实际是通过iterator接口完成的)
结论:
① foreach遍历只是语法糖,底层实现是iterator,两者性能差不多
② ArrayList的遍历性能优于linkedList,(此处应该与内存结构有关)
③ ArrayList遍历方式对性能影响不大(fori方式略优),LinkedList通过迭代器遍历性能比fori高得多
- LinkedList.for循环遍历:get(index),每次循环需从头遍历找到对应的值,时间复杂度:0.5*n^2;
- LinkedList.iterator遍历:维护了游标所指Element的引用,遍历时间复杂度:n
> Mode=吞吐量 Score=平均执行次数
> Benchmark Mode Cnt Score Error Units
> ListTest.forAscArrayList thrpt 5 40013.804 ± 236.174 ops/ms
> ListTest.forDescArrayList thrpt 5 39349.368 ± 5252.728 ops/ms
> ListTest.foreachArrayList thrpt 5 34618.842 ± 545.326 ops/ms
> ListTest.iteratorArrayList thrpt 5 34670.444 ± 301.078 ops/ms
> ListTest.foreach8ArrayList thrpt 5 33739.255 ± 2379.272 ops/ms
> ListTest.forAscLinkedList thrpt 5 3858.812 ± 43.145 ops/ms
> ListTest.forDescLinkedList thrpt 5 3885.409 ± 25.798 ops/ms
> ListTest.foreachLinkedList thrpt 5 17039.682 ± 52.282 ops/ms
> ListTest.iteratorLinkedList thrpt 5 17553.558 ± 40.178 ops/ms
> ListTest.foreach8LinkedList thrpt 5 17178.035 ± 3644.734 ops/ms
@BenchmarkMode(Mode.Throughput) // 测试类型:吞吐量
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) // 预热 5 轮,每次 1s
@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS) // 测试 5 轮,每次 2s
@Fork(1) // fork 1 个线程
@State(Scope.Thread) // 每个测试线程一个实例
public class ListTest {
private static List<String> list = null;
static {
list = new LinkedList(128);
for(int i = 0;i < 50;i++){
list.add(UUID.randomUUID().toString());
}
}
// 1. foreach遍历
@Benchmark
public static void foreachList(){
for (String j : list) {
}
}
// 2. iterator遍历
@Benchmark
public static void iteratorList(){
String j;
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
j = iterator.next();
}
}
// 3. for 递减遍历
@Benchmark
public static void forDescList(){
String j;
for(int i = list.size() - 1;i >= 0; i--){
j = list.get(i);
}
}
// 4. for 递增遍历
@Benchmark
public static void forAscList(){
String j;
int size = list.size();
for(int i = 0;i < size; i++){
j = list.get(i);
}
}
// 5. java8 新增foreach
@Benchmark
public static void foreachJava8(){
list.forEach(item -> {
});
}
}
HashMap遍历方式性能对比
1:foreach遍历只是语法糖,底层实现是iterator,两者性能差不多
2:KeySet遍历性能明显低于EntrySet,原因:keySet多有一步map.get(key)操作
3:JDK8 Stream性能需根据具体场景考虑,Stream优点:为集合的遍历和处理提供了新的方法
Benchmark Mode Cnt Score Error Units
MapTest.foreachKeySet thrpt 25 11189.487 ± 164.446 ops/ms
MapTest.iteratorKeySet thrpt 25 11286.328 ± 223.540 ops/ms
MapTest.foreachEntrySet thrpt 25 17457.300 ± 434.603 ops/ms
MapTest.iteratorEntrySet thrpt 25 17159.188 ± 961.122 ops/ms
MapTest.foreachjava8 thrpt 25 18068.674 ± 92.609 ops/ms
MapTest.parallelStream thrpt 25 76.718 ± 5.382 ops/ms
MapTest.stream thrpt 25 7361.508 ± 119.650 ops/ms
@BenchmarkMode(Mode.Throughput) // 测试类型:吞吐量
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) // 预热 5 轮,每次 1s
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) // 测试 5 轮,每次 2s
//@Fork(1) // fork 1 个线程
@State(Scope.Thread) // 每个测试线程一个实例
public class MapTest {
private static HashMap<String,Object> map = null;
static {
map = new HashMap<>(128);
for(int i = 0;i < 10;i++){
map.put(UUID.randomUUID().toString(),UUID.randomUUID());
}
}
// 1. foreach entrySet遍历
@Benchmark
public static void foreachEntrySet(){
// 1. EntrySet - foreach
String key;
Object value;
for (Map.Entry<String, Object> entry : map.entrySet()){
key = entry.getKey();
value = entry.getValue();
}
}
// 2. iterator entrySet遍历
@Benchmark
public static void iteratorEntrySet(){
String key;
Object value;
Map.Entry<String,Object> entry;
Iterator<Map.Entry<String,Object>> entrySetIterator = map.entrySet().iterator();
while (entrySetIterator.hasNext()){
entry = entrySetIterator.next();
key = entry.getKey();
value = entry.getValue();
}
}
// 3. foreach keySet遍历
@Benchmark
public static void foreachKeySet(){
// 1. EntrySet - foreach
Object value;
for (String key : map.keySet()){
value = map.get(key);
}
}
// 4. iterator keySet遍历
@Benchmark
public static void iteratorKeySet(){
String key;
Object value;
Iterator<String> keySetIterator = map.keySet().iterator();
while (keySetIterator.hasNext()){
key = keySetIterator.next();
value = map.get(key);
}
}
// 5. lambda遍历
@Benchmark
public static void lambda(){
map.forEach((key,value) -> {
});
}
// 6. stream串行遍历
@Benchmark
public static void stream(){
map.entrySet().stream().forEach((entry) -> {
entry.getKey();
entry.getValue();
});
}
// 7. stream并行遍历
@Benchmark
public static void parallelStream(){
map.entrySet().parallelStream().forEach((entry) -> {
entry.getKey();
entry.getValue();
});
}
}
Stream API
Java8新特性:为集合的遍历和处理提供了新的方法
Stream API主要是解决数据处理和运算问题,
特点:
- 将一系列操作构成一个管道,并且可以在管道的节点上进行处理(Element拷贝推入流中,执行中间处理,完成终止操作)
- Stream 自己不会存储元素,也不会改变源对象
总结:Stream()在遍历上性能低于常规遍历方式,但其灵活性和代码简洁性完全优于常规遍历(并行流性能需根据实际业务情况判断)
欢迎疑问、期待评论、感谢指点 -- kiqi,愿同您为友
-- 星河有灿灿,愿与之辉