Java IO 之 SequenceInputStream 原理解析

概述

今天給大家分享一個比較有意思的 IO 流。SequenceInputStream ,聽名字顧名思義。

SequenceInputStream 流可以把多個 InputStream 合併爲一個 InputStream . 按照指定的順序,把幾個輸入流連續地合併起來,使用起來像一個流一樣。並且使它們像單個輸入流一樣出現。每個輸入流依次被讀取,直到到達該流的末尾。然後“序列輸入流”類關閉這個流並自動地切換到下一個輸入流。

合併流的作用是將多個源合併合一個源。

使用場景

比如現在有三個文件【1.txt】、【2.txt】、【3.txt】;現在要把這三個文件按照1、2、3的順序合併成一個文件輸出到 【all.txt】文件中。

如果不知道有這個流,大家可能都是自己一個一個文件的去讀,自己合併到一個文件中。
有了這個流,我們操作起來,代碼更加優雅。

示例

public class SequenceInputStreamDemo {
    public static void sequenceStream() {
        // 創建字節輸入流對象s1,s2,s3
        try (InputStream s1 = new FileInputStream(new File("d:\\1.txt"));
                InputStream s2 = new FileInputStream(new File("d:\\2.txt"));
                InputStream s3 = new FileInputStream(new File("d:\\3.txt"));) {

            /**
             * SequenceInputStream(Enumeration<? extends InputStream> e)
             * 通過記住參數來初始化新創建的 SequenceInputStream, 該參數必須是生成運行時類型爲 InputStream
             * 對象的 Enumeration 型參數。
             */
            // 創建一個Vector類對象v
            Vector<InputStream> v = new Vector<>();
            /**
             * void addElement(E obj) 將指定的組件添加到此向量的末尾,將其大小增加 1。
             */
            // 將3個字節流對象添加到Vector容器中
            v.addElement(s1);
            v.addElement(s2);
            v.addElement(s3);
            // 獲取Vector對象中的元素
            Enumeration<InputStream> e = v.elements();
            // 將Enumeration對象中的流合併(創建一個序列流,用於合併多個字節流文件s1,s2,s3)
            try (SequenceInputStream se = new SequenceInputStream(e);
                    OutputStream os = new FileOutputStream("d:\\all.txt")) {
                byte[] b = new byte[1024];
                int len = 0;
                while ((len = se.read(b)) != -1) {
                    os.write(b, 0, len);
                    os.write("\r\n".getBytes());
                }
                System.out.println("合併成功");
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        sequenceStream();
    }
}

該示例是,把D盤目錄下的三個文件【1.txt】、【2.txt】、【3.txt】使用SequenceInputStream 流,按照 指定的文件的順序合併輸出到 【all.txt】 文件中。

SequenceInputStream 源碼解析

public class SequenceInputStream extends InputStream {
    Enumeration<? extends InputStream> e;
    InputStream in;
    ......
}

SequenceInputStream 繼承 InputStream。
定義了一個 Enumeration 的一個變量。該變量存儲的是所有需要合併的InputStream。
定義了一個 InputStream的一個變量。該變量是記錄 Enumeration 中正在讀去的 InputStream。

Enumeration
Enumeration 接口是Iterator迭代器的“古老版本”,從JDK 1.0開始,Enumeration接口就已經存在了(Iterator從JDK 1.2纔出現)。Enumeration接口只有兩個方法。

構造方法

提供兩個構造方法。
* 1、提供 Enumeration 類型的參數。
把傳過來的參數賦值給 e 變量。
並調用 nextStream() 方法。

現在的迭代器都在使用Iterator,而這裏爲啥還在使用Enumeration 呢?
因爲 Iterator 是在JDK1.2以後纔有的,而SequenceInputStream 這個流在JDK1.0就已經存在了,故使用 Enumeration 當參數。

  • 2、提供兩個 InputStream 類型的參數。
    構造中把這兩個流添加到 Vector 數組中,然後調用 elements 方法放回一個 Enumeration 類型,並賦值給 e 變量。
    並調用 nextStream() 方法。

nextStream 方法


1、判斷該流是否不爲null,不爲null 則把當前 InputStream 關閉。
2、該方法是先判斷 Enumeration 中是否有可用的元素,如果獲取 Enumeration 迭代器中下一個的元素,並賦值給 in 變量。

read 方法


讀取 InputStream 中的數據。如果當前 InputStream 讀取完了,再調用 nextStream 方法,獲取下一個流,然後繼續讀取。直到所有的流都已經讀取完畢。

close 方法


close 方法會關閉剩下沒有讀取完的所有的 InputStream。
讀取過的InputStream 會在 調用 nextStream 方法中關閉。所以在這隻需要關閉沒有讀取完的流就可以了。

看完源碼是不是感覺 SequenceInputStream 超級簡單,自己也可以實現一個。


想了解更多精彩內容請關注我的公衆號

本人簡書blog地址:http://www.jianshu.com/u/1f0067e24ff8    
點擊這裏快速進入簡書
GIT地址:http://git.oschina.net/brucekankan/
點擊這裏快速進入GIT

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章