Java는 ByteArrayOutputStream 및 ByteArrayInputStream을 사용하여 구성 파일을 반복적으로 읽지 않도록 합니다.
ByteArray OutputStream: 메모리 버퍼의 데이터를 포착하여 바이트 그룹으로 변환할 수 있습니다.
ByteArrayInputStream: 바이트 그룹을 입력 흐름으로 변환
ByteArray InputStream 클래스에는 다음과 같은 두 가지 기본 구조 함수가 있습니다.
Byte Array Input Stream(byte[]b): 한 바이트 그룹의 모든 데이터를 데이터 원본으로 사용합니다. 프로그램은 입력 흐름 방식처럼 바이트를 읽을 수 있습니다. 가상 파일로 볼 수 있고 파일로 그 안의 데이터를 읽을 수 있습니다.
Byte Array Input Stream(byte[]b, int offset, int length): 수조 중의 offset부터 시작해서 이 바이트를 데이터 원본으로 계속 꺼냅니다.
ByteArrayOutputStream 클래스에도 두 가지 기본 구조 함수가 있습니다.
ByteArrayOutputStream(): 32바이트 버퍼 만들기
ByteArrayOutputStream(int): 매개 변수에 따라 크기를 지정하여 버퍼 만들기
최근에 github의 개원 프로젝트인 Mycat에 참여했습니다. 이것은 mysql의 라이브러리 테이블의 중간부품입니다.그 중에서 프로필을 읽는 코드를 발견하면 여러 번 반복해서 열고, 읽고, 닫는 문제가 빈번하게 존재한다. 코드는 매우 초보적으로 쓰여 있다. 일부 프레임워크 원본을 조금 본 사람은 이런 오류를 범하지 않을 것이다.그래서 그것에 대해 약간의 최적화를 진행하였다.
이전 코드를 최적화하려면 다음과 같이 하십시오.
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;
}
그리고 다른 방법으로 loadRoot()을 자주 호출합니다.
@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;
}
// ... ...
ConfigUtil.getDocument(dtd, xml) 방법은 다음과 같습니다.
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);
}
분명히 이것은 좋은 처리 방식이 아니다.구성 파일이 여러 번 반복됩니다.1. 1차 최적화:
왜 한 번 읽지 않고 캐시합니까?그리고 다른 방법은loadRoot () 를 호출할 때 캐시에 있는 것을 직접 사용하면 됩니다.그러나 한 가지 문제에 부딪히면 InputStream은 캐시된 다음에 다시 읽을 수 없습니다. 왜냐하면 InputStream이 일단 읽히면pos 바늘 등이 바뀌어 중복 읽을 수 없기 때문입니다.따라서 프로필의 내용을 읽기 처리하고byte[]에 넣고 캐시한 다음ByteArrayOutputStream에 맞추면byte[] 캐시의 내용을 반복해서 읽을 수 있습니다.그리고 ByteArray OutputStream을 이용하여 InputStream을 구성하면 프로필을 한 번 읽고 InputStream을 다시 구성하여 다음과 같은 코드를 반복합니다.
// loadRoot /mycat.dtd /mycat.xml, ,
// , LocalLoader , 。
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 방법:
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 최적화 이후:
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;
}
이렇게 최적화된 후에 loadRoot () 방법을 자주 호출해도 프로필을 반복적으로 읽지 않고 byte [] 내용을 사용하여 InputStream을 재구성할 뿐입니다.사실 그 원리는byte[]를 중간 용기로 이용하여byte를 캐시하는 것이다. Byte Array Output Stream은 Input Stream이 읽는 byte를 byte[] 용기와 같이 저장한 다음에 Byte Array Input Stream을 이용하여byte[] 용기에서 내용을 읽고 Input Stream을 구축한다. 이 캐시 용기가 존재하기만 하면 여러 번 반복해서 Input Stream을 구성할 수 있다.그래서 프로필을 한 번 읽고 InputStream을 반복적으로 구성하여 매번 InputStream을 구성할 때마다 프로필을 한 번 읽는 문제를 피했다.
2. 2차 최적화:
아마도 당신은 더 좋은 방법을 생각할 것이다. 예를 들면:
왜 우리는 private static Element root =null를 사용하지 않습니까?클래스 속성으로서 캐시를 하면 구성 파일을 다시 열고 닫을 필요가 없습니다. 다음과 같이 수정합니다.
public class LocalLoader implements ConfigLoader {
private static final Logger logger = LoggerFactory.getLogger("LocalLoader");
// ... ..
private static Element root = null;
// loadRoot :
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;
}
이렇게 하면 필요 없이 프로필을 열고 닫는 것을 반복하지 않을 것이다.루트 속성이 회수되지 않으면 루트가 도입한 Document 대상도 캐시에 있습니다.이것은 분명히 첫 번째 최적화보다 훨씬 낫다. 왜냐하면 첫 번째 최적화는byte[]에서 InputStream을 재구성하고 build에서 Document 대상을 재구성해야 하기 때문이다.3. 3차 최적화
위에는 private static Element root =null;반복 읽기를 피하기 위해 속성으로 캐시합니다.그러면 우리는 왜 Document 객체를 하나의 속성으로 캐시하지 않습니까?그리고 더 좋은 의미를 가지고 코드가 더 잘 이해된다.코드는 다음과 같습니다.
public class LocalLoader implements ConfigLoader {
private static final Logger logger = LoggerFactory.getLogger("LocalLoader");
// ... ...
// loadRoot /mycat.dtd /mycat.xml, Document ,
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();
}
이렇게 해야만 비교적 합격된 실현이다.Anyway, 첫 번째 최적화는 ByteArray Output Stream과 ByteArray Input Stream과byte[]를 결합하여 사용하는 방법을 배웠다.분할선
참고문:http://blog.csdn.net/it_magician/article/details/9240727
때때로 우리는 같은 InputStream 객체에 대해 여러 번 사용해야 합니다.예를 들어 클라이언트가 서버에서 데이터를 얻고 HttpURLConnection의 get Input Stream () 방법을 이용하여 Stream 대상을 얻는다. 이때 데이터를 프론트 데스크톱에 표시해야 할 뿐만 아니라 파일을 로컬로 캐시하려고 한다.
그러나 InputStream 객체를 처음 읽은 후 두 번째 다시 읽을 때 Stream의 끝에 도달했거나 Stream이 닫혔을 수 있습니다.
InputStream 객체 자체는 Cloneable 인터페이스를 구현하지 못했기 때문에 복제할 수 없습니다.이때 InputStream을 Byte Array Output Stream으로 바꾸고 다음에 Input Stream 대상을 사용할 때 Byte Array Output Stream에서 바꾸면 됩니다.코드는 다음과 같습니다.
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:
InputStream stream2 = new ByteArrayInputStream(baos.toByteArray());
//TODO:
java에서 ByteArray InputStream 및 ByteArray OutputStream 클래스 사용ByteArray InputStream 및 ByteArray OutputStream은 바이트 그룹 내용에 대한 읽기와 쓰기를 IO 흐름으로 수행하여 메모리 가상 파일이나 메모리 맵 파일과 같은 기능을 지원합니다.
인스턴스:
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); // ,
}
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