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

目录

前言

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());
            }
        }
    }
}
相关推荐
IT古董31 分钟前
【权限管理】Apache Shiro学习教程
java·apache·shiro·权限
风月歌33 分钟前
基于Spring Boot的海滨体育馆管理系统的设计与实现
java·spring boot·后端
ggdpzhk5 小时前
idea 编辑竖列:alt +shift+insert
java·ide·intellij-idea
hikktn5 小时前
Java 兼容读取WPS和Office图片,结合EasyExcel读取单元格信息
java·开发语言·wps
迪迦不喝可乐5 小时前
软考 高级 架构师 第十一章 面向对象分析 设计模式
java·设计模式
檀越剑指大厂6 小时前
【Java基础】使用Apache POI和Spring Boot实现Excel文件上传和解析功能
java·spring boot·apache
苹果酱05676 小时前
Golang的网络流量分配策略
java·spring boot·毕业设计·layui·课程设计
孑么6 小时前
GDPU Android移动应用 重点习题集
android·xml·java·okhttp·kotlin·android studio·webview
苹果7 小时前
C++二十三种设计模式之迭代器模式
c++·设计模式·迭代器模式
摇光937 小时前
js迭代器模式
开发语言·javascript·迭代器模式