Java USES ByteArrayOutputStream and ByteArrayInputStream to avoid repeating methods that read the configuration file

  • 2020-04-01 04:29:02
  • OfStack

The ByteArrayOutputStream class creates a buffer inside the program for a byte array when its instance is created, and then writes or reads byte data to the array using instances of ByteArrayOutputStream and ByteArrayInputStream. In network transmission, we often transfer many variables, we can use the ByteArrayOutputStream to collect all variables together, and then send the data out at once. The specific usage is as follows:

ByteArrayOutputStream:       You can capture data from memory buffers and convert it into byte arrays.

ByteArrayInputStream: converts byte arrays into input streams

The ByteArrayInputStream class has two default constructors:

ByteArrayInputStream(byte[] b): USES all the data in a byte array as the data source. The program can read the bytes just like the input stream.

ByteArrayInputStream(byte[] b,int offset,int length): from the array offset, all the way to the length of this byte as the data source.

The ByteArrayOutputStream class also has two default constructors:

ByteArrayOutputStream(): creates a 32-byte buffer
ByteArrayOutputStream(int): creates a buffer based on the size specified by the parameter

I was recently involved in an open source project on github called Mycat, which is a middleware for mysql's library sub-table. Found which read the configuration file code, there are frequent repeated open, read, close the problem, the code is very elementary, a little bit of the framework source code, is not to make such a mistake. So we optimized it a little bit.

The code before optimization is shown as follows:


private static Element loadRoot() {
  InputStream dtd = null;
  InputStream xml = null;
  Element root = null;
  try {
    dtd = ConfigFactory.class.getResourceAsStream("/mycat.dtd");
    xml = ConfigFactory.class.getResourceAsStream("/mycat.xml");
    root = ConfigUtil.getDocument(dtd, xml).getDocumentElement();
  } catch (ConfigException e) {
    throw e;
  } catch (Exception e) {
    throw new ConfigException(e);
  } finally {
    if (dtd != null) {
      try {
        dtd.close();
      } catch (IOException e) { }
    }
    if (xml != null) {
      try {
        xml.close();
      } catch (IOException e) { }
    }
  }
  return root;
} 

Then other methods call loadRoot() frequently:


@Override
public UserConfig getUserConfig(String user) {
  Element root = loadRoot();
  loadUsers(root);
  return this.users.get(user);
}
@Override
public Map<String, UserConfig> getUserConfigs() {
  Element root = loadRoot();
  loadUsers(root);
  return users;
}
@Override
public SystemConfig getSystemConfig() {
  Element root = loadRoot();
  loadSystem(root);
  return system;
}
// ... ... 

The method ConfigUtil. GetDocument (DTD, XML) is as follows:


public static Document getDocument(final InputStream dtd, InputStream xml) throws ParserConfigurationException,
      SAXException, IOException {
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//factory.setValidating(false);
    factory.setNamespaceAware(false);
    DocumentBuilder builder = factory.newDocumentBuilder();
    builder.setEntityResolver(new EntityResolver() {
      @Override
      public InputSource resolveEntity(String publicId, String systemId) {
        return new InputSource(dtd);
      }
    });
    builder.setErrorHandler(new ErrorHandler() {
      @Override
      public void warning(SAXParseException e) {
      }
      @Override
      public void error(SAXParseException e) throws SAXException {
        throw e;
      }
      @Override
      public void fatalError(SAXParseException e) throws SAXException {
        throw e;
      }
    });
    return builder.parse(xml);
  } 

Obviously that's not a very good way to handle it. Because the configuration file will be repeated many times.

1. First optimization:

Why not read it once and cache it? The other methods then simply use the cache when they call loadRoot(). However, there is a problem that InputStream cannot be cached and read repeatedly, because once the InputStream is read, its pos pointer, etc., will change and cannot be read repeatedly. So you can only read the contents of the configuration file and put it in   Byte [] is cached, and then with the ByteArrayOutputStream, the contents of the byte[] cache can be read repeatedly. Then use ByteArrayOutputStream to construct the InputStream so as to read the configuration file once, and then repeatedly construct the InputStream for repeated reading. The relevant code is as follows:


