博客 / 詳情

返回

8、SequenceInputStream的源碼和Vector.class的一些函數説明(windows操作系統,JDK8)

一、SequenceInputStream源碼——可以順序讀取多個輸入Stream的裝飾器類

  SequenceInputStream.class 的UML關係圖,如下所示:
clipboard

  SequenceInputStream.class的源碼,如下所示:

package java.io;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Vector;

public
class SequenceInputStream extends InputStream {
    //順序(序列化)裝載多個被裝飾輸入Stream的集合,一般是Vector實例
    Enumeration<? extends InputStream> e;
    InputStream in;//順序(序列化)裝載多個被裝飾輸入Stream的集合中當前正在被SequenceInputStream 對象使用的被裝飾的輸入Stream
    
    //構造函數,傳入一個順序(序列化)裝載多個被裝飾輸入Stream的集合
    public SequenceInputStream(Enumeration<? extends InputStream> e) {
        this.e = e;
        try {
            nextStream();
        } catch (IOException ex) {
            // This should never happen
            throw new Error("panic");
        }
    }
    
    //構造函數,可以將2個被裝飾的輸入Stream放入到集合中
    public SequenceInputStream(InputStream s1, InputStream s2) {
        Vector<InputStream> v = new Vector<>(2);

        v.addElement(s1);
        v.addElement(s2);
        e = v.elements();
        try {
            nextStream();
        } catch (IOException ex) {
            // This should never happen
            throw new Error("panic");
        }
    }
    //獲取集合中下一個被裝飾的輸入Stream
    final void nextStream() throws IOException {
        if (in != null) {
            in.close();//先關閉當前被裝飾的輸入Stream
        }

        if (e.hasMoreElements()) {//如果集合中還有被裝飾的輸入Stream
            in = (InputStream) e.nextElement();//獲取集合中下一個被裝飾的輸入Stream
            if (in == null)
                throw new NullPointerException();//如果集合中下一個被裝飾的輸入Stream為null,拋出一個NullPointerException
        }
        else in = null;//如果集合中沒有了被裝飾的輸入Stream,將當前正在使用的被裝飾的輸入Stream置為null

    }
    
    //判斷當前正在使用的被裝飾的輸入Stream是否還有可以讀取的字節數據
    public int available() throws IOException {
        if (in == null) {
            return 0; // no way to signal EOF from available()
        }
        return in.available();
    }
    
    //從SequenceInputStream 對象的集合(該集合放着多個被裝飾的輸入Stream)中讀取1個字節
    public int read() throws IOException {
        while (in != null) {//如果in!=null,則説明當前這個SequenceInputStream 對象的集合中還有被裝飾的輸入Stream沒有關閉
            int c = in.read();//從當前正在使用的被裝飾的被裝飾輸入Stream中讀取1個字節
            if (c != -1) {//c != -1説明從當前正在使用的被裝飾輸入Stream中讀取到了字節
                return c;//返回讀取到的這個字節
            }
            nextStream();//如果c==-1説明當前正在使用的被裝飾輸入Stream中字節(byte)數據已經讀完,獲取SequenceInputStream 對象的集合中下一個被裝飾的輸入Stream
        }
        return -1;//如果SequenceInputStream 對象的集合中所有被裝飾的輸入Stream中的字節(byte)數據都已經讀完,返回-1
    }

    //從SequenceInputStream 對象的集合(該集合放着多個被裝飾的輸入Stream)中讀取len個字節,放入到byte[]數組b的[off,off+len)(左閉右開,不包括off+len)索引位置
    public int read(byte b[], int off, int len) throws IOException {
        if (in == null) {//如果in==null,則説明當前這個SequenceInputStream 對象的集合中所有被裝飾的輸入Stream都已經關閉
            return -1;
        } else if (b == null) {
            throw new NullPointerException();//如果byte[]數組b==null,拋出一個NullPointerException
        } else if (off < 0 || len < 0 || len > b.length - off) {//相當於off + len > b.length(源碼中這樣寫代碼的好處我沒看出來)
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;//要從SequenceInputStream 對象的集合(該集合放着至少2個被裝飾的輸入Stream)中讀取的len個字節==0時,返回0
        }
        do {
            int n = in.read(b, off, len);//從當前正在使用的被裝飾的輸入Stream中讀取len個字節,放入到byte[]數組b的[off,off+len)(左閉右開,不包括off+len)索引位置
            if (n > 0) {
                return n;//只要能從當前正在使用的被裝飾的輸入Stream中讀取到字節,則返回讀取的數量
            }
            nextStream();//此時n==-1,説明當前正在使用的被裝飾的輸入Stream中字節(byte)數據已經讀完,獲取SequenceInputStream 對象的集合中下一個被裝飾的輸入Stream
        } while (in != null);//SequenceInputStream 對象的集合中下一個被裝飾的輸入Stream為null時,跳出循環,返回-1
        return -1;
    }
    //順序關閉SequenceInputStream 對象的集合中所有被裝飾的輸入Stream
    public void close() throws IOException {
        do {
            nextStream();
        } while (in != null);
    }
}
1.1、SequenceInputStream的read()函數和nextStream()函數
package java.io;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Vector;

