迭代器模式(统一对集合的访问方式)

目录

前言

UML

plantuml

类图

实战代码

Iterator

ArrayList

Client

自定义迭代器

TreeNode

TreeUtils

Client


前言

在实际开发过程中,常用各种集合来存储业务数据并处理,比如使用 List,Map,Set 等等集合来存储业务数据。存储在集合中的数据,往往需要遍历集合元素再进行相应的业务处理。

不同的集合类型有不同的数据结构,遍历的方式也各不相同。

而迭代器模式,就是用来简化这项工作,让开发者不必关心底层的数据结构是如何组织的,只需关注如何取用数据。它解决了数据的获取与表示之间的耦合问题,提升了集合管理的灵活性与可维护性。

UML

plantuml

复制代码
@startuml
'https://plantuml.com/class-diagram

interface Iterator {
    + hasNext() : boolean
    + next() : type
}

class ConcreteIterator {
    + hasNext() : boolean
    + next() : type
}

class Client {
    + iterator() : Iterator
}

Iterator <|.. ConcreteIterator

Client ..> Iterator

@enduml

类图

实战代码

Iterator

JDK 提供了 Iterator 这个顶级接口,JDK 下的集合都实现了 Iterator 接口,这样在遍历集合时,便可以直接使用 iterator 来遍历集合元素,而不用关心底层的数据结构。

ArrayList

以 ArrayList 为例,内部类 Itr 实现了 Iterator 接口,iterator 方法则实例化一个迭代器返回

Client

java 复制代码
public class Client {  
    public static void main(String[] args) {  
        List<Integer> array = Arrays.asList(1, 2, 3, 4, 5);  
  
        Iterator<Integer> iterator = array .iterator();  
        while (iterator.hasNext()) {  
            System.out.println(iterator.next());  
        }  
    }  
}

自定义迭代器

如果 JDK 的集合类不满足业务需求,则需要自定义集合类,那么就需要自己实现 Iterator 接口,从而让自定义集合也能用统一的方式来遍历集合元素

以实际业务中最常见的分类树来举例,自定义 TreeNode 类,并实现 Iterator 接口

TreeNode

java 复制代码
public class TreeNode {
    String id;
    String pid;
    String value;
    List<TreeNode> children;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getPid() {
        return pid;
    }

    public void setPid(String pid) {
        this.pid = pid;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public List<TreeNode> getChildren() {
        return children;
    }

    public void setChildren(List<TreeNode> children) {
        this.children = children;
    }

    public Iterator<TreeNode> iterator() {
        return new TreeNode.Itr(this);
    }

    private class Itr implements Iterator<TreeNode> {
        TreeNode currentNode;
        private Deque<TreeNode> stack;

        public Itr(TreeNode root) {
            currentNode = root;
            stack = new LinkedList<>();

            stack.push(root);
        }

        @Override
        public boolean hasNext() {
            return !stack.isEmpty();
        }

        @Override
        public TreeNode next() {
            if (!hasNext()) {
                throw new NoSuchElementException("No more elements to iterate.");
            }

            TreeNode node = stack.pop();
            currentNode = node;

            List<TreeNode> children = node.getChildren();
            if (children != null) {
                for (int index = children.size() - 1; index >= 0; index--) {
                    stack.push(children.get(index));
                }
            }

            return currentNode;
        }

    }
}

TreeUtils

java 复制代码
public class TreeUtils {

    public final static String ROOT = "root";

    /**
     * 递归构造树
     *
     * @param sources 按parentId分类的节点
     * @param parentId 父id,根节点父id为null
     * @param getId 获取id
     * @param setChildren 设置子节点
     * @return 根节点集合
     * @param <T> 节点类
     * @param <R> id类型
     */
    public static <T, R> List<T> buildTree(Map<R, List<T>> sources, R parentId,
                                           Function<T, R> getId, BiConsumer<T, List<T>> setChildren) {
        List<T> nodes = sources.getOrDefault(parentId, emptyList());

        for (T node : nodes) {
            List<T> subNodes = buildTree(sources, getId.apply(node), getId, setChildren);
            setChildren.accept(node, subNodes);
        }

        return nodes;
    }

}

Client

java 复制代码
public class Client {

    public static void main(String[] args) {
        TreeNode root = new TreeNode();
        root.setId("1");
        root.setPid(null);
        root.setValue("root");

        TreeNode child1 = new TreeNode();
        child1.setId("2");
        child1.setPid("1");
        child1.setValue("child1");

        TreeNode child2 = new TreeNode();
        child2.setId("3");
        child2.setPid("1");
        child2.setValue("child2");

        TreeNode child3 = new TreeNode();
        child3.setId("4");
        child3.setPid("2");
        child3.setValue("child3");

        //模拟从数据库中查到的节点数据
        List<TreeNode> nodes = Arrays.asList(root, child1, child2, child3);
        //按父节点分类
        Map<String, List<TreeNode>> sources = nodes.stream()
                .collect(groupingBy(e -> Objects.isNull(e.getPid()) ? ROOT : e.getPid()));
        //构造树
        List<TreeNode> tree = TreeUtils.buildTree(sources, ROOT, TreeNode::getId, TreeNode::setChildren);

        for (TreeNode node : tree) {
            Iterator iterator = node.iterator();
            while (iterator.hasNext()) {
                TreeNode child = (TreeNode) iterator.next();
                System.out.println(child.getId() + " " + child.getValue());
            }
        }
    }
}
相关推荐
腥臭腐朽的日子熠熠生辉33 分钟前
解决maven失效问题(现象:maven中只有jdk的工具包,没有springboot的包)
java·spring boot·maven
ejinxian35 分钟前
Spring AI Alibaba 快速开发生成式 Java AI 应用
java·人工智能·spring
杉之40 分钟前
SpringBlade 数据库字段的自动填充
java·笔记·学习·spring·tomcat
圈圈编码1 小时前
Spring Task 定时任务
java·前端·spring
俏布斯1 小时前
算法日常记录
java·算法·leetcode
27669582921 小时前
美团民宿 mtgsig 小程序 mtgsig1.2 分析
java·python·小程序·美团·mtgsig·mtgsig1.2·美团民宿
爱的叹息1 小时前
Java 连接 Redis 的驱动(Jedis、Lettuce、Redisson、Spring Data Redis)分类及对比
java·redis·spring
程序猿chen1 小时前
《JVM考古现场(十五):熵火燎原——从量子递归到热寂晶壁的代码涅槃》
java·jvm·git·后端·java-ee·区块链·量子计算
松韬2 小时前
Spring + Redisson:从 0 到 1 搭建高可用分布式缓存系统
java·redis·分布式·spring·缓存
绝顶少年2 小时前
Spring Boot 注解:深度解析与应用场景
java·spring boot·后端