設計模式學習筆記(十六)迭代器模式及其在Java 容器中的應用

歸斯君發表於2022-04-06

迭代器(Iterator)模式,也叫做遊標(Cursor)模式。我們知道,在Java 容器中,為了提高容器遍歷的方便性,把遍歷邏輯從不同型別的集合類中抽取出來,避免向外部暴露集合容器的內部結構。

一、迭代器模式介紹

迭代器模式也就是提供一個物件來順序訪問聚合物件中的一系列資料,而不暴露聚合物件的內部表示。它是一種行為型模式,下面就來看看迭代器模式的結構:

1.1 迭代器模式的結構

迭代器模式的結構很簡單,就是將聚合物件中的遍歷行為分離,並抽象成迭代器類來實現:

image-20220406161104154

  • Aggregate:抽象聚合介面,定義對聚合物件的一些操作和建立迭代器物件的介面
  • Iterator:抽象迭代器介面,定義訪問和遍歷聚合元素的介面
  • Aggregate1:具體聚合實現,實現抽象聚合介面,返回一個具體迭代器例項物件
  • Iterator1:具體迭代器實現,實現抽象迭代器介面中所定義的方法

1.2 迭代器模式的實現

根據上面的類圖,可以實現如下程式碼:

/**
 * @description: 抽象聚合介面
 * @author: wjw
 * @date: 2022/4/6
 */
public interface Aggregate {
    /**
     * 增加物件
     * @param obj 物件
     */
    void add(Object obj);

    /**
     * 移除物件
     * @param obj 物件
     */
    void remove(Object obj);

    /**
     * 呼叫迭代器
     * @return 迭代器
     */
    Iterator getIterator();
}

/**
 * @description: 具體迭代器類
 * @author: wjw
 * @date: 2022/4/6
 */
public class Aggregate1 implements Aggregate{

    private List<Object> list = new ArrayList<>();

    @Override
    public void add(Object obj) {
        list.add(obj);
    }

    @Override
    public void remove(Object obj) {
        list.remove(obj);
    }

    @Override
    public Iterator getIterator() {
        return new Iterator1(list);
    }
}
/**
 * @description: 抽象迭代器
 * @author: wjw
 * @date: 2022/4/6
 */
public interface Iterator {

    /**
     * 呼叫第一個物件
     * @return 物件
     */
    Object first();

    /**
     * 呼叫下一個物件
     * @return 物件
     */
    Object next();

    /**
     * 迭代器中是否還有下一個物件
     * @return
     */
    boolean hasNext();

}

/**
 * @description: 具體迭代器類
 * @author: wjw
 * @date: 2022/4/6
 */
public class Iterator1 implements Iterator{

    private List<Object> list = null;
    private int index = -1;

    public Iterator1(List<Object> list) {
        this.list = list;
    }

    @Override
    public Object first() {
        index = 0;
        Object obj = list.get(index);
        return obj;
    }

    @Override
    public Object next() {
        Object obj = null;
        if (this.hasNext()) {
            obj = list.get(++index);
        }
        return obj;
    }

    @Override
    public boolean hasNext() {
        if (index < list.size() - 1) {
            return true;
        } else {
            return false;
        }
    }
}
/**
 * @description: 客戶端類
 * @author: wjw
 * @date: 2022/4/6
 */
public class Client {
    public static void main(String[] args) {
        Aggregate1 aggregate1 = new Aggregate1();
        aggregate1.add("A");
        aggregate1.add("B");
        aggregate1.add("C");
        System.out.println("聚合物件有:");
        Iterator iterator = aggregate1.getIterator();
        while (iterator.hasNext()) {
            Object obj = iterator.next();
            System.out.println(obj.toString());
        }
        Object first = iterator.first();
        System.out.println("第一個聚合物件是:" + first.toString());
    }
}

客戶端測試場結果為:

聚合物件有:
A
B
C
第一個聚合物件是:A

二、迭代器模式的應用場景

2.1 Java 集合容器

Java 集合容器中的使用就是容器中的迭代器了,以ArrayList為例,ArrayList是繼承Collection的:

image-20220406193326506

我們發現ArrayList類裡面實現迭代器介面的內部類:

image-20220406192935330

其中Itr實現IteratorListItr繼承Itr並實現ListIteratorListIteratorIterator功能的擴充套件。所以實際上ArrayList是抽象聚合和抽象迭代器兩者的具體實現,可以畫出大致結構圖如下:

image-20220406194540279

舉個使用ArrayList的例子:

public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<>();
    list.add("A");
    list.add("B");
    list.add("C");
    Iterator<String> iterator = list.iterator();
    System.out.println("ArrayList中的聚合物件為:");
    while (iterator.hasNext()) {
        String next = iterator.next();
        System.out.println(next);
    }
}

輸出結果:

ArrayList中的聚合物件為:
A
B
C

在日常業務的開發中,迭代器模式使用的場景並不多,下面就來看看關於迭代器的實戰

三、迭代器模式實戰

在本案例中模擬迭代遍歷輸出公司中樹形結構的組織結構關係中僱員列表:

公司案例

利用迭代器模式實現的結構如下:

image-20220406200217207

上面結構是以Java容器中迭代器模式基礎構建的,左邊是迭代器的定義,右邊是實現的迭代器功能。具體程式碼結構圖如下:

├─src
│  ├─main
│  │  ├─java
│  │  │  ├─group
│  │  │  │      Employee.java
│  │  │  │      GroupStructure.java
│  │  │  │      Link.java
│  │  │  │
│  │  │  └─lang
│  │  │          Collection.java
│  │  │          Iterable.java
│  │  │          Iterator.java
│  │  │
│  │  └─resources
│  └─test
│      └─java
│              ApiTest.java

對於lang包下是迭代器實現部分,具體程式碼如下:

/**
 * @description: 可迭代介面定義
 * @author: wjw
 * @date: 2022/4/6
 */
public interface Iterator<E> {

    boolean hasNext();

    E next();
}
/**
 * @description:
 * @author: wjw
 * @date: 2022/4/6
 */
public interface Iterable<E> {

    Iterator<E> iterator();
}
/**
 * @description: 集合功能介面
 * @author: wjw
 * @date: 2022/4/6
 */
public interface Collection<E, L> extends Iterable<E> {

    boolean add(E e);

    boolean remove(E e);

    boolean addLink(String key, L l);

    boolean removeLink(String key);

    /**
     * 繼承Iterable介面的方法
     * @return Iterator
     */
    @Override
    Iterator<E> iterator();
}

group包下是組織結構以及聚類物件及其實現,具體程式碼如下所示:

/**
 * @description: 僱員類
 * @author: wjw
 * @date: 2022/4/6
 */
public class Employee {

    private String uId;
    private String name;
    private String desc;

    public Employee(String uId, String name, String desc) {
        this.uId = uId;
        this.name = name;
        this.desc = desc;
    }

    public String getuId() {
        return uId;
    }

    public void setuId(String uId) {
        this.uId = uId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
}
/**
 * @description: 樹節點鏈路
 * @author: wjw
 * @date: 2022/4/6
 */
public class Link {

    private String fromId;
    private String toId;

    public Link(String fromId, String toId) {
        this.fromId = fromId;
        this.toId = toId;
    }

    public String getFromId() {
        return fromId;
    }

    public void setFromId(String fromId) {
        this.fromId = fromId;
    }

    public String getToId() {
        return toId;
    }

    public void setToId(String toId) {
        this.toId = toId;
    }
}
/**
 * @description: 迭代器功能實現
 * @author: wjw
 * @date: 2022/4/6
 */
public class GroupStructure implements Collection<Employee, Link> {

    /**組織ID*/
    private String groupId;

    /**組織名稱*/
    private String groupName;

    /**僱員列表*/
    private Map<String, Employee> employeeMap = new ConcurrentHashMap<>();

    /**組織架構關係*/
    private Map<String, List<Link>> linkMap = new ConcurrentHashMap<>();

    /**反向關係鏈*/
    private Map<String, String> invertedMap = new ConcurrentHashMap<>();

    public GroupStructure(String groupId, String groupName) {
        this.groupId = groupId;
        this.groupName = groupName;
    }

    @Override
    public boolean add(Employee employee) {
        return null != employeeMap.put(employee.getuId(), employee);
    }

    @Override
    public boolean remove(Employee employee) {
        return null != employeeMap.remove(employee.getuId());
    }

