欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
徹底轉變流,第 1 部分
徹底轉變流,第 1 部分
作者:Merlin 文章來(lái)源:IBM developerWorks 點(diǎn)擊數:75 更新時(shí)間:2006-7-18
【字體:

如果您有任何疑問(wèn),請到開(kāi)發(fā)論壇上提問(wèn)。

通常,Java I/O 框架用途極其廣泛。同一個(gè)框架支持文件存取、網(wǎng)絡(luò )訪(fǎng)問(wèn)、字符轉換、壓縮和加密等等。不過(guò),有時(shí)它不是十分靈活。例如,壓縮流允許您將數據寫(xiě)成壓縮格式,但它們不能讓您讀取壓縮格式的數據。同樣地,某些第三方模塊被構建成寫(xiě)出數據,而沒(méi)有考慮應用程序需要讀取數據的情形。本文是兩部分系列文章的第一部分,Java 密碼專(zhuān)家和作家 Merlin Hughes 介紹了使應用程序從僅支持將數據寫(xiě)至輸出流的源中有效讀取數據的框架。

自早期基于瀏覽器的 applet 和簡(jiǎn)單應用程序以來(lái),Java 平臺已有了巨大的發(fā)展?,F在,我們有多個(gè)平臺和概要及許多新的 API,并且還在制作的差不多有數百種之多。盡管 Java 語(yǔ)言的復雜程度在不斷增加,但它對于日常的編程任務(wù)而言仍是一個(gè)出色的工具。雖然有時(shí)您會(huì )陷入那些日復一日的編程問(wèn)題中,但偶爾您也能夠回過(guò)頭去,發(fā)現一個(gè)很棒的解決方案來(lái)處理您以前曾多次遇到過(guò)的問(wèn)題。

就在前幾天,我想要壓縮一些通過(guò)網(wǎng)絡(luò )連接讀取的數據(我以壓縮格式將 TCP 數據中繼到一個(gè) UDP 套接字)。記得 Java 平臺自版本 1.1 開(kāi)始就支持壓縮,所以我直接求助于 java.util.zip 包,希望能找到一個(gè)適合于我的解決方案。然而,我發(fā)現一個(gè)問(wèn)題:構造的類(lèi)都適用于常規情況,即在讀取時(shí)對數據解壓縮而在寫(xiě)入時(shí)壓縮它們,沒(méi)有其它變通方法。雖然繞過(guò) I/O 類(lèi)是可能的,但我希望構建一個(gè)基于流的解決方案,而不想偷懶直接使用壓縮程序。

不久以前,我在另一種情況下也遇到過(guò)完全相同的問(wèn)題。我有一個(gè) base-64 轉碼庫,與使用壓縮包一樣,它支持對從流中讀取的數據進(jìn)行譯碼,并對寫(xiě)入流中的數據進(jìn)行編碼。然而,我需要的是一個(gè)在我從流中讀取數據的同時(shí)可以進(jìn)行編碼的庫。

在我著(zhù)手解決該問(wèn)題時(shí),我認識到我在另一種情況下也遇到過(guò)該問(wèn)題:當序列化 XML 文檔時(shí),通常會(huì )循環(huán)遍歷整個(gè)文檔,將節點(diǎn)寫(xiě)入流中。然而,我遇到的情況是需要讀取序列化格式的文檔,以便將子集重新解析成一個(gè)新文檔。

回過(guò)頭想一下,我意識到這些孤立事件表示了一個(gè)共性的問(wèn)題:如果有一個(gè)遞增地將數據寫(xiě)入輸出流的數據源,那么我需要一個(gè)輸入流使我能夠讀取這些數據,每當需要更多數據時(shí),都能透明地訪(fǎng)問(wèn)數據源。

在本文中,我們將研究對這一問(wèn)題的三種可能的解決方案,同時(shí)決定一個(gè)實(shí)現最佳解決方案的新框架。然后,我們將針對上面列出的每個(gè)問(wèn)題,檢驗該框架。我們將扼要地談及性能方面的問(wèn)題,而把對此的大量討論留到下一篇文章中。

I/O 流基礎知識

首先,讓我們簡(jiǎn)單回顧一下 Java 平臺的基本流類(lèi),如圖 1 所示。 OutputStream 表示對其寫(xiě)入數據的流。通常,該流將直接連接至諸如文件或網(wǎng)絡(luò )連接之類(lèi)的設備,或連接至另一個(gè)輸出流(在這種情況下,它稱(chēng)為 過(guò)濾器(filter))。通常,輸出流過(guò)濾器在轉換了寫(xiě)入其中的數據之后,才將轉換后產(chǎn)生的數據寫(xiě)入相連的流中。 InputStream 表示可以從中讀取數據的流。同樣,該流也直接連接至設備或其它流。輸入流過(guò)濾器從相連的流中讀取數據,轉換該數據,然后允許從中讀取轉換后的數據。


圖 1. I/O 流基礎知識

就我最初的問(wèn)題看, GZIPOutputStream 類(lèi)是一個(gè)輸出流過(guò)濾器,它壓縮寫(xiě)入其中的數據,然后將該壓縮數據寫(xiě)入相連的流。我需要的輸入流過(guò)濾器應該能從流中讀取數據,壓縮數據,然后讓我讀取結果。

