InputStream複用,mark和reset

markSupported

InputStream是否支持mark,默認不支持。java

public boolean markSupported() {  
   return false;  
}

InputStream默認是不支持mark的,子類須要支持mark必須重寫這三個方法。數組

在此輸入流中標記當前的位置。對 reset 方法的後續調用會在最後標記的位置從新定位此流,以便後續讀取從新讀取相同的字節。緩存

readlimit 參數告知此輸入流在標記位置失效以前容許讀取許多字節。code

mark

mark接口。該接口在InputStream中默認實現不作任何事情。blog

public synchronized void mark(int readlimit) {}

reset

reset接口。該接口在InputStream中實現,調用就會拋異常。接口

public synchronized void reset() throws IOException {  
   throw new IOException("mark/reset not supported");  
}

將此流從新定位到對此輸入流最後調用 mark 方法時的位置。ip

reset 的常規協定是:內存

若是方法 markSupported 返回 true,則:若是建立流以來未調用方法 mark,或最後調用 mark 以來從該流讀取的字節數大於最後調用 mark 時的參數,則可能拋出 IOException。若是未拋出這樣的 IOException,則將該流從新設置爲這種狀態:最近調用 mark 以來(或若是未調用 mark,則從文件開始以來)讀取的全部字節將從新提供給 read 方法的後續調用方,後接多是調用 reset 時的下一輸入數據的全部字節。get

若是方法 markSupported 返回 false,則:對 reset 的調用可能拋出 IOException。若是未拋出IOException,則將該流從新設置爲一種固定狀態,該狀態取決於輸入流的特定類型和其建立方式的固定狀態。提供給 read 方法的後續調用方的字節取決於特定類型的輸入流。input

簡而言之就是:*調用mark方法會記下當前調用mark方法的時刻,InputStream被讀到的位置。

調用reset方法就會回到該位置。*

Code

String content = "yydcdut!";  
InputStream inputStream = new ByteArrayInputStream(content.getBytes());  

// 判斷該輸入流是否支持mark操做  
if (!inputStream.markSupported()) {  
    System.out.println("mark/reset not supported!");  
}  
int ch;    
boolean marked = false;    
while ((ch = inputStream.read()) != -1) {  

    //讀取一個字符輸出一個字符    
    System.out.print((char)ch);    
    //讀到 'c'的時候標記一下  
     if (((char)ch == 'c')& !marked) {    
        inputStream.mark(content.length());  //先不要理會mark的參數  
         marked = true;    
     }    

     //讀到'!'的時候從新回到標記位置開始讀  
      if ((char)ch == '!' && marked) {    
          inputStream.reset();    
          marked = false;  
      }    
}  
//程序最終輸出:yydcdut!dut!

咱們知道InputStream是不支持mark的。要想支持mark子類必須重寫這三個方法,我想說的是不一樣的實現子類,mark的參數readlimit做用不盡相同。 經常使用的FileInputStream不支持mark。

  • 對於BufferedInputStream,readlimit表示:InputStream調用mark方法的時刻起,在讀取readlimit個字節以前,標記的該位置是有效的。若是讀取的字節數大於readlimit,可能標記的位置會失效。

    在BufferedInputStream的read方法源碼中有這麼一段:

} else if (buffer.length >= marklimit) {  
     markpos = -1;   /* buffer got too big, invalidate mark */  
     pos = 0;        /* drop buffer contents */  
     } else {

爲何是可能會失效呢?

由於BufferedInputStream讀取不是一個字節一個字節讀取的,是一個字節數組一個字節數組讀取的。

例如,readlimit=35,第一次比較的時候buffer.length=0(沒開始讀)<readlimit.

而後buffer數組一次讀取48個字節。這時的read方法只會簡單的挨個返回buffer數組中的字節,不會作此次比較。直到讀到buffer數組最後一個字節(第48個)後,才從新再次比較。這時若是咱們讀到buffer中第47個字節就reset。mark仍然是有效的。雖然47>35。

  • 對於InputStream的另一個實現類:ByteArrayInputStream,咱們發現readlimit參數根本就沒有用,調用mark方法的時候寫多少都無所謂。 
public void mark(int readAheadLimit) {  
   mark = pos;  
}  
  
public synchronized void reset() {  
   pos = mark;  
}

由於對於ByteArrayInputStream來講,都是經過字節數組建立的,內部自己就保存了整個字節數組,mark只是標記一下數組下標位置,根本不用擔憂mark會建立太大的buffer字節數組緩存。 

  • 其餘的InputStream子類沒有去總結。原理都是同樣的。 

因此因爲mark和reset方法配合能夠記錄並回到咱們標記的流的位置從新讀流,很大一部分就能夠解決咱們的某些重複讀的須要。 

這種方式的優勢很明顯:不用緩存整個InputStream數據。對於ByteArrayInputStream甚至沒有任何的內存開銷。 

固然這種方式也有缺點:就是須要經過干擾InputStream的讀取細節,也相對比較複雜。

我是天王蓋地虎的分割線

參考:http://zhangbo-peipei-163-com.iteye.com/blog/2022460

相關文章
相關標籤/搜索