    @Override
    public boolean addLink(String key, Link link) {
        invertedMap.put(link.getToId(), link.getFromId());
        if (linkMap.containsKey(key)) {
            return linkMap.get(key).add(link);
        } else {
            List<Link> links = new LinkedList<>();
            links.add(link);
            linkMap.put(key, links);
            return true;
        }
    }

    @Override
    public boolean removeLink(String key) {
        return null != linkMap.remove(key);
    }

    @Override
    public Iterator<Employee> iterator() {
        return new Iterator<Employee>() {

            HashMap<String, Integer> keyMap = new HashMap<>();
            int totalIdx = 0;
            //僱員ID,From
            private String fromId = groupId;
            //僱員ID,To
            private String toId = groupId;

            @Override
            public boolean hasNext() {
                return totalIdx < employeeMap.size();
            }

            @Override
            public Employee next() {
                List<Link> links = linkMap.get(toId);
                int cursorIdx = getCursorIdx(toId);

                //同級掃描
                if (null == links) {
                    cursorIdx = getCursorIdx(fromId);
                    links = linkMap.get(fromId);
                }
                //上級節點掃描
                while (cursorIdx > links.size() - 1) {
                    fromId = invertedMap.get(fromId);
                    cursorIdx = getCursorIdx(fromId);
                    links = linkMap.get(fromId);
                }

                //獲取節點
                Link link = links.get(cursorIdx);
                toId = link.getToId();
                fromId = link.getFromId();
                totalIdx++;

                //返回最終結果
                return employeeMap.get(link.getToId());
            }

            //給每個層級定義寬度遍歷進度
            public int getCursorIdx(String key) {
                int idx = 0;
                if (keyMap.containsKey(key)) {
                    idx = keyMap.get(key);
                    keyMap.put(key, ++idx);
                } else {
                    keyMap.put(key, idx);
                }
                return idx;
            }
        };
    }
}

最後是測試類及其結果:

/**
 * @description: 單元測試類
 * @author: wjw
 * @date: 2022/4/6
 */
public class ApiTest {

    private Logger logger = LoggerFactory.getLogger(ApiTest.class);

    @Test
    public void test_iterator() {
        GroupStructure groupStructure = new GroupStructure("1", "ethan");
        groupStructure.add(new Employee("2", "花花", "二級部門"));
        groupStructure.add(new Employee("3", "豆包", "二級部門"));
        groupStructure.add(new Employee("4", "蹦蹦", "三級部門"));
        groupStructure.add(new Employee("5", "大燒", "三級部門"));
        groupStructure.add(new Employee("6", "虎哥", "四級部門"));
        groupStructure.add(new Employee("7", "玲姐", "四級部門"));
        groupStructure.add(new Employee("8", "秋雅", "四級部門"));

        //新增節點連結
        groupStructure.addLink("1", new Link("1", "2"));
        groupStructure.addLink("1", new Link("1", "3"));

        groupStructure.addLink("2", new Link("2", "4"));
        groupStructure.addLink("2", new Link("2", "5"));

        groupStructure.addLink("5", new Link("5", "6"));
        groupStructure.addLink("5", new Link("5", "7"));
        groupStructure.addLink("5", new Link("5", "8"));

        Iterator<Employee> iterator = groupStructure.iterator();
        while (iterator.hasNext()) {
            Employee employee = iterator.next();
            logger.info("{},僱員 Id: {} Name: {}", employee.getDesc(), employee.getuId(), employee.getName());
        }
    }
}
21:50:11.087 [main] INFO  ApiTest - 二級部門,僱員 Id: 2 Name: 花花
21:50:11.089 [main] INFO  ApiTest - 三級部門,僱員 Id: 4 Name: 蹦蹦
21:50:11.089 [main] INFO  ApiTest - 三級部門,僱員 Id: 5 Name: 大燒
21:50:11.089 [main] INFO  ApiTest - 四級部門,僱員 Id: 6 Name: 虎哥
21:50:11.089 [main] INFO  ApiTest - 四級部門,僱員 Id: 7 Name: 玲姐
21:50:11.089 [main] INFO  ApiTest - 四級部門,僱員 Id: 8 Name: 秋雅
21:50:11.089 [main] INFO  ApiTest - 二級部門,僱員 Id: 3 Name: 豆包

參考資料

《重學Java設計模式》

http://c.biancheng.net/view/1395.html

相關文章