Stack 리팩토링 - menuPath
1. 기존 스택의 문제점
//기존 menuGroup에서 Stack이 사용되는 소스코드
public MenuGroup(String title) {
super(title);
this.menuPath = new Stack<>();
}
public void setParent(MenuGroup parent) {
this.parent = parent;
this.menuPath = parent.menuPath;
}
private String getMenuPathTitle(Stack<String> menuPath) {
StringBuilder strBuilder = new StringBuilder();
for (String s : menuPath) {
if (!strBuilder.isEmpty()) {
strBuilder.append("/");
}
strBuilder.append(s);
}
return strBuilder.toString();
}
기존 소스코드에서 Stack은 menuGroup이 인스턴스 될 때마다 새로운 Stack을 생성한다.
부모의 menuGroup이 있다면 인스턴스된 Stack은 Garbage가 된다.
menuPath를 호출하는 메서드는 Stack의 구조로 꺼내는 것이 아니라 List타입으로 탐색을 한다.
2. 스택 구조 변경
- 현재 위치의 menuTitle을 push한다.
- 부모가 있다면 부모의 menuPath로 이동한다.
- root의 부모인 null를 만날때 까지 1~2를 반복한다.
public MenuGroup(String title) {
super(title);
}
public void setParent(MenuGroup parent) {
this.parent = parent;
}
private String getMenuPath() {
Stack<String> menuPath = new Stack<>();
MenuGroup menuGroup = this;
while (menuGroup != null) {
menuPath.push(menuGroup.title);
menuGroup = menuGroup.parent;
}
StringBuilder strBuilder = new StringBuilder();
while (!menuPath.empty()) {
if (!strBuilder.isEmpty()){
strBuilder.append("/");
}
strBuilder.append(menuPath.pop());
}
return strBuilder.toString();
}
파일 입출력 FILE I/O
1. 바이너리 데이터 입출력
- 바이너리 타입의 입력은 텍스트 타입 보다 메모리를 적게사용하고, 속도가 빠르다.
- 타입별로 인코딩하는 방식이 다르다.
- "abc" --> UTF-8 문자코드 바이트
- 20 --> 2의 보수
- 3.14 --> IEEE-754 규칙
- true --> 2의 보수
- 설정 방법이 복잡하다.
- PDF, PPT, DOC, GIF, JPEG, MP3, AVI 등이 있다.
2. 데이터 입출력 적용
1) 데이터 입출력 흐름
2) byte[] 만들기
- 객체를 byete[] 배열에 1바이트씩 담아야한다.
- ByteArrayOutputStream 클래스를 이용하여 바이트배열 담을 저장소를 만든다.
- 이후 toByteArray()메서드로 통해 바이트 배열을 리턴 할 수 있다.
public byte[] getBytes() throws IOException {
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
//객체의 정보들을 byte로 담는다.
return out.toByteArray();
}
}
3) write(int) 메소드
- 객체를 byete[] 배열에 1바이트씩 담을때 write 메서드를 사용한다.
- write(int) int 타입을 받지만 1바이트만 읽고 저장할 수 있다.
- 비트 연산자를 통해 바이트를 1바이트까지 이동 해야한다.
ByteArrayOutputStream out = new ByteArrayOutputStream()
out.write(no >> 24);
out.write(no >> 16);
out.write(no >> 8);
out.write(no);
4) 데이터 구조
- 데이터의 구조에 따라 바이트의 할당규칙을 정해야 load과정에서 해당규칙을 통해 값을 읽을 수 있다.
//유저정보를 바이트배열로 변환
public byte[] getBytes() throws IOException {
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
//회원번호 넣기
out.write(no >> 24);
out.write(no >> 16);
out.write(no >> 8);
out.write(no);
//이름 넣기
byte[] bytes = name.getBytes(StandardCharsets.UTF_8);
out.write(bytes.length >> 8);
out.write(bytes.length);
out.write(bytes);
//이메일 넣기
bytes = email.getBytes(StandardCharsets.UTF_8);
out.write(bytes.length >> 8);
out.write(bytes.length);
out.write(bytes);
//password
bytes = password.getBytes(StandardCharsets.UTF_8);
out.write(bytes.length >> 8);
out.write(bytes.length);
out.write(bytes);
//tel
bytes = tel.getBytes(StandardCharsets.UTF_8);
out.write(bytes.length >> 8);
out.write(bytes.length);
out.write(bytes);
return out.toByteArray();
}
}
5) 파일로 출력
- 데이터의 구조에 따라 바이트배열들을 병합하고 객체의 전체 개수를 구한다 .
- FileOutputStream에 바이트의 배열들 개수를 넣고 저장한 바이트 배열들을 넣는다.
//바이트배열로 변환된 유저정보를 파일로 출력
private void saveUser() {
try (FileOutputStream out = new FileOutputStream("user.data")) {
int userLength = userList.size();
out.write(userLength >> 8);
out.write(userLength);
for (User user : userList) {
byte[] bytes = user.getBytes();
out.write(bytes.length >> 8);
out.write(bytes.length);
out.write(bytes);
}
} catch (IOException e) {
System.out.println("회원 정보 저장 중 오류 발생" + e.getMessage());
}
}
6) 파일로 입력
- 입력은 출력과정의 역순으로 진행한다.
//파일을 테이터 배열로 전환
private void loadUser() {
try (FileInputStream in = new FileInputStream("user.data")) {
int userLength = in.read() << 8 | in.read();
int maxUserNum = 0;
for (int i = 0; i < userLength; i++) {
int len = (in.read() << 8) | in.read();
byte[] bytes = new byte[len];
in.read(bytes);
User user = User.valueOf(bytes);
userList.add(user);
maxUserNum = Math.max(maxUserNum, user.getNo());
}
User.initSeqNo(maxUserNum);
} catch (IOException e) {
System.out.println("회원 정보 로딩 중 오류 발생" + e.getMessage());
}
}
// 데이터 배열을 유저 객체로 전환
public static User valueOf(byte[] bytes) throws IOException {
try (ByteArrayInputStream in = new ByteArrayInputStream(bytes)) {
User user = new User();
user.setNo(in.read() << 24 | in.read() << 16 | in.read() << 8 | in.read());
byte[] buffer = new byte[10000];
int len = in.read() << 8 | in.read();
in.read(buffer, 0, len);
user.setName(new String(buffer, 0, len, StandardCharsets.UTF_8));
len = in.read() << 8 | in.read();
in.read(buffer, 0, len);
user.setEmail(new String(buffer, 0, len, StandardCharsets.UTF_8));
len = in.read() << 8 | in.read();
in.read(buffer, 0, len);
user.setPassword(new String(buffer, 0, len, StandardCharsets.UTF_8));
len = in.read() << 8 | in.read();
in.read(buffer, 0, len);
user.setTel(new String(buffer, 0, len, StandardCharsets.UTF_8));
return user;
}
}