调整JavaTM 的I/O性能(三)(zt)

压缩


Java提供了对字节流进行压缩和解压缩的类。它们可以在java.util.zip包中被找到,同时也作为Jar文件的基 础(Jar文件是具有一个清单的Zip文件)。


以下的程序采用一个单一的输入文件,并且生成一个压缩了的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);

【问题提问、论坛交流】编辑:xker.com#p#副标题#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]);

          }

      }

下一个程序正好相反,采用假定只有一个入口项的Zip输入文件,并且将该项解压到指定的输出文件:


      import java.io.*;

      import java.util.zip.*;



      public class uncompress {

          public static void doit(String filein, String fileout) {

【问题提问、论坛交流】编辑:xker.com#p#副标题#e#

              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();

【问题提问、论坛交流】编辑:xker.com#p#副标题#e#

                      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性能很大程度上依赖于本地硬件设置;尤其是处理器和磁盘驱动器的相对速度。采用 Zip技术进行压缩,典型地将数据量减少50%,但是有一些压缩和解压的时间开销。在带IDE磁盘驱动器的 300-MHz Pentium PC上,对一个大的压缩文本文件(5-10MB)的实验表明,从磁盘读取压缩文件比非压缩文件 快将近1/3。


充分展现压缩优越性的一个例子是在写入象软盘这样的低速介质。使用高速处理器 (300 MHz Pentium)和低 速软盘(PC上常用的软盘驱动器)进行的测试显示,压缩一个大文本文件,然后写入软盘,比直接将文件拷 贝到软盘快50%。


高速缓存


关于硬件的高速缓存的详细讨论超出了本文的范围。但是,有时软件高速缓存 能够加速I/O。考虑这样一个 例子,需要以随机的方式读取文本文件的某些行。完成的方法之一是读入所有的行,并且把它们存储在 ArrayList(一个与Vector相似的collection类)中:


      import java.io.*;

      import java.util.ArrayList;



      public class LineCache {

          private ArrayList list = new ArrayList();

【问题提问、论坛交流】编辑:xker.com#p#副标题#e#



          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) {

【问题提问、论坛交流】编辑:xker.com#p#副标题#e#

                  System.err.println(e);

              }

          }

      }

getLine方法被用来检索任意的一行。这项技术非常有用,但对于大文件而言,显然需要使用了大量的内存, 因此具有局限性。一个替代方法是仅仅记住最近被请求的100行,必要时从磁盘上读取其他行。在存在行的存 储局部性这种情况下,这个方案运行良好,但是需要真正的随机存储时就没有这么好了。


标志化(Tokenization)


标志化(Tokenization)指将字节或者字符序列拆散成象词一样的逻辑块的过程。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

【问题提问、论坛交流】编辑:xker.com#p#副标题#e#

                  }

                  br.close();

              }

              catch (IOException e) {

                  System.err.println(e);

              }

          }

      }

本例根据小写字母(字母a-z)进行标记。如果由您自己实现了其等效程序,可能看上去象这样:


      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) {

【问题提问、论坛交流】编辑:xker.com#p#副标题#e#

                              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;

【问题提问、论坛交流】编辑:xker.com#p#副标题#e#

                          }

                  } while (c != -1);

                  br.close();

              }

              catch (IOException e) {

                  System.err.println(e);

              }

          }

      }

【问题提问、论坛交流】编辑:xker.com