Java 平臺,版本 1.4 已引入了一個(gè)新的 I/O 框架 java.nio 。不過(guò),該框架在很大程度上與提供對操作系統 I/O 資源的有效訪(fǎng)問(wèn)有關(guān);而且,雖然它確實(shí)為一些傳統的 java.io 類(lèi)提供了類(lèi)似功能,并可以表示同時(shí)支持輸入和輸出的雙重用途的資源,但它并不能完全替代標準流類(lèi),并且不能直接處理我需要解決的問(wèn)題。







蠻力解決方案

在著(zhù)手尋找解決我問(wèn)題的工程方案前,我根據標準 Java API 類(lèi)的精致和有效性,研究了基于這些類(lèi)的解決方案。

該問(wèn)題的蠻力解決方案就是簡(jiǎn)單地從輸入源中讀取所有數據,然后通過(guò)轉換程序(即,壓縮流、編碼流或 XML 序列化器)將它們推進(jìn)內存緩沖區中。然后,我可以從該內存緩沖區中打開(kāi)要讀取的流,這樣我就解決了問(wèn)題。

首先,我需要一個(gè)通用的 I/O 方法。清單 1 中的方法利用一個(gè)小緩沖區將 InputStream 中的所有數據復制到 OutputStream 。當到達輸入的結尾( read() 函數的返回值小于零)時(shí),該方法就返回,但不關(guān)閉這兩個(gè)流。


清單 1. 通用的 I/O 方法
                        public static void io (InputStream in, OutputStream out)                        throws IOException {                        byte[] buffer = new byte[8192];                        int amount;                        while ((amount = in.read (buffer)) >= 0)                        out.write (buffer, 0, amount);                        }                        

清單 2 顯示蠻力解決方案如何使我讀取壓縮格式的輸入流。我打開(kāi)寫(xiě)入內存緩沖區的 GZIPOutputStream (使用 ByteArrayOutputStream )。接著(zhù),將輸入流復制到壓縮流中,這樣將壓縮數據填入內存緩沖區中。然后,我返回 ByteArrayInputStream ,它讓我從輸入流中讀取,如圖 2 所示。


圖 2. 蠻力解決方案


清單 2. 蠻力解決方案
                        public static InputStream bruteForceCompress (InputStream in)                        throws IOException {                        ByteArrayOutputStream sink = new ByteArrayOutputStream ():                        OutputStream out = new GZIPOutputStream (sink);                        io (in, out);                        out.close ();                        byte[] buffer = sink.toByteArray ();                        return new ByteArrayInputStream (buffer);                        }                        

這個(gè)解決方案有一個(gè)明顯的缺點(diǎn),它將整個(gè)壓縮文檔都存儲在內存中。如果文檔很大,那么這種方法將不必要地浪費系統資源。使用流的主要特性之一是它們允許您操作比所用系統內存要大的數據:您可以在讀取數據時(shí)處理它們,或在寫(xiě)入數據時(shí)生成數據,而無(wú)需始終將所有數據保存在內存中。

從效率上,讓我們對在緩沖區之間復制數據進(jìn)行更深入研究。

通過(guò) io() 方法,將數據從輸入源讀入至一個(gè)緩沖區中。然后,將數據從緩沖區寫(xiě)入 ByteArrayOutputStream 中的緩沖區(通過(guò)我忽略的壓縮過(guò)程)。然而, ByteArrayOutputStream 類(lèi)對擴展的內部緩沖區進(jìn)行操作;每當緩沖區變滿(mǎn)時(shí),就會(huì )分配一個(gè)大小是原來(lái)兩倍的新緩沖區,接著(zhù)將現有的數據復制到該緩沖區中。平均下來(lái),這一過(guò)程每個(gè)字節復制兩次。(算術(shù)計算很簡(jiǎn)單:當進(jìn)入 ByteArrayOutputStream 時(shí),對數據平均復制兩次;所有數據至少復制一次;有一半數據至少復制兩次;四分之一的數據至少復制三次,依次類(lèi)推。)然后,將數據從該緩沖區復制到 ByteArrayInputStream 的一個(gè)新緩沖區中?,F在,應用程序可以讀取數據了??傊?,這個(gè)解決方案將通過(guò)四個(gè)緩沖區寫(xiě)數據。這對于估計其它技術(shù)的效率是一個(gè)有用的基準。







管道式流解決方案

管道式流 PipedOutputStreamPipedInputStream 在 Java 虛擬機的線(xiàn)程之間提供了基于流的連接。一個(gè)線(xiàn)程將數據寫(xiě)入 PipedOutputStream 中的同時(shí),另一個(gè)線(xiàn)程可以從相關(guān)聯(lián)的 PipedInputStream 中讀取該數據。

就這樣,這些類(lèi)提供了一個(gè)針對我問(wèn)題的解決方案。清單 3 顯示了使用一個(gè)線(xiàn)程通過(guò) GZIPOutputStream 將數據從輸入流復制到 PipedOutputStream 的代碼。然后,相關(guān)聯(lián)的 PipedInputStream 將提供對來(lái)自另一個(gè)線(xiàn)程的壓縮數據的讀取權,如圖 3 所示:


圖 3. 管道式流解決方案


清單 3. 管道式流解決方案
                        private static InputStream pipedCompress (final InputStream in)                        throws IOException {                        PipedInputStream source = new PipedInputStream ();                        final OutputStream out =                        new GZIPOutputStream (new PipedOutputStream (source));                        new Thread () {                        public void run () {                        try {                        Streams.io (in, out);                        out.close ();                        } catch (IOException ex) {                        ex.printStackTrace ();                        }                        }                        }.start ();                        return source;                        }                        

理論上,這可能是個(gè)好技術(shù):通過(guò)使用線(xiàn)程(一個(gè)執行壓縮,另一個(gè)處理產(chǎn)生的數據),應用程序可以從硬件 SMP(對稱(chēng)多處理)或 SMT(對稱(chēng)多線(xiàn)程)中受益。另外,這一解決方案僅涉及兩個(gè)緩沖區寫(xiě)操作:I/O 循環(huán)將數據從輸入流讀入緩沖區,然后通過(guò)壓縮流寫(xiě)入 PipedOutputStream 。接著(zhù),輸出流將數據存儲在內部緩沖區中,與 PipedInputStream 共享緩沖區以供應用程序讀取。而且,因為數據通過(guò)固定緩沖區流動(dòng),所以從不需要將它們完全讀入內存中。事實(shí)上,在任何給定時(shí)刻,緩沖區都只存儲小部分的工作集。

不過(guò),實(shí)際上,它的性能很糟糕。管道式流需要利用同步,從而引起兩個(gè)線(xiàn)程之間激烈爭奪同步。它們的內部緩沖區太小,無(wú)法有效地處理大量數據或隱藏鎖爭用。其次,持久共享緩沖區會(huì )阻礙許多簡(jiǎn)單的高速緩存策略共享 SMP 機器上的工作負載。最后,線(xiàn)程的使用使得異常處理極其困難:沒(méi)有辦法將可能出現的任何 IOException 下推到管道中以便閱讀器處理??傊?,這一解決方案太難處理,根本不實(shí)際。

同步問(wèn)題

本文中提供的代碼都不能同步;也就是說(shuō),兩個(gè)線(xiàn)程并發(fā)地訪(fǎng)問(wèn)其中一個(gè)類(lèi)的共享實(shí)例是不安全的。
因為如 NIO 框架和 Collections API 之類(lèi)的庫已經(jīng)公認是實(shí)用的,所以使得同步成為應用程序中的一種負擔。如果應用程序希望對一個(gè)對象進(jìn)行并發(fā)訪(fǎng)問(wèn),應用程序必須采取必要的步驟來(lái)同步訪(fǎng)問(wèn)。
雖然最近的 JVM 已在其線(xiàn)程安全性機制的性能上有了很大的改進(jìn),但同步仍是一個(gè)開(kāi)銷(xiāo)很大的操作。在 I/O 的情況下,對單個(gè)流的并發(fā)訪(fǎng)問(wèn)幾乎必定是一個(gè)錯誤;結果數據流的次序是不確定的,這不是理想的情形。正因為這樣,要同步這些類(lèi)會(huì )強加一些不必要的費用,又沒(méi)有確實(shí)的收益。
我們將在這一系列文章的第 2 部分更詳細討論多線(xiàn)程的考慮事項;目前,只要注意:對我所提供的對流的并發(fā)訪(fǎng)問(wèn)將導致不確定錯誤。







工程解決方案

現在,我們將研究另一種解決該問(wèn)題的工程方案。這種解決方案提供了一個(gè)特地為解決這類(lèi)問(wèn)題而設計的框架,該框架提供了對數據的 InputStream 訪(fǎng)問(wèn),這些數據是從遞增地向 OutputStream 寫(xiě)入數據的源中產(chǎn)生的。遞增地寫(xiě)入數據這一事實(shí)很重要。如果源在單個(gè)原子操作中將所有數據都寫(xiě)入 OutputStream ,而且如果不使用線(xiàn)程,則我們基本上又回到了蠻力技術(shù)的老路上。不過(guò),如果可以訪(fǎng)問(wèn)源以遞增地寫(xiě)入其數據,則我們就實(shí)現了在蠻力和管道式流解決方案之間的良好平衡。該解決方案不僅提供了在任何時(shí)候只在內存中保存少量數據的管道式優(yōu)點(diǎn),同時(shí)也提供了避免線(xiàn)程的蠻力技術(shù)的優(yōu)點(diǎn)。

圖 4 演示了完整的解決方案。我們將在本文的剩余部分研究 該解決方案的源代碼。


圖 4. 工程解決方案

輸出引擎

清單 4 提供了一個(gè)描述數據源的接口 OutputEngine 。正如我所說(shuō)的,這些源遞增地將數據寫(xiě)入輸出流:


清單 4. 輸出引擎
                        package org.merlin.io;                        import java.io.*;                        /**                        * An incremental data source that writes data to an OutputStream.                        *                        * @author Copyright (c) 2002 Merlin Hughes <merlin@merlin.org>                        *                        * This program is free software; you can redistribute                        * it and/or modify it under the terms of the GNU                        * General Public License as published by the Free                        * Software Foundation; either version 2                        * of the License, or (at your option) any later version.                        */                        public interface OutputEngine {                        public void initialize (OutputStream out) throws IOException;                        public void execute () throws IOException;                        public void finish () throws IOException;                        }                        

initialize() 方法向該引擎提供一個(gè)流,應該向這個(gè)流寫(xiě)入數據。然后,重復調用 execute() 方法將數據寫(xiě)入該流中。當數據寫(xiě)完時(shí),引擎會(huì )關(guān)閉該流。最后,當引擎應該關(guān)閉時(shí),將調用 finish() 。這會(huì )發(fā)生在引擎關(guān)閉其輸出流的前后。

I/O 流引擎

輸出引擎解決了讓我費力處理的問(wèn)題,它是一個(gè)通過(guò)輸出流過(guò)濾器將數據從輸入流復制到目標輸出流的引擎。這滿(mǎn)足了遞增性的特性,因為它可以一次讀寫(xiě)單個(gè)緩沖區。

清單 5 到 10 中的代碼實(shí)現了這樣的一個(gè)引擎。通過(guò)輸入流和輸入流工廠(chǎng)來(lái)構造它。清單 11 是一個(gè)生成過(guò)濾后的輸出流的工廠(chǎng);例如,它會(huì )返回包裝了目標輸出流的 GZIPOutputStream 。


清單 5. I/O 流引擎
                        package org.merlin.io;                        import java.io.*;                        /**                        * An output engine that copies data from an InputStream through                        * a FilterOutputStream to the target OutputStream.                        *                        * @author Copyright (c) 2002 Merlin Hughes <merlin@merlin.org>                        *                        * This program is free software; you can redistribute it and/or                        * modify it under the terms of the GNU General Public License                        * as published by the Free Software Foundation; either version 2                        * of the License, or (at your option) any later version.                        */                        public class IOStreamEngine implements OutputEngine {                        private static final int DEFAULT_BUFFER_SIZE = 8192;                        private InputStream in;                        private OutputStreamFactory factory;                        private byte[] buffer;                        private OutputStream out;                        

該類(lèi)的構造器只初始化各種變量和將用于傳輸數據的緩沖區。


清單 6. 構造器
                          public IOStreamEngine (InputStream in, OutputStreamFactory factory) {                        this (in, factory, DEFAULT_BUFFER_SIZE);                        }                        public IOStreamEngine                        (InputStream in, OutputStreamFactory factory, int bufferSize) {                        this.in = in;                        this.factory = factory;                        buffer = new byte[bufferSize];                        }                        

initialize() 方法中,該引擎調用其工廠(chǎng)來(lái)封裝與其一起提供的 OutputStream 。該工廠(chǎng)通常將一個(gè)過(guò)濾器連接至 OutputStream 。


清單 7. initialize() 方法
                          public void initialize (OutputStream out) throws IOException {                        if (this.out != null) {                        throw new IOException ("Already initialised");                        } else {                        this.out = factory.getOutputStream (out);                        }                        }                        

execute() 方法中,引擎從 InputStream 中讀取一個(gè)緩沖區的數據,然后將它們寫(xiě)入已封裝的 OutputStream ;或者,如果輸入結束,它會(huì )關(guān)閉 OutputStream 。


清單 8. execute() 方法
                          public void execute () throws IOException {                        if (out == null) {                        throw new IOException ("Not yet initialised");                        } else {                        int amount = in.read (buffer);                        if (amount < 0) {                        out.close ();                        } else {                        out.write (buffer, 0, amount);                        }                        }                        }                        

最后,當關(guān)閉引擎時(shí),它就關(guān)閉其 InputStream 。


清單 9. 關(guān)閉 InputStream
                          public void finish () throws IOException {                        in.close ();                        }                        

內部 OutputStreamFactory 接口(下面清單 10 中所示)描述可以返回過(guò)濾后的 OutputStream 的類(lèi)。


清單 10. 內部輸出流工廠(chǎng)接口
                          public static interface OutputStreamFactory {                        public OutputStream getOutputStream (OutputStream out)                        throws IOException;                        }                        }                        

清單 11 顯示將提供的流封裝到 GZIPOutputStream 中的一個(gè)示例工廠(chǎng):


清單 11. GZIP 輸出流工廠(chǎng)
                        public class GZIPOutputStreamFactory                        implements IOStreamEngine.OutputStreamFactory {                        public OutputStream getOutputStream (OutputStream out)                        throws IOException {                        return new GZIPOutputStream (out);                        }                        }                        

該 I/O 流引擎及其輸出流工廠(chǎng)框架通常足以支持大多數的輸出流過(guò)濾需要。

輸出引擎輸入流

最后,我們還需要一小段代碼來(lái)完成這個(gè)解決方案。清單 12 到 16 中的代碼提供了讀取由輸出引擎所寫(xiě)數據的輸入流。事實(shí)上,這段代碼有兩個(gè)部分:主類(lèi)是一個(gè)從內部緩沖區讀取數據的輸入流。與此緊密耦合的是一個(gè)輸出流(如清單 17 所示),它把輸出引擎所寫(xiě)的數據填充到內部讀緩沖區。

主輸入流類(lèi)將用其內部輸出流來(lái)初始化輸出引擎。然后,每當它的緩沖區為空時(shí),它會(huì )自動(dòng)執行該引擎來(lái)接收更多數據。輸出引擎將數據寫(xiě)入其輸出流中,這將重新填充輸入流的內部緩沖區,以允許需要內部緩沖區數據的應用程序高效地讀取數據。


清單 12. 輸出引擎輸入流
                        package org.merlin.io;                        import java.io.*;                        /**                        * An input stream that reads data from an OutputEngine.                        *                        * @author Copyright (c) 2002 Merlin Hughes <merlin@merlin.org>                        *                        * This program is free software; you can redistribute it and/or                        * modify it under the terms of the GNU General Public License                        * as published by the Free Software Foundation; either version 2                        * of the License, or (at your option) any later version.                        */                        public class OutputEngineInputStream extends InputStream {                        private static final int DEFAULT_INITIAL_BUFFER_SIZE = 8192;                        private OutputEngine engine;                        private byte[] buffer;                        private int index, limit, capacity;                        private boolean closed, eof;                        

該輸入流的構造器獲取一個(gè)輸出引擎以從中讀取數據和一個(gè)可選的緩沖區大小。該流首先初始化其本身,然后初始化輸出引擎。


清單 13. 構造器
                        public OutputEngineInputStream (OutputEngine engine) throws IOException {                        this (engine, DEFAULT_INITIAL_BUFFER_SIZE);                        }                        public OutputEngineInputStream (OutputEngine engine, int initialBufferSize)                        throws IOException {                        this.engine = engine;                        capacity = initialBufferSize;                        buffer = new byte[capacity];                        engine.initialize (new OutputStreamImpl ());                        }                        

代碼的主要讀部分是一個(gè)相對簡(jiǎn)單的基于字節數組的輸入流,與 ByteArrayInputStream 類(lèi)非常相似。然而,每當需要數據而該流為空時(shí),它都會(huì )調用輸出引擎的 execute() 方法來(lái)重新填寫(xiě)讀緩沖區。然后,將這些新數據返回給調用程序。因而,這個(gè)類(lèi)將對輸出引擎所寫(xiě)的數據反復讀取,直到它讀完為止,此時(shí)將設置 eof 標志并且該流將返回已到達文件末尾的信息。


清單 14. 讀取數據
                          private byte[] one = new byte[1];                        public int read () throws IOException {                        int amount = read (one, 0, 1);                        return (amount < 0) ? -1 : one[0] & 0xff;                        }                        public int read (byte data[], int offset, int length)                        throws IOException {                        if (data == null) {                        throw new NullPointerException ();                        } else if                        ((offset < 0) || (length < 0) || (offset + length > data.length)) {                        throw new IndexOutOfBoundsException ();                        } else if (closed) {                        throw new IOException ("Stream closed");                        } else {                        while (index >= limit) {                        if (eof)                        return -1;                        engine.execute ();                        }                        if (limit - index < length)                        length = limit - index;                        System.arraycopy (buffer, index, data, offset, length);                        index += length;                        return length;                        }                        }                        public long skip (long amount) throws IOException {                        if (closed) {                        throw new IOException ("Stream closed");                        } else if (amount <= 0) {                        return 0;                        } else {                        while (index >= limit) {                        if (eof)                        return 0;                        engine.execute ();                        }                        if (limit - index < amount)                        amount = limit - index;                        index += (int) amount;                        return amount;                        }                        }                        public int available () throws IOException {                        if (closed) {                        throw new IOException ("Stream closed");                        } else {                        return limit - index;                        }                        }                        

當操作數據的應用程序關(guān)閉該流時(shí),它調用輸出引擎的 finish() 方法,以便可以釋放其正在使用的任何資源。


清單 15. 釋放資源
                          public void close () throws IOException {                        if (!closed) {                        closed = true;                        engine.finish ();                        }                        }                        

當輸出引擎將數據寫(xiě)入其輸出流時(shí),調用 writeImpl() 方法。它將這些數據復制到讀緩沖區,并更新讀限制索引;這將使新數據可自動(dòng)地用于讀方法。

在單次循環(huán)中,如果輸出引擎寫(xiě)入的數據比緩沖區中可以保存的數據多,則緩沖區的容量會(huì )翻倍。然而,這不能頻繁發(fā)生;緩沖區應該快速擴展到足夠的大小,以便進(jìn)行狀態(tài)穩定的操作。


清單 16. writeImpl() 方法
                          private void writeImpl (byte[] data, int offset, int length) {                        if (index >= limit)                        index = limit = 0;                        if (limit + length > capacity) {                        capacity = capacity * 2 + length;                        byte[] tmp = new byte[capacity];                        System.arraycopy (buffer, index, tmp, 0, limit - index);                        buffer = tmp;                        limit -= index;                        index = 0;                        }                        System.arraycopy (data, offset, buffer, limit, length);                        limit += length;                        }                        

下面清單 17 中顯示的內部輸出流實(shí)現表示了一個(gè)流將數據寫(xiě)入內部輸出流緩沖區。該代碼驗證參數都是可接受的,并且如果是這樣的話(huà),它調用 writeImpl() 方法。


清單 17. 內部輸出流實(shí)現
                          private class OutputStreamImpl extends OutputStream {                        public void write (int datum) throws IOException {                        one[0] = (byte) datum;                        write (one, 0, 1);                        }                        public void write (byte[] data, int offset, int length)                        throws IOException {                        if (data == null) {                        throw new NullPointerException ();                        } else if                        ((offset < 0) || (length < 0) || (offset + length > data.length)) {                        throw new IndexOutOfBoundsException ();                        } else if (eof) {                        throw new IOException ("Stream closed");                        } else {                        writeImpl (data, offset, length);                        }                        }                        

最后,當輸出引擎關(guān)閉其輸出流,表明它已寫(xiě)入了所有的數據時(shí),該輸出流設置輸入流的 eof 標志,表明已經(jīng)讀取了所有的數據。


清單 18. 設置輸入流的 eof 標志
                            public void close () {                        eof = true;                        }                        }                        }                        

敏感的讀者可能注意到我應該將 writeImpl() 方法的主體直接放在輸出流實(shí)現中:內部類(lèi)有權訪(fǎng)問(wèn)所有包含類(lèi)的私有成員。然而,對這些字段的內部類(lèi)訪(fǎng)問(wèn)比由包含類(lèi)的直接方法的訪(fǎng)問(wèn)在效率方面稍許差一些。所以,考慮到效率以及為了使類(lèi)之間的相關(guān)性最小化,我使用額外的助手方法。







應用工程解決方案:在讀取期間壓縮數據

清單 19 演示了這個(gè)類(lèi)框架的使用來(lái)解決我最初的問(wèn)題:在我讀取數據時(shí)壓縮它們。該解決方案歸結為創(chuàng )建一個(gè)與輸入流相關(guān)聯(lián)的 IOStreamEngine 和一個(gè) GZIPOutputStreamFactory ,然后將 OutputEngineInputStream 與這個(gè) GZIPOutputStreamFactory 相連。自動(dòng)執行流的初始化和連接,然后可以直接從結果流中讀取壓縮數據。當處理完成且關(guān)閉流時(shí),輸出引擎自動(dòng)關(guān)閉,并且它關(guān)閉初始輸入流。


清單 19. 應用工程解決方案
                          private static InputStream engineCompress (InputStream in)                        throws IOException {                        return new OutputEngineInputStream                        (new IOStreamEngine (in, new GZIPOutputStreamFactory ()));                        }                        

雖然為解決這類(lèi)問(wèn)題而設計的解決方案應該產(chǎn)生十分清晰的代碼,這一點(diǎn)沒(méi)有什么可驚奇的,但是通常要充分留意以下教訓:無(wú)論問(wèn)題大小,應用良好的設計技術(shù)都幾乎肯定會(huì )產(chǎn)生更為清晰、更便于維護的代碼。







測試性能

從效率看, IOStreamEngine 將數據讀入其內部緩沖區,然后通過(guò)壓縮過(guò)濾器將它們寫(xiě)入 OutputStreamImpl 。這將數據直接寫(xiě)入 OutputEngineInputStream ,以便它們可供讀取??偣仓粓绦袃纱尉彌_區復制,這意味著(zhù)我應該從管道式流解決方案的緩沖區復制效率和蠻力解決方案的無(wú)線(xiàn)程效率的結合中獲益。

要測試實(shí)際的性能,我編寫(xiě)了一個(gè)簡(jiǎn)單的測試工具(請參閱所附 資源中的 test.PerformanceTest ),它使用這三個(gè)推薦的解決方案,通過(guò)使用一個(gè)空過(guò)濾器來(lái)讀取一塊啞元數據。在運行 Java 2 SDK,版本 1.4.0 的 800 MHz Linux 機器上,達到了下列性能:

管道式流解決方案
15KB:23ms;15MB:22100ms
蠻力解決方案
15KB:0.35ms;15MB:745ms
工程解決方案
15KB:0.16ms;15MB:73ms

該問(wèn)題的工程解決方案很明顯比基于標準 Java API 的另兩個(gè)方法都更有效。

順便提一下,考慮到如果輸出引擎能夠遵守這樣的約定:在將數據寫(xiě)入其輸出流后,它不修改從中寫(xiě)入數據的數組而返回,那么我能提供一個(gè)只使用一次緩沖區復制操作的解決方案??墒?,輸出引擎很少會(huì )遵守這種約定。如果需要,輸出引擎只要通過(guò)實(shí)現適當的標記程序接口,就能宣稱(chēng)它支持這種方式的操作。







應用工程解決方案:讀取編碼的字符數據

任何可以用“提供對將數據反復寫(xiě)入 OutputStream 的實(shí)體的讀訪(fǎng)問(wèn)權”表述的問(wèn)題,都可以用這一框架解決。在這一節和下一節中,我們將研究這樣的問(wèn)題示例及其有效的解決方案。

