簡體   English   中英

如何實現嵌套列表迭代器

[英]How to implement a Nested List Iterator

我在一次采訪中被問到這個問題:

“如何實現自定義嵌套列表迭代器?”

方法next()應該首先返回每個列表的第一個元素,然后是第二個元素,依此類推。

輸入示例

[[1,2,3],[4,5],[6],[],[7,8,9]]

Output

[1,4,6,7,2,5,8,3,9]

存根代碼

public class CustomListIterator implements Iterator<Integer> {
    
    public CustomListIterator(List<List<Integer>> nestedList) {
        
    }

    @Override
    public boolean hasNext() {
        
    }
    
    @Override
    public Integer next() {
        
    }
}

如何實施?

您可以通過使用ListIteratorIterator的組合來實現。 這種方法可以讓您擺脫維持任何職位的必要性。

總體思路是創建一個迭代器列表和一個迭代這些列表的listIterator

方法hasNext()對列表執行迭代並檢查它是否至少有一個未耗盡的迭代器。

方法next()更復雜一些。 首先,它將嘗試將listIterator從最后一個元素后面的position“重置”到列表第一個元素之前的position。

然后在循環內部,它會檢查列表中的下一個迭代器,如果這個迭代器用盡,它將被刪除。 為了使此操作快速進行,迭代器存儲在LinkedList中。 如果找到該元素,它將被返回。

為了處理“空”迭代器出現在列表末尾並且listIterator到達最后一個 position 但列表中可能還有一些未訪問的迭代器的情況,在循環的最后放置了一個額外的檢查。

在下面的實現中,為了簡潔起見,我在hasNext()和構造函數中使用了流。 即使您對流不太熟悉,整體邏輯也應該清晰,並且您可以輕松地將其替換為流。

public class CustomListIterator<T> implements Iterator<T> {
    private List<Iterator<T>> iterators;
    private ListIterator<Iterator<T>> listIterator;
    
    public CustomListIterator(List<List<T>> nestedList) {
        this.iterators = nestedList.stream()
            .map(List::iterator)
            .collect(Collectors.toCollection(LinkedList::new));
        
        this.listIterator = iterators.listIterator();
    }
    
    @Override
    public boolean hasNext() {        
        return iterators.stream().anyMatch(Iterator::hasNext);
    }
    
    @Override
    public T next() {
        if (!iterators.isEmpty() && !listIterator.hasNext()) tryReset();

        while (!iterators.isEmpty() && listIterator.hasNext()) {
            Iterator<T> current = listIterator.next();

            if (!current.hasNext()) {
                listIterator.remove(); // removing exhausted iterator
            } else {
                return current.next();
            }
    
            if (!listIterator.hasNext()) tryReset();
        }
        throw new IllegalStateException();
    }
    
    private void tryReset() {
        while (listIterator.hasPrevious()) {
            listIterator.previous();
        }
    }
}

main() - 演示

public static void main(String[] args) {
    List<List<Integer>> numbers =
        List.of(List.of(1, 4, 6, 7),
                List.of(),
                List.of(2, 5),
                List.of(3));

    CustomListIterator<Integer> nestedIterator = new CustomListIterator<>(numbers);
    
    while (nestedIterator.hasNext()) {
        System.out.print(nestedIterator.next() + "\t");
    }
}

Output

1   2   3   4   5   6   7   

要實現一個迭代器,我們需要一個 cursor 來跟蹤我們當前所在的元素。 根據底層數據結構,cursor 可以是索引或指針,它將用於從一個元素前進到另一個元素。 這是在 next() 方法中完成的,該方法返回當前元素,cursor 前進到下一個元素。

因為在您的問題中,您要求為通用列表提供自定義迭代器(不是專門的 ArrayList 或 LinkedList); 我決定使用一個 int 索引作為 cursor 來指向兩個數據結構的元素。

為了回答您的問題,我將您的列表列表描繪為具有不同長度行的矩陣,我們首先遍歷當前列的行(外部列表光標),然后移動到下一列(內部列表或子列表光標)。

class CustomListIteratorVert implements Iterator<Integer> {

    private List<List<Integer>> nestedList;
    private int listCur, subListCur;

    //value to understand when the iteration has completed since the "column" cursor has reached its end
    private int maxSublistSize;

    public CustomListIteratorVert(List<List<Integer>> nestedList) {
        this.nestedList = nestedList;
        this.listCur = 0;
        this.subListCur = 0;

        //Identifying the greates size among the sublists
        this.maxSublistSize = nestedList.stream().map(list -> list.size()).max(Integer::compareTo).orElse(0);
    }

    @Override
    public boolean hasNext() {
        //If the sublist cursor hasn't exceeded the last "column" then there are further elements to iterate (worst case scenario: only the current element)
        return subListCur < maxSublistSize;
    }

    @Override
    public Integer next() {
        //If the last column has been exceeded (no further elements to iterate) then no value is returned
        if (subListCur == maxSublistSize) {
            return null;
        }

        //Saving the current value to return
        Integer value = nestedList.get(listCur).get(subListCur);

        //Incrementing the outer list cursor to point to the next "row" (matrix visualization)
        if (listCur < nestedList.size() - 1) {
            listCur++;
        } else {
            //Incrementing the sublist cursors to point to the next column and resetting the "row" index (matrix visualization)
            subListCur++;
            listCur = 0;
        }

        // In case there are still elements left and the current cursors do not point to an existing element (sublist cursor greater than the sublist's size)
        // then these "empty spots" are skipped as long as the last column hasn't been exceeded or the next element hasn't been met
        while (subListCur < maxSublistSize && subListCur >= nestedList.get(listCur).size()) {
            if (listCur < nestedList.size() - 1) {
                listCur++;
            } else {
                subListCur++;
                listCur = 0;
            }
        }

        return value;
    }
}

public class Main {
    public static void main(String[] args) {
        //Added extra empty lists to create more "empty sposts"
        List<List<Integer>> nestedList = new ArrayList<>(List.of(List.of(1, 2, 3), List.of(4, 5), List.of(6), List.of(), List.of(7, 8, 9), List.of(), List.of()));

        System.out.println("------------ Iterator ------------");
        CustomListIteratorVert custIt = new CustomListIteratorVert(nestedList);
        while (custIt.hasNext()) {
            System.out.println(custIt.next());
        }

        
        System.out.println("\n\n------------ Flattened List foreach loop ------------");
        List<Integer> listFlattenedFor = new ArrayList<>();
        for (List<Integer> sublist : nestedList) {
            listFlattenedFor.addAll(sublist);
        }
        listFlattenedFor.forEach(i -> System.out.println(i));


        System.out.println("\n\n------------ Flattened List Stream ------------");
        List<Integer> listFlattenedStream = new ArrayList<>();
        nestedList.stream().forEach(list -> listFlattenedStream.addAll(list));
        listFlattenedFor.forEach(i -> System.out.println(i));
    }
}

編輯

我的主要內容還包含您的列表的展平,因為在問題編輯之前已經詢問過它。


警告

具有狀態 lambda 的第二種扁平化方法應僅且絕對用於非並行流。 此外,簡單的 for 循環方法不僅更安全,而且效率更高。 我剛剛還發布了 stream 方法,只是為了展示實現相同結果的不同方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM