<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 188,comments - 176,trackbacks - 0
     

     

    這篇文章討論和闡明了提供 JavaTM I/O 性能的多種技術(shù)。大多技術(shù)圍繞調(diào)整磁盤文件 I/O,但是有些內(nèi)容也同樣適合網(wǎng)絡(luò) I/O 和窗口輸出。第一部分技術(shù)討論底層的I/O問題,然后討論諸如壓縮,格式化和串行化等高級(jí)I/O問題。然而這個(gè)討論沒有包含應(yīng)用設(shè)計(jì)問題,例如搜索算法和數(shù)據(jù)結(jié)構(gòu),也沒有討論系統(tǒng)級(jí)的問題,例如文件高速緩沖。 

    當(dāng)我們討論Java I/O時(shí),值得注意的是Java語(yǔ)言采取兩種截然不同的磁盤文件結(jié)構(gòu)。一個(gè)是基于字節(jié)流,另一個(gè)是字符序列。在Java語(yǔ)言中一個(gè)字符有兩個(gè)字節(jié)表示,而不是像通常的語(yǔ)言如c語(yǔ)言那樣是一個(gè)字節(jié)。因此,從一個(gè)文件讀取字符時(shí)需要進(jìn)行轉(zhuǎn)換。這個(gè)不同在某些情況下是很重要的,就像下面的幾個(gè)例子將要展示的那樣。 

    低級(jí) I/O I問題

    加速I/O的基本規(guī)則 
    緩沖 
    讀寫文本文件 
    格式化的代價(jià) 
    隨機(jī)訪問 
    高級(jí)I/O問題

    壓縮 
    高速緩沖 
    分解 
    串行化 
    獲取文件信息 
    更多信息 
    加速I/O的基本規(guī)則

    作為這個(gè)討論的開始,這里有幾個(gè)如何加速I/O的基本規(guī)則: 

    避免訪問磁盤 
    避免訪問底層的操作系統(tǒng) 
    避免方法調(diào)用 
    避免個(gè)別的處理字節(jié)和字符 
    很明顯這些規(guī)則不能在所有的問題上避免,因?yàn)槿绻軌虻脑捑蜎]有實(shí)際的I/O被執(zhí)行。考慮下面的計(jì)算文件中的新行符('\n')的三部分范例。 

    方法1: 讀方法
    第一個(gè)方法簡(jiǎn)單的使用FileInputStream的read方法: 



      import java.io.*;
      
      public class intro1 {
        public static void main(String args[]) {
          if (args.length != 1) {
            System.err.println("missing filename");
            System.exit(1);
          }
          try {
            FileInputStream fis =
                new FileInputStream(args[0]);
            int cnt = 0;
            int b;
            while ((b = fis.read()) != -1) {
              if (b == '\n')
                cnt++;
            }
            fis.close();
            System.out.println(cnt);
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }

    然而這個(gè)方法觸發(fā)了大量的底層運(yùn)行時(shí)系統(tǒng)調(diào)用--FileInputStream.read--返回文件的下一個(gè)字節(jié)的本機(jī)方法。 
    方法 2: 使用大緩沖區(qū)
    第二種方法使用大緩沖區(qū)避免了上面的問題: 

     

     import java.io.*;
      
     public class intro2 {
       public static void main(String args[]) {
        if (args.length != 1) {
          System.err.println("missing filename");
          System.exit(1);
        }
        try {
          FileInputStream fis =
              new FileInputStream(args[0]);
          BufferedInputStream bis =
              new BufferedInputStream(fis);
          int cnt = 0;
          int b;
          while ((b = bis.read()) != -1) {
            if (b == '\n')
              cnt++;
            }
          bis.close();
          System.out.println(cnt);
        }
        catch (IOException e) {
          System.err.println(e);
        }
      }
     }

    BufferedInputStream.read 從輸入緩沖區(qū)獲取下一個(gè)字節(jié),僅僅只訪問了一次底層系統(tǒng)。 
    方法 3: 直接緩沖
    第三種方法避免使用 BufferedInputStream 而直接緩沖,因此排除了 read 方法的調(diào)用: 



      import java.io.*;
      
      public class intro3 {
        public static void main(String args[]) {
          if (args.length != 1) {
            System.err.println("missing filename");
            System.exit(1);
          }
          try {
            FileInputStream fis =
                new FileInputStream(args[0]);
            byte buf[] = new byte[2048];
            int cnt = 0;
            int n;
            while ((n = fis.read(buf)) != -1) {
              for (int i = 0; i < n; i++) {
                if (buf[i] == '\n')
                  cnt++;
              }
            }
            fis.close();
            System.out.println(cnt);
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }

    對(duì)于一個(gè)1 MB 的輸入文件,以秒為單位的執(zhí)行時(shí)間是: 

     
     intro1    6.9
     intro2    0.9
     intro3    0.4

    或者說在最慢的方法和最快的方法間是17比1的不同。 

    這個(gè)巨大的加速并不能證明你應(yīng)該總是使用第三種方法,即自己做緩沖。這可能是一個(gè)錯(cuò)誤的傾向特別是在處理文件結(jié)束事件時(shí)沒有仔細(xì)的實(shí)現(xiàn)。在可讀性上它也沒有其它方法好。但是記住時(shí)間花費(fèi)在哪兒了以及在必要的時(shí)候如何矯正是很有用。 

    方法2 或許是對(duì)于大多應(yīng)用的 "正確" 方法. 

    緩沖

    方法 2 和 3 使用了緩沖技術(shù), 大塊文件被從磁盤讀取,然后每次訪問一個(gè)字節(jié)或字符。緩沖是一個(gè)基本而重要的加速I/O 的技術(shù),而且有幾個(gè)類支持緩沖(BufferedInputStream 用于字節(jié), BufferedReader 用于字符)。 

    一個(gè)明顯得問題是: 緩沖區(qū)越大I/O越快嗎?典型的Java緩沖區(qū)長(zhǎng)1024 或者 2048 字節(jié),一個(gè)更大的緩沖區(qū)有可能加速 I/O但是只能占很小的比重,大約5 到10%。 

    方法4: 整個(gè)文件
    緩沖的極端情況是事先決定整個(gè)文件的長(zhǎng)度,然后讀取整個(gè)文件: 



      import java.io.*;
      
      public class readfile {
        public static void main(String args[]) {
          if (args.length != 1) {
            System.err.println("missing filename");
            System.exit(1);
          }
          try {
            int len = (int)(new File(args[0]).length());
            FileInputStream fis =
                new FileInputStream(args[0]);
            byte buf[] = new byte[len];
            fis.read(buf);
            fis.close();
            int cnt = 0;
            for (int i = 0; i < len; i++) {
              if (buf[i] == '\n')
                cnt++;
            }
            System.out.println(cnt);
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }

    這個(gè)方法很方便,在這里文件被當(dāng)作一個(gè)字節(jié)數(shù)組。但是有一個(gè)明顯得問題是有可能沒有讀取一個(gè)巨大的文件的足夠的內(nèi)存。 
    緩沖的另一個(gè)方面是向窗口終端的文本輸出。缺省情況下, System.out ( 一個(gè)PrintStream) 是行緩沖的,這意味著在遇到一個(gè)新行符后輸出緩沖區(qū)被提交。對(duì)于交互來(lái)說這是很重要的,在那種情況下你可能喜歡在實(shí)際的輸出前顯示一個(gè)輸入提示。 

    方法 5: 關(guān)閉行緩沖
    行緩沖可以被禁止,像下面的例子那樣: 



      import java.io.*;
      
      public class bufout {
        public static void main(String args[]) {
          FileOutputStream fdout =
              new FileOutputStream(FileDeor.out);
          BufferedOutputStream bos =
              new BufferedOutputStream(fdout, 1024);
          PrintStream ps =
              new PrintStream(bos, false);
      
          System.setOut(ps);
      
          final int N = 100000;
      
          for (int i = 1; i <= N; i++)
            System.out.println(i);
      
          ps.close();
        }
      }

    這個(gè)程序輸出整數(shù)1到100000缺省輸出,比在缺省的行緩沖情況下快了三倍。 

    緩沖也是下面將要展示的例子的重要部分,在那里,緩沖區(qū)被用于加速文件隨機(jī)訪問。 

    讀寫文本文件

    早些時(shí)候曾提到從文件里面讀取字符的方法調(diào)用的消耗可能是重大的。這個(gè)問題在計(jì)算文本文件的行數(shù)的另一個(gè)例子中也可以找到。: 

     

      import java.io.*;

      public class line1 {
        public static void main(String args[]) {
          if (args.length != 1) {
            System.err.println("missing filename");
            System.exit(1);
          }
          try {
            FileInputStream fis =
                new FileInputStream(args[0]);
            BufferedInputStream bis =
                new BufferedInputStream(fis);
            DataInputStream dis =
                new DataInputStream(bis);
            int cnt = 0;
            while (dis.readLine() != null)
              cnt++;
            dis.close();
            System.out.println(cnt);
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }

    這個(gè)程序使用老的DataInputStream.readLine 方法,該方法是使用用讀取每個(gè)字符的 read 方法實(shí)現(xiàn)的。一個(gè)新方法是: 



      import java.io.*;

      public class line2 {
        public static void main(String args[]) {
          if (args.length != 1) {
            System.err.println("missing filename");
            System.exit(1);
          }
          try {
            FileReader fr = new FileReader(args[0]);
            BufferedReader br = new BufferedReader(fr);
            int cnt = 0;
            while (br.readLine() != null)
              cnt++;
            br.close();
            System.out.println(cnt);
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }

    這個(gè)方法更快。例如在一個(gè)有200,000行的 6 MB文本文件上,第二個(gè)程序比第一個(gè)快大約20%。 

    但是即使第二個(gè)程序不是更快的,第一個(gè)程序依然有一個(gè)重要的問題要注意。第一個(gè)程序在JavaTM 2編譯器下引起了不贊成警告,因?yàn)镈ataInputStream.readLine太陳舊了。它不能恰當(dāng)?shù)膶⒆止?jié)轉(zhuǎn)換為字符,因此在操作包含非ASCII字符的文本文件時(shí)可能是不合適的選擇。(Java語(yǔ)言使用Unicode字符集而不是ASCII) 

    這就是早些時(shí)候提到的字節(jié)流和字符流之間的區(qū)別。像這樣的一個(gè)程序: 



      import java.io.*;
      
      public class conv1 {
        public static void main(String args[]) {
          try {
            FileOutputStream fos =
                new FileOutputStream("out1");
            PrintStream ps = 
                new PrintStream(fos);
            ps.println("\uffff\u4321\u1234");
            ps.close();
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }

    向一個(gè)文件里面寫,但是沒有保存實(shí)際的Unicode字符輸出。Reader/Writer I/O 類是基于字符的,被設(shè)計(jì)用來(lái)解決這個(gè)問題。OutputStreamWriter 應(yīng)用于字節(jié)編碼的字符。 
    一個(gè)使用PrintWriter寫入U(xiǎn)nicode字符的程序是這樣的: 

     

      import java.io.*;

      public class conv2 {
        public static void main(String args[]) {
          try {
            FileOutputStream fos =
                new FileOutputStream("out2");
            OutputStreamWriter osw =
                new OutputStreamWriter(fos, "UTF8");
            PrintWriter pw =
                new PrintWriter(osw);
            pw.println("\uffff\u4321\u1234");
            pw.close();
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }

    這個(gè)程序使用UTF8編碼,具有ASCII文本是本身而其他字符是兩個(gè)或三個(gè)字節(jié)的特性。 

    格式化的代價(jià)
    實(shí)際上向文件寫數(shù)據(jù)只是輸出代價(jià)的一部分。另一個(gè)可觀的代價(jià)是數(shù)據(jù)格式化。考慮一個(gè)三部分程序,它像下面這樣輸出一行: 

     
    The square of 5 is 25

    方法 1
    第一種方法簡(jiǎn)單的輸出一個(gè)固定的字符串,了解固有的I/O開銷: 



      public class format1 {
        public static void main(String args[]) {
          final int COUNT = 25000;

          for (int i = 1; i <= COUNT; i++) {
            String s = "The square of 5 is 25\n";
            System.out.print(s);
          }
        }
      }

    方法2
    第二種方法使用簡(jiǎn)單格式"+": 



     public class format2 {
        public static void main(String args[]) {
          int n = 5;
      
          final int COUNT = 25000;


          for (int i = 1; i <= COUNT; i++) {
            String s = "The square of " + n + " is " +
                n * n + "\n";
            System.out.print(s);
          }
        }
      }

    方法 3
    第三種方法使用java.text包中的 MessageFormat 類: 



     import java.text.*;
      
     public class format3 {
       public static void main(String args[]) {
         MessageFormat fmt =
          new MessageFormat("The square of {0} is {1}\n");
          Object s[] = new Object[2];


        int n = 5;


        s[0] = new Integer(n);
        s[1] = new Integer(n * n);
      
        final int COUNT = 25000;


        for (int i = 1; i <= COUNT; i++) {
          String s = fmt.format(s);
          System.out.print(s);
         }
        }
      }

    這些程序產(chǎn)生同樣的輸出。運(yùn)行時(shí)間是: 

     
     format1   1.3
     format2   1.8
     format3   7.8

    或者說最慢的和最快的大約是6比1。如果格式?jīng)]有預(yù)編譯第三種方法將更慢,使用靜態(tài)的方法代替: 

    方法 4
    MessageFormat.format(String, Object[]) 



      import java.text.*;
      
      public class format4 {
        public static void main(String args[]) {
          String fmt = "The square of {0} is {1}\n";
          Object s[] = new Object[2];


          int n = 5;


          s[0] = new Integer(n);
          s[1] = new Integer(n * n);
      
          final int COUNT = 25000;


          for (int i = 1; i <= COUNT; i++) {
            String s =
                MessageFormat.format(fmt, s);
            System.out.print(s);
          }
        }
      }

    這比前一個(gè)例子多花費(fèi)1/3的時(shí)間。 

    第三個(gè)方法比前兩種方法慢很多的事實(shí)并不意味著你不應(yīng)該使用它,而是你要意識(shí)到時(shí)間上的開銷。 

    在國(guó)際化的情況下信息格式化是很重要的,關(guān)心這個(gè)問題的應(yīng)用程序通常從一個(gè)綁定的資源中讀取格式然后使用它。 

    隨機(jī)訪問

    RandomAccessFile 是一個(gè)進(jìn)行隨機(jī)文件I/O(在字節(jié)層次上)的類。這個(gè)類提供一個(gè)seek方法,和 C/C++中的相似,移動(dòng)文件指針到任意的位置,然后從那個(gè)位置字節(jié)可以被讀取或?qū)懭搿?nbsp;

    seek方法訪問底層的運(yùn)行時(shí)系統(tǒng)因此往往是消耗巨大的。一個(gè)更好的代替是在RandomAccessFile上建立你自己的緩沖,并實(shí)現(xiàn)一個(gè)直接的字節(jié)read方法。read方法的參數(shù)是字節(jié)偏移量(>= 0)。這樣的一個(gè)例子是: 

     

      import java.io.*;
      
      public class ReadRandom {
        private static final int DEFAULT_BUFSIZE = 4096;
      
        private RandomAccessFile raf;
        private byte inbuf[];
        private long startpos = -1;
        private long endpos = -1;
        private int bufsize;
      
        public ReadRandom(String name) 
         throws FileNotFoundException {
          this(name, DEFAULT_BUFSIZE);
        }
      
        public ReadRandom(String name, int b)
            throws FileNotFoundException {
          raf = new RandomAccessFile(name, "r");
          bufsize = b;
          inbuf = new byte[bufsize];
        }
      
        public int read(long pos) {
          if (pos < startpos || pos > endpos) {
            long blockstart = (pos / bufsize) * bufsize;
            int n;
            try {
              raf.seek(blockstart);
              n = raf.read(inbuf);
            }
            catch (IOException e) {
              return -1;
            }
            startpos = blockstart;
            endpos = blockstart + n - 1;
            if (pos < startpos || pos > endpos)
              return -1;
          }
      
          return inbuf[(int)(pos - startpos)] & 0xffff;
        }
      
        public void close() throws IOException {
          raf.close();
        }
      
        public static void main(String args[]) {
          if (args.length != 1) {
            System.err.println("missing filename");
            System.exit(1);
          }
      
          try {
            ReadRandom rr = new ReadRandom(args[0]);
            long pos = 0;
            int c;
            byte buf[] = new byte[1];
            while ((c = rr.read(pos)) != -1) {
              pos++;
              buf[0] = (byte)c;
              System.out.write(buf, 0, 1);
            }
            rr.close();
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }

    這個(gè)程序簡(jiǎn)單的讀取字節(jié)序列然后輸出它們。 

    如果有訪問位置,這個(gè)技術(shù)是很有用的,文件中的附近字節(jié)幾乎在同時(shí)被讀取。例如,如果你在一個(gè)排序的文件上實(shí)現(xiàn)二分法查找,這個(gè)方法可能很有用。如果你在一個(gè)巨大的文件上的任意點(diǎn)做隨機(jī)訪問的話就沒有太大價(jià)值。 

    壓縮

    Java提供用于壓縮和解壓字節(jié)流的類,這些類包含在java.util.zip 包里面,這些類也作為 Jar 文件的服務(wù)基礎(chǔ) ( Jar 文件是帶有附加文件列表的 Zip 文件)。 

    下面的程序接收一個(gè)輸入文件并將之寫入一個(gè)只有一項(xiàng)的壓縮的 Zip 文件: 



     import java.io.*;
     import java.util.zip.*;
      
      public class compress {
        public static void doit(
                                String filein, 
                                String fileout
                                ) {
          FileInputStream fis = null;
          FileOutputStream fos = null;
          try {
            fis = new FileInputStream(filein);
            fos = new FileOutputStream(fileout);
            ZipOutputStream zos =
                new ZipOutputStream(fos);
            ZipEntry ze = new ZipEntry(filein);
            zos.putNextEntry(ze);
            final int BUFSIZ = 4096;
            byte inbuf[] = new byte[BUFSIZ];
            int n;
            while ((n = fis.read(inbuf)) != -1)
              zos.write(inbuf, 0, n);
            fis.close();
            fis = null;
            zos.close();
            fos = null;
          }
          catch (IOException e) {
            System.err.println(e);
          }
          finally {
            try {
              if (fis != null)
                fis.close();
              if (fos != null)
                fos.close();
            }
            catch (IOException e) {
            }
          }
        }
      public static void main(String args[]) {
        if (args.length != 2) {
         System.err.println("missing filenames");
         System.exit(1);
        }
       if (args[0].equals(args[1])) {
         System.err.println("filenames are identical");
         System.exit(1);
          }
          doit(args[0], args[1]);
        }
      }

    下一個(gè)程序執(zhí)行相反的過程,將一個(gè)假設(shè)只有一項(xiàng)的Zip文件作為輸入然后將之解壓到輸出文件: 


     import java.io.*;
     import java.util.zip.*;
      
      public class uncompress {
        public static void doit(
                                String filein, 
                                String fileout
                                ) {
          FileInputStream fis = null;
          FileOutputStream fos = null;
          try {
            fis = new FileInputStream(filein);
            fos = new FileOutputStream(fileout);
            ZipInputStream zis = new ZipInputStream(fis);
            ZipEntry ze = zis.getNextEntry();
            final int BUFSIZ = 4096;
            byte inbuf[] = new byte[BUFSIZ];
            int n;
            while ((n = zis.read(inbuf, 0, BUFSIZ)) != -1)
              fos.write(inbuf, 0, n);
            zis.close();
            fis = null;
            fos.close();
            fos = null;
          }
          catch (IOException e) {
            System.err.println(e);
          }
          finally {
            try {
              if (fis != null)
                fis.close();
              if (fos != null)
                fos.close();
            }
            catch (IOException e) {
            }
          }
        }
        public static void main(String args[]) {
          if (args.length != 2) {
         System.err.println("missing filenames");
         System.exit(1);
          }
        if (args[0].equals(args[1])) {
         System.err.println("filenames are identical");
         System.exit(1);
          }
          doit(args[0], args[1]);
        }
      }

    壓縮是提高還是損害I/O性能很大程度依賴你的硬件配置,特別是和處理器和磁盤驅(qū)動(dòng)器的速度相關(guān)。使用Zip技術(shù)的壓縮通常意味著在數(shù)據(jù)大小上減少50%,但是代價(jià)是壓縮和解壓的時(shí)間。一個(gè)巨大(5到10 MB)的壓縮文本文件,使用帶有IDE硬盤驅(qū)動(dòng)器的300-MHz Pentium PC從硬盤上讀取可以比不壓縮少用大約1/3的時(shí)間。 

    壓縮的一個(gè)有用的范例是向非常慢的媒介例如軟盤寫數(shù)據(jù)。使用高速處理器(300 MHz Pentium)和低速軟驅(qū)(PC上的普通軟驅(qū))的一個(gè)測(cè)試顯示壓縮一個(gè)巨大的文本文件然后在寫入軟盤比直接寫入軟盤快大約50% 。 

    高速緩存

    關(guān)于硬件的高速緩存的詳細(xì)討論超出了本文的討論范圍。但是在有些情況下軟件高速緩存能被用于加速I/O。考慮從一個(gè)文本文件里面以隨機(jī)順序讀取一行的情況,這樣做的一個(gè)方法是讀取所有的行,然后把它們存入一個(gè)ArrayList (一個(gè)類似Vector的集合類): 


     import java.io.*;
     import java.util.ArrayList;
      
      public class LineCache {
        private ArrayList list = new ArrayList();
      
        public LineCache(String fn) throws IOException {
          FileReader fr = new FileReader(fn);
          BufferedReader br = new BufferedReader(fr);
          String ln;
          while ((ln = br.readLine()) != null)
            list.add(ln);
          br.close();
        }
      
        public String getLine(int n) {
          if (n < 0)
            throw new IllegalArgumentException();
      
          return (n < list.size() ? 
           (String)list.get(n) : null);
        }
      
        public static void main(String args[]) {
          if (args.length != 1) {
            System.err.println("missing filename");
            System.exit(1);
          }
          try {
            LineCache lc = new LineCache(args[0]);
            int i = 0;
            String ln;
            while ((ln = lc.getLine(i++)) != null)
              System.out.println(ln);
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      } 

    getLine 方法被用來(lái)獲取任意行。這個(gè)技術(shù)是很有用的,但是很明顯對(duì)一個(gè)大文件使用了太多的內(nèi)存,因此有局限性。一個(gè)代替的方法是簡(jiǎn)單的記住被請(qǐng)求的行最近的100行,其它的請(qǐng)求直接從磁盤讀取。這個(gè)安排在局域性的訪問時(shí)很有用,但是在真正的隨機(jī)訪問時(shí)沒有太大作用。 

    分解

    分解 是指將字節(jié)或字符序列分割為像單詞這樣的邏輯塊的過程。Java 提供StreamTokenizer 類, 像下面這樣操作: 

     
      import java.io.*;
      
      public class token1 {
        public static void main(String args[]) {
         if (args.length != 1) {
           System.err.println("missing filename");
           System.exit(1);
          }
          try {
            FileReader fr = new FileReader(args[0]);
            BufferedReader br = new BufferedReader(fr);
            StreamTokenizer st = new StreamTokenizer(br);
            st.resetSyntax();
            st.wordChars('a', 'z');
            int tok;
            while ((tok = st.nextToken()) !=
                StreamTokenizer.TT_EOF) {
              if (tok == StreamTokenizer.TT_WORD)
                ;// st.sval has token
            }
            br.close();
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }

    這個(gè)例子分解小寫單詞 (字母a-z)。如果你自己實(shí)現(xiàn)同等地功能,它可能像這樣: 
     

      import java.io.*;
      
      public class token2 {
        public static void main(String args[]) {
          if (args.length != 1) {
            System.err.println("missing filename");
            System.exit(1);
          }
          try {
            FileReader fr = new FileReader(args[0]);
            BufferedReader br = new BufferedReader(fr);
            int maxlen = 256;
            int currlen = 0;
            char wordbuf[] = new char[maxlen];
            int c;
            do {
              c = br.read();
              if (c >= 'a' && c <= 'z') {
                if (currlen == maxlen) {
                  maxlen *= 1.5;
                  char xbuf[] =
                      new char[maxlen];
                  System.arraycopy(
                      wordbuf, 0,
                      xbuf, 0, currlen);
                  wordbuf = xbuf;
                }
                wordbuf[currlen++] = (char)c;
              }
              else if (currlen > 0) {
                String s = new String(wordbuf,
                    0, currlen);
              // do something with s
                currlen = 0;
              }
            } while (c != -1);
            br.close();
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }

    第二個(gè)程序比前一個(gè)運(yùn)行快大約 20%,代價(jià)是寫一些微妙的底層代碼。 
    StreamTokenizer 是一種混合類,它從字符流(例如 BufferedReader)讀取, 但是同時(shí)以字節(jié)的形式操作,將所有的字符當(dāng)作雙字節(jié)(大于 0xff) ,即使它們是字母字符。 

    串行化

    串行化 以標(biāo)準(zhǔn)格式將任意的Java數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換為字節(jié)流。例如,下面的程序輸出隨機(jī)整數(shù)數(shù)組: 


      import java.io.*;
      import java.util.*;
      
      public class serial1 {
        public static void main(String args[]) {
          ArrayList al = new ArrayList();
          Random rn = new Random();
          final int N = 100000;
      
          for (int i = 1; i <= N; i++)
            al.add(new Integer(rn.nextInt()));
      
          try {
            FileOutputStream fos =
                new FileOutputStream("test.ser");
            BufferedOutputStream bos =
                new BufferedOutputStream(fos);
            ObjectOutputStream oos =
                new ObjectOutputStream(bos);
            oos.writeObject(al);
            oos.close();
          }
          catch (Throwable e) {
            System.err.println(e);
          }
        }
      }

    而下面的程序讀回?cái)?shù)組: 
     
     import java.io.*;
     import java.util.*;
      
      public class serial2 {
        public static void main(String args[]) {
          ArrayList al = null;
      
          try {
            FileInputStream fis =
                new FileInputStream("test.ser");
            BufferedInputStream bis =
                new BufferedInputStream(fis);
            ObjectInputStream ois =
                new ObjectInputStream(bis);
            al = (ArrayList)ois.readObject();
            ois.close();
          }
          catch (Throwable e) {
            System.err.println(e);
          }
        }
      }

    注意我們使用緩沖提高I/O操作的速度。 
    有比串行化更快的輸出大量數(shù)據(jù)然后讀回的方法嗎?可能沒有,除非在特殊的情況下。例如,假設(shè)你決定將文本輸出為64位的整數(shù)而不是一組8字節(jié)。作為文本的長(zhǎng)整數(shù)的最大長(zhǎng)度是大約20個(gè)字符,或者說二進(jìn)制表示的2.5倍長(zhǎng)。這種格式看起來(lái)不會(huì)快。然而,在某些情況下,例如位圖,一個(gè)特殊的格式可能是一個(gè)改進(jìn)。然而使用你自己的方案而不是串行化的標(biāo)準(zhǔn)方案將使你卷入一些權(quán)衡。 

    除了串行化實(shí)際的I/O和格式化開銷外(使用DataInputStream和 DataOutputStream), 還有其他的開銷,例如在串行化恢復(fù)時(shí)的創(chuàng)建新對(duì)象的需要。 

    注意DataOutputStream 方法也可以用于開發(fā)半自定義數(shù)據(jù)格式,例如: 

     

      import java.io.*;
      import java.util.*;
      
      public class binary1 {
        public static void main(String args[]) {
          try {
            FileOutputStream fos =
                new FileOutputStream("outdata");
            BufferedOutputStream bos =
                new BufferedOutputStream(fos);
            DataOutputStream dos =
                new DataOutputStream(bos);
            Random rn = new Random();
            final int N = 10;
            dos.writeInt(N);
            for (int i = 1; i <= N; i++) {
              int r = rn.nextInt();
              System.out.println(r);
              dos.writeInt(r);
            }
            dos.close();
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }

    和: 

      import java.io.*;
      
      public class binary2 {
        public static void main(String args[]) {
          try {
            FileInputStream fis =
                new FileInputStream("outdata");
            BufferedInputStream bis =
                new BufferedInputStream(fis);
            DataInputStream dis =
                new DataInputStream(bis);
            int N = dis.readInt();
            for (int i = 1; i <= N; i++) {
              int r = dis.readInt();
              System.out.println(r);
            }
            dis.close();
          }
          catch (IOException e) {
            System.err.println(e);
          }
        }
      }

    這些程序?qū)?0個(gè)整數(shù)寫入文件然后讀回它們。 

    獲取文件信息

    迄今為止我們的討論圍繞單一的文件輸入輸出。但是加速I/O性能還有另一方面--和得到文件特性有關(guān)。例如,考慮一個(gè)打印文件長(zhǎng)度的小程序: 


      import java.io.*;
      
      public class length1 {
        public static void main(String args[]) {
          if (args.length != 1) {
            System.err.println("missing filename");
            System.exit(1);
          }
          File f = new File(args[0]);
          long len = f.length();
          System.out.println(len);
        }
      }

    Java運(yùn)行時(shí)系統(tǒng)自身并不知道文件的長(zhǎng)度,因此必須向底層的操作系統(tǒng)查詢以獲得這個(gè)信息,對(duì)于文件的其他信息這也成立,例如文件是否是一個(gè)目錄,文件上次修改時(shí)間等等。 java.io包中的File 類提供一套查詢這些信息的方法。這些方法總體來(lái)說在時(shí)間上開銷很大因此應(yīng)該盡可能少用。 
    下面是一個(gè)查詢文件信息的更長(zhǎng)的范例,它遞歸整個(gè)文件系統(tǒng)寫出所有的文件路徑: 

     
      import java.io.*;
      
      public class roots {
        public static void visit(File f) {
          System.out.println(f);
        }
      
        public static void walk(File f) {
          visit(f);
          if (f.isDirectory()) {
            String list[] = f.list();
            for (int i = 0; i < list.length; i++)
              walk(new File(f, list[i]));
          }
        }
      
        public static void main(String args[]) {
          File list[] = File.listRoots();
          for (int i = 0; i < list.length; i++) {
            if (list[i].exists())
              walk(list[i]);
            else
              System.err.println("not accessible: "
                  + list[i]);
          }
        }
      }

    這個(gè)范例使用 File 方法,例如 isDirectory 和 exists,穿越目錄結(jié)構(gòu)。每個(gè)文件都被查詢一次它的類型 (普通文件或者目錄)。 

    轉(zhuǎn)http://www.csai.cn/
    posted on 2007-05-24 11:06 cheng 閱讀(267) 評(píng)論(0)  編輯  收藏 所屬分類: J2SE

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 亚洲黄黄黄网站在线观看| 四虎国产精品永久免费网址| 免费精品国产自产拍在| 亚洲精品视频在线播放| 麻豆视频免费观看| 色在线亚洲视频www| 无码高潮少妇毛多水多水免费| 亚洲自偷自偷在线成人网站传媒 | 亚洲免费电影网站| 久久精品亚洲中文字幕无码麻豆| 国产午夜无码片免费| 亚洲精品一级无码鲁丝片| eeuss影院免费92242部| 亚洲αv久久久噜噜噜噜噜| 久久w5ww成w人免费| 亚洲国产精品久久人人爱| 青娱乐免费视频在线观看| 亚洲精品无码av片| 美女被免费视频网站a国产| 久久无码av亚洲精品色午夜 | 亚洲人成人无码网www电影首页| 久久青青草原国产精品免费| 亚洲国产一区二区三区青草影视| 最近2019免费中文字幕视频三| 亚洲va久久久久| 亚洲第一永久AV网站久久精品男人的天堂AV | 久久国产乱子伦免费精品| 亚洲中文字幕一二三四区苍井空| 四虎AV永久在线精品免费观看| A级毛片成人网站免费看| 亚洲综合在线观看视频| 成人午夜性A级毛片免费| yellow免费网站| 亚洲宅男永久在线| 国产色爽女小说免费看| 中文在线观看永久免费| 亚洲一区二区三区精品视频| 免费人成年激情视频在线观看| 午夜影院免费观看| 真正全免费视频a毛片| 亚洲天天在线日亚洲洲精|