首先,考慮要讀取 UTF-8 編碼格式的字符流的情況: InputStreamReader 類(lèi)讓您將以二進(jìn)制編碼的字符數據作為一系列 Unicode 字符讀??;它表示了從字節輸入流到字符輸入流的關(guān)口。 OutputStreamWriter 類(lèi)讓您將一系列二進(jìn)制編碼格式的 Unicode 字符寫(xiě)入輸出流;它表示從字符輸出流到字節輸入流的關(guān)口。 String 類(lèi)的 getBytes() 方法將字符串轉換成經(jīng)編碼的字節數組。然而,這些類(lèi)中沒(méi)有一個(gè)能直接讓您讀取 UTF-8 編碼格式的字符流。

清單 20 到 24 中的代碼演示了以與 IOStreamEngine 類(lèi)極其相似的方式使用 OutputEngine 框架的一種解決方案。我們并不是從輸入流讀取和通過(guò)輸出流過(guò)濾器進(jìn)行寫(xiě)操作,而是從字符流讀取,并通過(guò)所選的字符進(jìn)行編碼的 OutputStreamWriter 進(jìn)行寫(xiě)操作。


清單 20. 讀取編碼的字符數據
                        package org.merlin.io;                        import java.io.*;                        /**                        * An output engine that copies data from a Reader through                        * a OutputStreamWriter to the target OutputStream.                        *                        * @author Copyright (c) 2002 Merlin Hughes <merlin@merlin.org>                        *                        * This program is free software; you can redistribute it and/or                        * modify it under the terms of the GNU General Public License                        * as published by the Free Software Foundation; either version 2                        * of the License, or (at your option) any later version.                        */                        public class ReaderWriterEngine implements OutputEngine {                        private static final int DEFAULT_BUFFER_SIZE = 8192;                        private Reader reader;                        private String encoding;                        private char[] buffer;                        private Writer writer;                        

該類(lèi)的構造器接受要從中讀取的字符流、要使用的編碼以及可選的緩沖區大小。


清單 21. 構造器
                          public ReaderWriterEngine (Reader in, String encoding) {                        this (in, encoding, DEFAULT_BUFFER_SIZE);                        }                        public ReaderWriterEngine                        (Reader reader, String encoding, int bufferSize) {                        this.reader = reader;                        this.encoding = encoding;                        buffer = new char[bufferSize];                        }                        

當該引擎初始化時(shí),它將以所選編碼格式寫(xiě)字符的 OutputStreamWriter 連接至提供的輸出流。


清單 22. 初始化輸出流寫(xiě)程序
                          public void initialize (OutputStream out) throws IOException {                        if (writer != null) {                        throw new IOException ("Already initialised");                        } else {                        writer = new OutputStreamWriter (out, encoding);                        }                        }                        

當執行該引擎時(shí),它從輸入字符流中讀取數據,然后將它們寫(xiě)入 OutputStreamWriter ,接著(zhù) OutputStreamWriter 將它們以所選的編碼格式傳遞給相連的輸出流。至此,該框架使數據可供讀取。


清單 23. 讀取數據
                          public void execute () throws IOException {                        if (writer == null) {                        throw new IOException ("Not yet initialised");                        } else {                        int amount = reader.read (buffer);                        if (amount < 0) {                        writer.close ();                        } else {                        writer.write (buffer, 0, amount);                        }                        }                        }                        

當引擎執行完時(shí),它關(guān)閉其輸入。


清單 24. 關(guān)閉輸入
                          public void finish () throws IOException {                        reader.close ();                        }                        }                        

在這種與壓縮不同的情況中,Java I/O 包不提供對 OutputStreamWriter 之下的字符編碼類(lèi)的低級別訪(fǎng)問(wèn)。因此,這是在 Java 平臺 1.4 之前的發(fā)行版上讀取編碼格式的字符流的唯一有效解決方案。從版本 1.4 開(kāi)始, java.nio.charset 包確實(shí)提供了與流無(wú)關(guān)的字符編碼和譯碼能力。然而,這個(gè)包不能滿(mǎn)足我們對基于輸入流的解決方案的要求。







應用工程解決方案:讀取序列化的 DOM 文檔

最后,讓我們研究該框架的最后一種用法。清單 25 到 29 中的代碼提供了一個(gè)用來(lái)讀取序列化格式的 DOM 文檔或文檔子集的解決方案。這一代碼的潛在用途可能是對部分 DOM 文檔執行確認性重新解析。


