Code Smell : Iteration : 迭代

问题概述

当你看到通过for循环或者streams-api遍历集合的代码时,需要明确知道迭代遍历是必须的(意味着对集合中的每个元素都进行了必要的逻辑处理).如果遍历一个数据结构总是为了从中查找某个值,那么最好将集合转换为Set或Map.这样做不仅可以令代码更简洁已读,而且还能提供更好的性能.

每当选择使用一种数据结构时,都要考虑一下对该数据结构的更新(add/update)只读操作的频率,根据读写操作的占比和更新操作的类型来决定使用何种数据结构会更高效.举例来说:

  • 根据key寻找value当然是用Map
  • 检查value是否存在用Set
  • 使用索引进行随机写入使用ArrayList,顺序写入任意位置增删元素用LinkedList
  • 更多

全部都是常识.

症状

  • 使用for循环遍历列表来查找某个符合条件的值
  • 使用streams-api调用findFirst/findAny/anyMatch

解决方案

  • 如果总是通过for循环遍历来检查某个值是否存在于List,可以想想List中的元素是否唯一,如果是可以使用Set进行替换.

  • 如果总是查找一个具有特定属性值的对象,可以考虑使用以属性值为key的Map进行替换,可以提升查找效率.

  • java.util.Collections API提供了非常多有用的方法,如果你无法变更数据结果,可以考虑看看看Collections中有没有方法可以替换循环迭代的代码.例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    //代码片段1
    for (String s : getLoadNames()) {
    if (storedName.equals(s)) {
    return true;
    }
    }
    return false;

    //可以改为如下代码
    return Collections.contains(getLoadNames,storedName);

    //代码片段2
    Iterator<String> iter = getLoadNames().iterator();
    while(iter.hasNext()){
    String name = iter.next();
    if(storedName.equals()){
    iter.remove();
    }
    }

    //可以改为
    Collections.removeIf(name->storedName.equals(name))

    示例代码中的转换,IntellijIdea IDE提供了快速识别转换的能力,通常在编辑器中会自动标注可以转换的代码,只需要按下Alt+Enter就可以快速转换为Collections API代码.

  • 如果确实需要对数据结构进行遍历,最好将遍历代码封装的业务领域对象的方法中,然后给它起一个容易识别的名字.