设计模式 - 迭代器模式

迭代器模式:为集合提供一套统一的遍历方式,而不暴露集合内部的数据结构细节
核心:实现Iterator{hasNext(),next()}接口,为集合数据遍历提供统一的方式

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()在遍历上性能低于常规遍历方式,但其灵活性和代码简洁性完全优于常规遍历(并行流性能需根据实际业务情况判断)

posted @ 2020-11-29 22:47  祁奇  阅读(124)  评论(0编辑  收藏  举报