清單 25. 讀取序列化的 DOM 文檔
                        package org.merlin.io;                        import java.io.*;                        import java.util.*;                        import org.w3c.dom.*;                        import org.w3c.dom.traversal.*;                        /**                        * An output engine that serializes a DOM tree using a specified                        * character encoding to the target OutputStream.                        *                        * @author Copyright (c) 2002 Merlin Hughes <merlin@merlin.org>                        *                        * This program is free software; you can redistribute it and/or                        * modify it under the terms of the GNU General Public License                        * as published by the Free Software Foundation; either version 2                        * of the License, or (at your option) any later version.                        */                        public class DOMSerializerEngine implements OutputEngine {                        private NodeIterator iterator;                        private String encoding;                        private OutputStreamWriter writer;                        

構造器獲取要在上面進(jìn)行循環(huán)的 DOM 節點(diǎn),或預先構造的節點(diǎn)迭代器(這是 DOM 2 的一部分),以及一個(gè)用于序列化格式的編碼。


清單 26. 構造器
                          public DOMSerializerEngine (Node root) {                        this (root, "UTF-8");                        }                        public DOMSerializerEngine (Node root, String encoding) {                        this (getIterator (root), encoding);                        }                        private static NodeIterator getIterator (Node node) {                        DocumentTraversal dt= (DocumentTraversal)                        (node.getNodeType () ==                        Node.DOCUMENT_NODE) ? node : node.getOwnerDocument ();                        return dt.createNodeIterator (node, NodeFilter.SHOW_ALL, null, false);                        }                        public DOMSerializerEngine (NodeIterator iterator, String encoding) {                        this.iterator = iterator;                        this.encoding = encoding;                        }                        

初始化期間,該引擎將適當的 OutputStreamWriter 連接至目標輸出流。


清單 27. initialize() 方法
                          public void initialize (OutputStream out) throws IOException {                        if (writer != null) {                        throw new IOException ("Already initialised");                        } else {                        writer = new OutputStreamWriter (out, encoding);                        }                        }                        

在執行階段,該引擎從節點(diǎn)迭代器中獲得下一個(gè)節點(diǎn),然后將其序列化至 OutputStreamWriter 。當獲取了所有節點(diǎn)后,引擎關(guān)閉它的流。


清單 28. execute() 方法
                          public void execute () throws IOException {                        if (writer == null) {                        throw new IOException ("Not yet initialised");                        } else {                        Node node = iterator.nextNode ();                        closeElements (node);                        if (node == null) {                        writer.close ();                        } else {                        writeNode (node);                        writer.flush ();                        }                        }                        }                        

當該引擎關(guān)閉時(shí),沒(méi)有要釋放的資源。


清單 29. 關(guān)閉
                          public void finish () throws IOException {                        }                        // private void closeElements (Node node) throws IOException ...                        // private void writeNode (Node node) throws IOException ...                        }                        

序列化每個(gè)節點(diǎn)的其它內部細節不太有趣;這一過(guò)程主要涉及根據節點(diǎn)的類(lèi)型和 XML 1.0 規范寫(xiě)出節點(diǎn),所以我將在本文中省略這一部分的代碼。請參閱附帶的 源代碼,獲取完整的詳細信息。







結束語(yǔ)

我所提供的是一個(gè)有用的框架,它利用標準輸入流 API 讓您能有效讀取由只能寫(xiě)入輸出流的系統產(chǎn)生的數據。它讓我們讀取經(jīng)壓縮或編碼的數據及序列化文檔等。雖然可以使用標準 Java API 實(shí)現這一功能,但使用這些類(lèi)的效率根本不行。應該充分注意到,這種解決方案比最簡(jiǎn)單的蠻力解決方案更有效(即使在數據不大的情況下)。將數據寫(xiě)入 ByteArrayOutputStream 以便進(jìn)行后續處理的任何應用程序都可能從這一框架中受益。

字節數組流的拙劣性能和管道式流難以置信的蹩腳性能,實(shí)際上都是我下一篇文章的主題。在那篇文章中,我將研究重新實(shí)現這些類(lèi),并比這些類(lèi)的原創(chuàng )者更加關(guān)注它們的性能。只要 API 約定稍微寬松一點(diǎn),性能就可能改進(jìn)一百倍了。

我討厭洗碗。不過(guò),正如大多數我自認為是較好(雖然常常還是微不足道)的想法一樣,這些類(lèi)背后的想法都是在我洗碗時(shí)冒出來(lái)的。我時(shí)常發(fā)現撇開(kāi)實(shí)際代碼,回頭看看并且把問(wèn)題的范圍考慮得更廣些,可能會(huì )得出一個(gè)更好的解決方案,它最終為您提供的方法可能比您找出的容易方法更好。這些解決方案常常會(huì )產(chǎn)生更清晰、更有效而且更可維護的代碼。

我真的擔心我們有了洗碗機的那一天。







參考資料







關(guān)于作者

Merlin 是 Baltimore Technologies的密碼專(zhuān)家和首席技術(shù)傳道者,該公司是一家愛(ài)爾蘭人開(kāi)辦的電子安全性公司,他還是兼職作家、業(yè)余門(mén)房和“洗碗工”;他不會(huì )因 JDK 1.4 而煩惱。他住在紐約州紐約市(一個(gè)非常美的城市,人們給它取了兩次名字),可以通過(guò) merlin@merlin.org與他聯(lián)系。

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
java中基本輸入輸出流的解釋
NIO核心概念及基本讀寫(xiě)AAA
Java網(wǎng)絡(luò )編程
理解java.io的類(lèi)繼承關(guān)系
16. 文件的保存與讀取
將輸出流(OutputStream)轉化為輸入流(InputStream)的方法(二)
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久