public
class SequenceInputStream extends InputStream {
    ...省略部分代碼...
    //順序(序列化)存儲多個被裝飾的輸入Stream的集合,一般是Vector實例
    Enumeration<? extends InputStream> e;
    InputStream in;//順序(序列化)裝載多個被裝飾輸入Stream的集合中當前正在被SequenceInputStream 對象使用的被裝飾的輸入Stream
    //獲取集合中下一個被裝飾的輸入Stream
    final void nextStream() throws IOException {
        if (in != null) {
            in.close();//先關閉當前被裝飾的輸入Stream
        }

        if (e.hasMoreElements()) {//如果集合中還有被裝飾的輸入Stream
            in = (InputStream) e.nextElement();//獲取集合中下一個被裝飾的輸入Stream
            if (in == null)
                throw new NullPointerException();//如果集合中下一個被裝飾的輸入Stream為null,拋出一個NullPointerException
        }
        else in = null;//如果集合中沒有了被裝飾的輸入Stream,將當前正在使用的被裝飾的輸入Stream置為null

    }
    
    //從SequenceInputStream 對象的集合(該集合放着多個被裝飾的輸入Stream)中讀取1個字節
    public int read() throws IOException {
        while (in != null) {//如果in!=null,則説明當前這個SequenceInputStream 對象的集合中還有被裝飾的輸入Stream沒有關閉
            int c = in.read();//從當前正在使用的被裝飾的被裝飾輸入Stream中讀取1個字節
            if (c != -1) {//c != -1説明從當前正在使用的被裝飾輸入Stream中讀取到了字節
                return c;//返回讀取到的這個字節
            }
            nextStream();//如果c==-1説明當前正在使用的被裝飾輸入Stream中字節(byte)數據已經讀完,獲取SequenceInputStream 對象的集合中下一個被裝飾的輸入Stream
        }
        return -1;//如果SequenceInputStream 對象的集合中所有被裝飾的輸入Stream中的字節(byte)數據都已經讀完,返回-1
    }
    ...省略部分代碼...
}

如果使用者用的是2個被裝飾的輸入Stream(此處為FileInputStream),構造的SequenceInputStream的對象,如下所示(偽代碼):

is1 = new FileInputStream("D:\\data1.txt");
is2 = new FileInputStream("D:\\data2.txt");
sequenceInputStream = new SequenceInputStream(is1, is2);

那麼,SequenceInputStream對象中Vector集合的容量是2,如果此時執行SequenceInputStream.class::read()函數。

//偽代碼
int readByte = -1;
while ((readByte = sequenceInputStream.read()) != -1) {
   System.out.print((char) readByte);
}

過程如下(假設2個被裝飾的輸入Stream(此處為FileInputStream)中的字節數據如下):
clipboard

①、先執行第1個被裝飾的輸入Stream(也是Vector集合的第1個元素)的read()函數,直到該函數返回-1,如下所示:
clipboard
clipboard

②、關閉第1個被裝飾的輸入Stream(也是Vector集合的第1個元素),再執行第2個被裝飾的輸入Stream(也是Vector集合的第2個元素)的read()函數,直到該函數返回-1,如下所示:
clipboard
clipboard

1.1.1、使用舉例

  下面這個例子就恰當的使用SequenceInputStream的read()函數;

  • 我的windows操作系統的D盤根目錄下有2個txt文件,一個是data1.txt,另一個是data2.txt文件,這2個文件中總共有30個字節,如下所示:
    clipboard
    clipboard

  • 使用者可以用一個SequenceInputStream對象裝飾2個被裝飾的輸入Stream(此處為FileInputStream),如下代碼所示:

package com.chelong.StreamAndReader;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;

public class SequenceInputStreamTest {
   public static void main(String[] args) {
      InputStream is1 = null;
      InputStream is2 = null;
      SequenceInputStream sequenceInputStream = null;
      try {
         is1 = new FileInputStream("D:\\data1.txt");
         is2 = new FileInputStream("D:\\data2.txt");
         sequenceInputStream = new SequenceInputStream(is1, is2);
         int readByte = -1;
         while ((readByte = sequenceInputStream.read()) != -1) {
            System.out.print((char) readByte);
         }
      } catch (IOException e) {
         e.printStackTrace();
      } finally {
          //此處省略關閉所有的Stream的代碼
      }
   }
}

程序運行結果,如下所示:
clipboard

1.2、SequenceInputStream的read(byte b[], int off, int len)函數和nextStream()函數
package java.io;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Vector;

public
class SequenceInputStream extends InputStream {
    ...省略部分代碼...
    //順序(序列化)裝載多個被裝飾輸入Stream的集合,一般是Vector實例
    Enumeration<? extends InputStream> e;
    InputStream in;//順序(序列化)裝載多個被裝飾輸入Stream的集合中當前正在被SequenceInputStream 對象使用的被裝飾的輸入Stream
    //獲取集合中下一個被裝飾的輸入Stream
    final void nextStream() throws IOException {
        if (in != null) {
            in.close();//先關閉當前被裝飾的輸入Stream
        }

        if (e.hasMoreElements()) {//如果集合中還有被裝飾的輸入Stream
            in = (InputStream) e.nextElement();//獲取集合中下一個被裝飾的輸入Stream
            if (in == null)
                throw new NullPointerException();//如果集合中下一個被裝飾的輸入Stream為null,拋出一個NullPointerException
        }
        else in = null;//如果集合中沒有了被裝飾的輸入Stream,將當前正在使用的被裝飾的輸入Stream置為null

    }
    
    //從SequenceInputStream 對象的集合(該集合放着多個被裝飾的輸入Stream)中讀取len個字節,放入到byte[]數組b的[off,off+len)(左閉右開,不包括off+len)索引位置
    public int read(byte b[], int off, int len) throws IOException {
        if (in == null) {//如果in==null,則説明當前這個SequenceInputStream 對象的集合中所有被裝飾的輸入Stream都已經關閉
            return -1;
        } else if (b == null) {
            throw new NullPointerException();//如果byte[]數組b==null,拋出一個NullPointerException
        } else if (off < 0 || len < 0 || len > b.length - off) {//相當於off + len > b.length(源碼中這樣寫代碼的好處我沒看出來)
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;//要從SequenceInputStream 對象的集合(該集合放着至少2個被裝飾的輸入Stream)中讀取的len個字節==0時,返回0
        }
        do {
            int n = in.read(b, off, len);//從當前正在使用的被裝飾的輸入Stream中讀取len個字節,放入到byte[]數組b的[off,off+len)(左閉右開,不包括off+len)索引位置
            if (n > 0) {
                return n;//只要能從當前正在使用的被裝飾的輸入Stream中讀取到字節,則返回讀取的數量
            }
            nextStream();//此時n==-1,説明當前正在使用的被裝飾的輸入Stream中字節(byte)數據已經讀完,獲取SequenceInputStream 對象的集合中下一個被裝飾的輸入Stream
        } while (in != null);//SequenceInputStream 對象的集合中下一個被裝飾的輸入Stream為null時,跳出循環,返回-1
        return -1;
    }
    ...省略部分代碼...
}

如果使用者用的是2個被裝飾的輸入Stream(此處為FileInputStream),構造的SequenceInputStream的對象,如下所示(偽代碼):

is1 = new FileInputStream("D:\\data1.txt");
is2 = new FileInputStream("D:\\data2.txt");
sequenceInputStream = new SequenceInputStream(is1, is2);

那麼,SequenceInputStream對象中Vector集合的容量是2,並且假設這2個被裝飾的輸入Stream(此處為FileInputStream)中的字節數據如下:
clipboard

如果此時執行SequenceInputStream.class::read()函數。接下來使用SequenceInputStream對象讀取字節數據到使用者創建的byte[]數組,如果使用者創建的字節數組byte[]的長度>=第1個被裝飾的輸入Stream中的所有字節個數,比如,使用者創建的byte[]數組的長度為12,如下所示(偽代碼):

int readByte = -1;
byte[] buff = new byte[12];
while ((readByte = sequenceInputStream.read(buff, 0, buff.length)) != -1) {
   for (int i = 0; i < readByte; i++) {
      System.out.print((char) buff[i]);
   }
}

整個執行過程如下:
①、第1次進入read()函數
clipboard

②、第2次進入read()函數(重點是當前正在使用的被裝飾的輸入Stream中的字節數據已經讀取完了時,再次讀取,會返回-1,不會返回0
clipboard

③、第3次進入read()函數
clipboard

④、第4次進入read()函數(重點是當前正在使用的被裝飾的輸入Stream中的字節數據已經讀取完了時,再次讀取,會返回-1,不會返回0
clipboard

最終使用者創建的byte[]數組中的字節(byte)數據,如下所示:
clipboard

1.2.1、使用舉例

  下面這個例子就恰當的使用SequenceInputStream的read()函數;

  • 我的windows操作系統的D盤根目錄下有2個txt文件,一個是data1.txt,另一個是data2.txt文件,這2個文件中總共有30個字節,如下所示:
    clipboard
    clipboard

  • 使用者可以用一個SequenceInputStream對象裝飾2個被裝飾的輸入Stream(此處為FileInputStream),如下代碼所示:

package com.chelong.StreamAndReader;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.util.Vector;

public class SequenceInputStreamTest {
   public static void main(String[] args) {
      InputStream is1 = null;
      InputStream is2 = null;
      SequenceInputStream sequenceInputStream = null;
      try {
         is1 = new FileInputStream("D:\\data1.txt");
         is2 = new FileInputStream("D:\\data2.txt");
         Vector<InputStream> vector = new Vector<InputStream>();
         vector.addElement(is1);
         vector.addElement(is2);
         sequenceInputStream = new SequenceInputStream(vector.elements());
         int readByte = -1;
         byte[] buff = new byte[12];
         while ((readByte = sequenceInputStream.read(buff, 0, buff.length)) != -1) {
            for (int i = 0; i < readByte; i++) {
               System.out.print((char) buff[i]);
            }
         }
         System.out.println();
         System.out.println("最終留在byte[]數組buff中的字節:");
         for (byte b : buff) {
            System.out.print((char) b);
         }
      } catch (IOException e) {
         e.printStackTrace();
      } finally {
         try {
            if (is1 != null) is1.close();
            if (is2 != null) is1.close();
            if (sequenceInputStream != null) sequenceInputStream.close();
         } catch (IOException e) {
            e.printStackTrace();
         }
      }
   }
}

程序運行結果,如下所示:
clipboard

二、Vector.class的一些函數説明

  Vector 與 ArrayList 一樣,也是通過數組實現的,不同的是它支持線程的同步,即某一時刻只有一個線程能夠寫 Vector,避免多線程同時寫而引起的不一致性,但實現同步需要很高的花費,因此,訪問它比訪問 ArrayList慢。Vector的UML圖,如下所示:
clipboard

2.1、構造函數
函數名 函數説明
public Vector() 此構造函數創建的Vector中,Object[]數組的初始長度為10,capacityIncrement=0(capacityIncrement表示擴容時Object[]數組增加的長度,如果等於0的話,當Object[]數組需要擴容時,新的數組長度=2*舊數組的長度,但是新的數組長度最大為2^31-8)
public Vector(int initialCapacity) 此構造函數創建的Vector中,Object[]數組的初始長度為initialCapacity,capacityIncrement=0(capacityIncrement表示擴容時Object[]數組增加的長度,如果等於0的話,當Object[]數組需要擴容時,新的數組長度=2*舊數組的長度,但是新的數組長度最大為2^31-8)
public Vector(int initialCapacity, int capacityIncrement) 此構造函數創建的Vector中,Object[]數組的初始長度為initialCapacity,capacityIncrement=capacityIncrement(capacityIncrement表示擴容時Object[]數組增加的長度,如果等於0的話,當Object[]數組需要擴容時,新的數組長度=2*舊數組的長度,但是新的數組長度最大為2^31-8)
public Vector(Collection<? extends E> c) 此構造函數創建的Vector中,Object[]數組的初始長度為傳入集合Collection<? extends E> c的長度,capacityIncrement=0(capacityIncrement表示擴容時Object[]數組增加的長度,如果等於0的話,當Object[]數組需要擴容時,新的數組長度=2*舊數組的長度,但是新的數組長度最大為2^31-8)

2.2、常用函數

函數名 函數説明
boolean add(E o) 此函數將指定的元素追加到此Vector的末尾,該函數與addElement()函數的區別是,該()函數是List.interface接口規定的函數,addElement()函數是Vector自己實現的(接口中沒有規定addElement()函數)
void add(int index, E element) 此函數將指定的元素插入此Vector中的指定索引位置
boolean addAll(Collection<? extends E> c) 此函數將指定Collection中的所有元素追加到此Vector的末尾
boolean addAll(int index, Collection<? extends E> c) 此函數將指定Collection中的所有元素插入到此Vector中的指定索引位置
void addElement(E obj) 此函數將指定的元素追加到此Vector的末尾,這個函數與add()函數的區別是,add()函數是List.interface接口規定的函數,這個函數是Vector自己實現的(接口中沒有規定該函數)
int capacity() 此函數返回此Vector的當前容量
void clear() 此函數從此Vector中刪除所有元素
Object clone() 此函數返回此Vector的克隆對象
boolean contains(Object elem) 如果此Vector包含指定的元素,則此函數返回true
boolean containsAll(Collection<?> c) 如果此Vector包含指定Collection中的所有元素,則此函數返回true
void copyInto(Object[] anArray) 此方法將此向量的組件複製到指定的數組中
E elementAt(int index) 此函數返回Vector指定索引處的元素
Enumeration elements() 此函數返回此Vector中所包含的所有元素的枚舉。
void ensureCapacity(int minCapacity) 此函數可增加此Vector的容量,以確保它至少可以保存最小容量元素個數
boolean equals(Object o) 此函數將指定的Object與此Vector進行比較以獲得相等性
E firstElement() 返回此Vector的第一個元素(位於Object[]數組索引 0 處的元素)
E get(int index) 返回Vector中指定索引位置的元素
int indexOf(Object elem) 搜索給定參數的第一個匹配項,使用 equals ()函數測試相等性
int indexOf(Object elem, int index) 搜索給定參數的第一個匹配項,從 index 處開始搜索,並使用 equals()函數測試其相等性
void insertElementAt(E obj, int index) 將指定對象作為此Vector中的元素插入到指定的 索引位置
boolean isEmpty() 測試此Vector中的是否不包含任何元素
E lastElement() 返回此Vector的最後一個元素(位於Object[]數組索引 Object[].length-1 處的元素)
int lastIndexOf(Object elem) 返回指定的對象在此Vector中最後一個匹配項的索引
int lastIndexOf(Object elem, int index) 從指定的索引處開始向後搜索指定的對象,並返回搜索到的最後一個索引
E remove(int index) 移除此Vector中指定索引位置的元素
boolean remove(Object o) 移除此Vector中指定元素的第一個匹配項,如果此Vector不包含該元素,則所有元素保持不變,並返回false
boolean removeAll(Collection<?> c) 從此Vector中移除包含在指定 Collection 中的所有元素
void removeAllElements() 從此Vector中移除全部元素,並設置elementCount=0(該變量表示此Vector對象中有效元素的數量),Object[]數組的長度不變。
void removeElementAt(int index) 刪除指定索引處的元素
protected void removeRange(int fromIndex, int toIndex) 從此 Vector 中移除索引位於 [fromIndex, toIndex)(左閉右開)之間的所有元素
boolean retainAll(Collection<?> c) 如果此Vector中包含指定 Collection 中的所有元素,此函數返回true
E set(int index, E element) 用指定的元素替換此Vector中指定索引處的元素
void setElementAt(E obj, int index) 將此Vector指定 索引處的元素設置為指定的另一個元素
void setSize(int newSize) 設置此Vector的大小
int size() 返回此Vector中的元素數
List subList(int fromIndex, int toIndex) 返回此 Vector的子集,該子集的元素範圍為 [fromIndex, toIndex)(左閉右開)索引位置的所有元素
Object[] toArray() 返回一個Object[]數組,包含此Vector中以正確順序存放的所有元素
String toString() 返回此Vector的字符串表示形式,其中包含每個元素的 String 表示形式
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.