//To avoid frequent loadRoot calls in the source code to frequently read /mycat.dtd and /mycat.xml, cache the two files,
//Note that the cache is not always cached in memory, and as the LocalLoader object is recycled, the memory occupied by the cache will naturally be recycled as well.
private static byte[] xmlBuffer = null;
private static byte[] dtdBuffer = null;
private static ByteArrayOutputStream xmlBaos = null;
private static ByteArrayOutputStream dtdBaos = null;
static {
  InputStream input = ConfigFactory.class.getResourceAsStream("/mycat.dtd");
  if(input != null){
    dtdBuffer = new byte[1024 * 512];
    dtdBaos = new ByteArrayOutputStream();
    bufferFileStream(input, dtdBuffer, dtdBaos);
  }
  input = ConfigFactory.class.getResourceAsStream("/mycat.xml");
  if(input != null){
    xmlBuffer = new byte[1024 * 512];
    xmlBaos = new ByteArrayOutputStream();
    bufferFileStream(input, xmlBuffer, xmlBaos);
  }
} 

BufferFileStream method:


private static void bufferFileStream(InputStream input, byte[] buffer, ByteArrayOutputStream baos){
  int len = -1;
  try {
    while ((len = input.read(buffer)) > -1 ) {
      baos.write(buffer, 0, len);
    }
    baos.flush();
  } catch (IOException e) {
    e.printStackTrace();
    logger.error(" bufferFileStream error: " + e.getMessage());
  }
} 

  LoadRoat is optimized as follows:


private static Element loadRoot() {
  Element root = null;
  InputStream mycatXml = null;
  InputStream mycatDtd = null;
  if(xmlBaos != null)
    mycatXml = new ByteArrayInputStream(xmlBaos.toByteArray());
  if(dtdBaos != null)
    mycatDtd = new ByteArrayInputStream(dtdBaos.toByteArray());
  try {
  root = ConfigUtil.getDocument(mycatDtd, mycatXml).getDocumentElement();
  } catch (ParserConfigurationException | SAXException | IOException e1) {
    e1.printStackTrace();
    logger.error("loadRoot error: " + e1.getMessage());
  }finally{
    if(mycatXml != null){
      try { mycatXml.close(); } catch (IOException e) {}
    }
    if(mycatDtd != null){
      try { mycatDtd.close(); } catch (IOException e) {}
    }
  }
  return root;
} 

After this optimization, even if many methods call the loadRoot() method frequently, they will not read the configuration file repeatedly, but will use the byte[] content to repeatedly construct the InputStream.

In fact, the principle is to use byte[] as an intermediate container to cache byte, and ByteArrayOutputStream will store the byte read from InputStream in the byte[] container, and then use ByteArrayInputStream to read from the byte[] container to construct InputStream. As long as the byte[] cache container exists, InputStream can be repeatedly constructed. The result is to read the configuration file once, and repeatedly construct the InputStream, avoiding the problem of reading the configuration file every time you construct the InputStream.

2. Second optimization:

Maybe you can think of a better way, such as:

Why don't we make private static Element root = null; As a class attribute, cache it, so that you do not need to open and close the configuration file repeatedly, as follows:


public class LocalLoader implements ConfigLoader {
  private static final Logger logger = LoggerFactory.getLogger("LocalLoader");
  // ... ..  
  private static Element root = null;
  //Then the loadRoot method is changed to:
  private static Element loadRoot() {
    InputStream dtd = null;
    InputStream xml = null;
//    Element root = null;
    if(root == null){
      try {
        dtd = ConfigFactory.class.getResourceAsStream("/mycat.dtd");
        xml = ConfigFactory.class.getResourceAsStream("/mycat.xml");
        root = ConfigUtil.getDocument(dtd, xml).getDocumentElement();
      } catch (ConfigException e) {
        throw e;
      } catch (Exception e) {
        throw new ConfigException(e);
      } finally {
        if (dtd != null) {
          try {
            dtd.close();
          } catch (IOException e) { }
        }
        if (xml != null) {
          try {
            xml.close();
          } catch (IOException e) { }
        }
      }
    }
    return root;
  } 

This eliminates the need and the need to repeatedly open and close configuration files. As long as the root attribute is not reclaimed, the Document object introduced by root is also in the cache. This is obviously much better than the first optimization, which involves repeatedly constructing the InputStream from byte[] and then repeatedly building the Document object.

3. Third optimization

Above, private static Element root = null; Cache as a property to avoid repeated reads. So why don't we just cache the Document object as a property. It also has better semantics and the code is easier to understand. The code is as follows:


public class LocalLoader implements ConfigLoader {
  private static final Logger logger = LoggerFactory.getLogger("LocalLoader");
  // ... ...
  //In order to avoid frequent loadRoot calls in the original code to frequently read /mycat.dtd and /mycat.xml, the Document is cached.
  private static Document document = null;
  private static Element loadRoot() {
    InputStream dtd = null;
    InputStream xml = null;    
    if(document == null){
      try {
        dtd = ConfigFactory.class.getResourceAsStream("/mycat.dtd");
        xml = ConfigFactory.class.getResourceAsStream("/mycat.xml");
        document = ConfigUtil.getDocument(dtd, xml);
        return document.getDocumentElement();
      } catch (Exception e) {
        logger.error(" loadRoot error: " + e.getMessage());
        throw new ConfigException(e);
      } finally {
        if (dtd != null) {
          try { dtd.close(); } catch (IOException e) { }
        }
        if (xml != null) {
          try { xml.close(); } catch (IOException e) { }
        }
      }
    }    
    return document.getDocumentElement();
  } 

This is a more qualified implementation. Anyway, the first optimization, you learned the methods ByteArrayOutputStream and ByteArrayInputStream used with byte[].

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - line -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

Refer to the article: the original (link: http://blog.csdn.net/it_magician/article/details/9240727) as follows:

Sometimes we need to use the same InputStream object multiple times. For example, the client gets the data from the server and gets the Stream object using HttpURLConnection's getInputStream() method, both to display the data in the foreground (first read) and to write the data to the file cache locally (second read).

However, after the first reading of the InputStream object, the second reading may have reached the end of the Stream (EOFException) or the Stream has been closed.

The InputStream object itself cannot be copied because it does not implement the Cloneable interface. At this point, you can first convert the InputStream to the ByteArrayOutputStream, and then when you want to use the InputStream object, you can convert it back from the ByteArrayOutputStream. The code is as follows:


InputStream input = httpconn.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = input.read(buffer)) > -1 ) {
  baos.write(buffer, 0, len);
}
baos.flush();       
InputStream stream1 = new ByteArrayInputStream(baos.toByteArray());
//TODO: displays to the front desk
InputStream stream2 = new ByteArrayInputStream(baos.toByteArray());
//TODO: The local cache  

The use of the ByteArrayInputStream and ByteArrayOutputStream classes in Java

ByteArrayInputStream and ByteArrayOutputStream, used to read and write the contents of byte arrays as IO streams, support functions like in-memory virtual files or memory-mapped files

Example:


import java.io.*; 
public class ByteArrayStreamTest { 
  public static void main(String [] args) { 
    String str = "abcdef"; 
    ByteArrayInputStream in = new ByteArrayInputStream(str.getBytes()); 
    ByteArrayOutputStream out = new ByteArrayOutputStream(); 
    transform(in, out); 
    byte[] result = out.toByteArray(); 
    System.out.println(out); 
    System.out.println(new String(result)); 
    transform(System.in, System.out); //Read from the keyboard and output to the monitor
  } 
  public static void transform(InputStream in, OutputStream out) { 
    int ch = 0; 
    try { 
      while ((ch = in.read()) != -1) { 
        int upperChar = Character.toUpperCase((char)ch); 
        out.write(upperChar); 
      } // close while 
    } catch (Except

Related articles: