//연결 리스트의 각 항목의 값을 저장하는 일을 할 클래스이다.
public class Node {
Object value;
Node prev;
Node next;
public Node(){}
public Node(Object v) {
this.value = v;
}
}
// Node를 이용해 값을 목록을 관리하는 일을 한다.
public class LinkedList {
private Node head;
private Node tail;
private int size;
public void append(Object value) {
Node node = new Node(value);
size++;
if (tail == null) {
head = tail = node;
return;
}
tail.next = node;
node.prev = tail;
tail = node;
}
public Object retrieve(int index) {
if (index < 0 || index >= size) {
throw new ListException("인덱스의 범위를 초과했습니다!");
}
Node cursor = head;
for (int i = 0; i < index; i++) {
cursor = cursor.next;
}
return cursor.value;
}
public Object delete(int index) {
if (index < 0 || index >= size) {
throw new ListException("인덱스의 범위를 초과했습니다!");
}
size--;
Object deleted;
if (head == tail) {
deleted = head.value;
head.value = null;
head = tail = null;
return deleted;
}
Node cursor = head;
for (int i = 0; i < index; i++) {
cursor = cursor.next;
}
if (cursor.prev != null) {
cursor.prev.next = cursor.next;
} else {
head = cursor.next;
head.prev = null;
}
if (cursor.next != null) {
cursor.next.prev = cursor.prev;
} else {
tail = cursor.prev;
tail.next = null;
}
deleted = cursor.value;
cursor.value = null;
cursor.prev = null;
cursor.next = null;
return deleted;
}
public int length() {
return size;
}
public Object[] getArray() {
Object[] arr = new Object[size];
Node cursor = head;
for (int i = 0; i < size; i++) {
arr[i] = cursor.value;
cursor = cursor.next;
}
return arr;
}
}
private Node head; // 첫 노드의 주소를 저장
private Node tail; // 마지막 노드의주소를 저장
private int size; // 저장된 데이터의 개수
//파라미터로 주어진 값을 노드에 담아 리스트 끝에 연결한다.
public void append(Object value) {
// Node 생성 후 값을 저장한다.
Node node = new Node(value);
size++; // 목록의 크기를 한 개 증가시킨다.
// 리스트의 끝에 노드를 붙인다.
// 만약, 리스트에 노드가 없다면
if (tail == null) {
head = tail = node; // 첫 노드를 등록한다.
return;
}
tail.next = node; // 리스트 끝에 새 노드를 연결한다.
node.prev = tail; // 새 노드가 현재의 끝 노드를 가리키게 한다.
tail = node; // 새 노드를 끝 노드로 만든다.
}
public Object retrieve(int index) {
// 인덱스의 유효 여부 검사
if (index < 0 || index >= size) {
throw new ListException("인덱스의 범위를 초과했습니다!");
}
// 인덱스에 해당하는 노드를 찾을 때 head 부터 시작한다.
Node cursor = head;
// 지정된 인덱스의 노드 주소를 알아낸다.
for (int i = 0; i < index; i++) {
cursor = cursor.next;
}
// cursor가 가리키는 노드의 값을 꺼내 리턴한다.
return cursor.value;
}
public Object delete(int index) {
// 인덱스의 유효 여부 검사
if (index < 0 || index >= size) {
throw new ListException("인덱스의 범위를 초과했습니다!");
}
// 목록 크기를 한 개 줄인다.
size--;
// 삭제할 값을 임시 보관하여 메서드를 리턴할 때 호출자에게 전달한다.
Object deleted;
if (head == tail) { // 마지막 남은 노드를 제거할 때
deleted = head.value; // 노드를 삭제하기 전에 리턴할 수 있도록 값을 임시 보관한다.
head.value = null; // 노드에 들어 있는 값 객체의 주소를 비운다.
head = tail = null;
return deleted; // 메서드를 종료할 때 호출자에게 삭제한 값을 리턴한다.
}
// 삭제할 노드를 찾기 위해 시작 노드를 head로 설정한다.
Node cursor = head;
// 지정된 인덱스의 노드 주소를 알아낸다.
for (int i = 0; i < index; i++) {
cursor = cursor.next;
}
// 찾은 노드의 앞, 뒤 노드를 바로 연결한다.
if (cursor.prev != null) { // 맨 앞 노드가 아니라면
cursor.prev.next = cursor.next; // 현재 노드의 다음 노드 주소를 이전 노드의 next 저장
} else { // 맨 앞 노드라면
head = cursor.next; // 삭제할 다음 노드를 시작 노드로 설정한다.
head.prev = null; // 시작 노드이기에 앞노드를 가리키지 않게 한다.
}
if (cursor.next != null) { // 마지막 노드가 아니라면
cursor.next.prev = cursor.prev; // 현재 노드의 이전 노드 주소를 다음 노드의 prev 저장
} else { // 마지막 노드라면
tail = cursor.prev; // 현재 커서의 이전 노드를 마지막 노드로 설정한다.
tail.next = null; // 마지막 노드이기에 다음 노드를 가리키지 않게 한다.
}
// 삭제할 노드를 초기화시킨다.
// => garbage 객체가 다른 garbage 객체를 참조하지 않게 한다.
deleted = cursor.value; // 노드를 삭제하기 전에 노드에 들어 있는 값을 임시 보관해 둔다.
cursor.value = null;
cursor.prev = null;
cursor.next = null;
return deleted; // 메서드를 리턴할 때 삭제된 값을 호출자에게 전달한다.
}
public int length() {
return size;
}
public Object[] getArray() {
// 값을 담을 배열을 준비
Object[] arr = new Object[size];
// 노드를 따라 가면서 값을 꺼내 배열에 담는다.
Node cursor = head;
for (int i = 0; i < size; i++) {
arr[i] = cursor.value;
cursor = cursor.next;
}
return arr;
}
public class LinkedListTest {
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add("홍길동");
list.add("임꺽정");
list.add("유관순");
list.add("안중근");
System.out.println(list.get(0));
System.out.println(list.get(1));
System.out.println(list.get(2));
System.out.println(list.get(3));
// System.out.println(list.get(-1));
// System.out.println(list.get(4));
//중간 노드 삭제 테스트.
list.remove(2); // 유관순 삭제.
printList(list);
// 맨 앞 노드 삭제 테스트.
list.remove(0); // 홍길동 삭제
printList(list);
// 맨 마지막 노드 삭제 테스트.
list.remove(1); // 안중근 삭제.
printList(list);
}
static void printList(LinkedList list) {
System.out.println("---------------------------------------------------");
for(int i=0;i<list.size();i++) {
System.out.println(list.get(i));
}
}
}
import com.bitcamp.board.domain.Board;
import com.bitcamp.util.LinkedList;
// 게시글 목록을 관리하는 역할
//
public class BoardList extends LinkedList {
private int boardNo = 0;
@Override
public void append(Object e) {
Board board = (Board) e;
board.no = nextNo();
super.append(e);
}
@Override
public Board retrieve(int boardNo) {
for (int i = 0; i < length(); i++) {
Board board = (Board) super.retrieve(i);
if (board.no == boardNo) {
return board;
}
}
return null;
}
@Override
public Object delete(int boardNo) {
for (int i = 0; i < length(); i++) {
Board board = (Board) super.retrieve(i);
if (board.no == boardNo) {
return super.delete(i);
}
}
return null;
}
private int nextNo() {
return ++boardNo;
}
}
import java.text.SimpleDateFormat;
import java.util.Date;
import com.bitcamp.board.dao.BoardList;
import com.bitcamp.board.domain.Board;
import com.bitcamp.util.Prompt;
public class BoardHandler {
private String title; // 게시판의 제목
// 게시글 목록을 관리할 객체 준비
private BoardList boardList = new BoardList();
public BoardHandler() {
this.title = "게시판";
}
public BoardHandler(String title) {
this.title = title;
}
public void execute() {
while (true) {
System.out.printf("%s:\n", this.title);
System.out.println(" 1: 목록");
System.out.println(" 2: 상세보기");
System.out.println(" 3: 등록");
System.out.println(" 4: 삭제");
System.out.println(" 5: 변경");
System.out.println();
try {
int menuNo = Prompt.inputInt("메뉴를 선택하세요[1..5](0: 이전) ");
displayHeadline();
// 다른 인스턴스 메서드를 호출할 때 this에 보관된 인스턴스 주소를 사용한다.
switch (menuNo) {
case 0: return;
case 1: this.onList(); break;
case 2: this.onDetail(); break;
case 3: this.onInput(); break;
case 4: this.onDelete(); break;
case 5: this.onUpdate(); break;
default: System.out.println("메뉴 번호가 옳지 않습니다!");
}
displayBlankLine();
} catch (Exception ex) {
System.out.printf("예외 발생: %s\n", ex.getMessage());
}
} // 게시판 while
}
private static void displayHeadline() {
System.out.println("=========================================");
}
private static void displayBlankLine() {
System.out.println(); // 메뉴를 처리한 후 빈 줄 출력
}
private void onList() {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
System.out.printf("[%s 목록]\n", this.title);
System.out.println("번호 제목 조회수 작성자 등록일");
// boardList 인스턴스에 들어 있는 데이터 목록을 가져온다.
Object[] list = this.boardList.getArray();
for (Object item : list) {
Board board = (Board) item;
Date date = new Date(board.createdDate);
String dateStr = formatter.format(date);
System.out.printf("%d\t%s\t%d\t%s\t%s\n",
board.no, board.title, board.viewCount, board.writer, dateStr);
}
}
private void onDetail() {
System.out.printf("[%s 상세보기]\n", this.title);
int boardNo = 0;
while (true) {
try {
boardNo = Prompt.inputInt("조회할 게시글 번호? ");
break;
} catch (Exception ex) {
System.out.println("입력 값이 옳지 않습니다!");
}
}
// 해당 번호의 게시글이 몇 번 배열에 들어 있는지 알아내기
Board board = this.boardList.retrieve(boardNo);
// 사용자가 입력한 번호에 해당하는 게시글을 못 찾았다면
if (board == null) {
System.out.println("해당 번호의 게시글이 없습니다!");
return;
}
System.out.printf("번호: %d\n", board.no);
System.out.printf("제목: %s\n", board.title);
System.out.printf("내용: %s\n", board.content);
System.out.printf("조회수: %d\n", board.viewCount);
System.out.printf("작성자: %s\n", board.writer);
Date date = new Date(board.createdDate);
System.out.printf("등록일: %tY-%1$tm-%1$td %1$tH:%1$tM\n", date);
}
private void onInput() {
System.out.printf("[%s 등록]\n", this.title);
Board board = new Board();
board.title = Prompt.inputString("제목? ");
board.content = Prompt.inputString("내용? ");
board.writer = Prompt.inputString("작성자? ");
board.password = Prompt.inputString("암호? ");
board.viewCount = 0;
board.createdDate = System.currentTimeMillis();
this.boardList.append(board);
System.out.println("게시글을 등록했습니다.");
}
private void onDelete() {
System.out.printf("[%s 삭제]\n", this.title);
int boardNo = 0;
while (true) {
try {
boardNo = Prompt.inputInt("삭제할 게시글 번호? ");
break;
} catch (Exception ex) {
System.out.println("입력 값이 옳지 않습니다!");
}
}
if (boardList.delete(boardNo) != null) {
System.out.println("삭제하였습니다.");
} else {
System.out.println("해당 번호의 게시글이 없습니다!");
}
}
private void onUpdate() {
System.out.printf("[%s 변경]\n", this.title);
int boardNo = 0;
while (true) {
try {
boardNo = Prompt.inputInt("변경할 게시글 번호? ");
break;
} catch (Throwable ex) {
System.out.println("입력 값이 옳지 않습니다!");
}
}
Board board = this.boardList.retrieve(boardNo);
if (board == null) {
System.out.println("해당 번호의 게시글이 없습니다!");
return;
}
String newTitle = Prompt.inputString("제목?(" + board.title + ") ");
String newContent = Prompt.inputString(String.format("내용?(%s) ", board.content));
String input = Prompt.inputString("변경하시겠습니까?(y/n) ");
if (input.equals("y")) {
board.title = newTitle;
board.content = newContent;
System.out.println("변경했습니다.");
} else {
System.out.println("변경 취소했습니다.");
}
}
}
단순 인스턴스 목록을 관리하는 관점에서 게시글 데이터를 다루는 관점으로 변경시켰기 때문에 데이터 관점에서 이름을 변경해준다.
// 게시글 목록을 관리하는 역할
//
public class BoardDao {
ObjectList list = new ObjectList();
private int boardNo = 0;
// BoardDao 에서 제공할 메서드를 정의하고,
// 이 메서드가 호출되면 ObjectList의 도움을 받아 처리한다.
public void insert(Object e) {
// 게시글 객체를 적절하게 준비한 다음
Board board = (Board) e;
board.no = nextNo();
// 의존 객체 ObjectList를 사용하여 목록에 추가한다.
list.add(e);
}
public Board findByNo(int boardNo) {
// 의존 객체 BoardList를 이용하여 기존에 저장된 게시글 목록 중에
// 해당 번호의 게시글을 찾는다.
for (int i = 0; i < list.size(); i++) {
Board board = (Board) list.get(i);
if (board.no == boardNo) {
return board;
}
}
return null;
}
public boolean delete(int boardNo) {
// 의존 객체 ObjectList을 이용하여 목록에 저장된 게시글을 찾아 삭제한다.
for (int i = 0; i < list.size(); i++) {
Board board = (Board) list.get(i);
if (board.no == boardNo) {
return list.remove(i);
}
}
return false;
}
public Board[] findAll() {
// 의존 객체 ObjectList를 이용하여 목록에 저장된 게시글을 가져온다.
Object[] arr = list.toArray();
// Object[] 배열에 담긴 인스턴스 목록을 Board[] 배열에 담아 리턴한다.
Board[] boards = new Board[arr.length];
for (int i = 0; i < arr.length; i++) {
boards[i] = (Board) arr[i];
}
return boards;
}
private int nextNo() {
return ++boardNo;
}
}
public class MemberDao {
// MemberDao 가 사용할 의존 객체를 선언한다.
ObjectList list = new ObjectList();
// ObjectList를 상속 받지 않기 때문에
// 목록에 데이터를 추가하고 싶다면
// MemberDao 클래스에 해당 메서드를 직접 정의해야 한다.
// 물론, 실제 작업은 ObjectList 가 할 것이다.
//
public void insert(Member member) {
list.add(member);
}
// MemberList 에서 MemberDao 로 바꿔는 것에 맞춰
// 메서드의 이름도 데이터에 초점을 맞춰 변경한다.
//
public Member findByEmail(String email) {
for (int i = 0; i < list.size(); i++) {
Member member = (Member) list.get(i);
if (member.email.equals(email)) {
return member;
}
}
return null;
}
public boolean delete(String email) {
for (int i = 0; i < list.size(); i++) {
Member member = (Member) list.get(i);
if (member.email.equals(email)) {
return list.remove(i);
}
}
return false;
}
public Member[] findAll() {
//목록에 저장된 회원 데이터를 가져온다.
Object arr[] = list.toArray();
//Object배열의 값을 Member배열로 옮긴다.
Member [] members = new Member[arr.length];
for(int i=0; i < arr.length; i++) {
members[i] = (Member)arr[i];
}
return members;
}
}
public class BoardHandler {
private String title; // 게시판의 제목
// 게시글 목록을 관리할 객체 준비
private BoardDao boardDao = new BoardDao();
public BoardHandler() {
this.title = "게시판";
}
public BoardHandler(String title) {
this.title = title;
}
public void execute() {
while (true) {
System.out.printf("%s:\n", this.title);
System.out.println(" 1: 목록");
System.out.println(" 2: 상세보기");
System.out.println(" 3: 등록");
System.out.println(" 4: 삭제");
System.out.println(" 5: 변경");
System.out.println();
try {
int menuNo = Prompt.inputInt("메뉴를 선택하세요[1..5](0: 이전) ");
displayHeadline();
// 다른 인스턴스 메서드를 호출할 때 this에 보관된 인스턴스 주소를 사용한다.
switch (menuNo) {
case 0: return;
case 1: this.onList(); break;
case 2: this.onDetail(); break;
case 3: this.onInput(); break;
case 4: this.onDelete(); break;
case 5: this.onUpdate(); break;
default: System.out.println("메뉴 번호가 옳지 않습니다!");
}
displayBlankLine();
} catch (Exception ex) {
System.out.printf("예외 발생: %s\n", ex.getMessage());
}
} // 게시판 while
}
private static void displayHeadline() {
System.out.println("=========================================");
}
private static void displayBlankLine() {
System.out.println(); // 메뉴를 처리한 후 빈 줄 출력
}
private void onList() {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
System.out.printf("[%s 목록]\n", this.title);
System.out.println("번호 제목 조회수 작성자 등록일");
// boardList 인스턴스에 들어 있는 데이터 목록을 가져온다.
Board[] boards = this.boardDao.findAll();
for (Board board : boards) {
Date date = new Date(board.createdDate);
String dateStr = formatter.format(date);
System.out.printf("%d\t%s\t%d\t%s\t%s\n",
board.no, board.title, board.viewCount, board.writer, dateStr);
}
}
private void onDetail() {
System.out.printf("[%s 상세보기]\n", this.title);
int boardNo = 0;
while (true) {
try {
boardNo = Prompt.inputInt("조회할 게시글 번호? ");
break;
} catch (Exception ex) {
System.out.println("입력 값이 옳지 않습니다!");
}
}
// 해당 번호의 게시글이 몇 번 배열에 들어 있는지 알아내기
Board board = this.boardDao.findByNo(boardNo);
// 사용자가 입력한 번호에 해당하는 게시글을 못 찾았다면
if (board == null) {
System.out.println("해당 번호의 게시글이 없습니다!");
return;
}
System.out.printf("번호: %d\n", board.no);
System.out.printf("제목: %s\n", board.title);
System.out.printf("내용: %s\n", board.content);
System.out.printf("조회수: %d\n", board.viewCount);
System.out.printf("작성자: %s\n", board.writer);
Date date = new Date(board.createdDate);
System.out.printf("등록일: %tY-%1$tm-%1$td %1$tH:%1$tM\n", date);
}
private void onInput() {
System.out.printf("[%s 등록]\n", this.title);
Board board = new Board();
board.title = Prompt.inputString("제목? ");
board.content = Prompt.inputString("내용? ");
board.writer = Prompt.inputString("작성자? ");
board.password = Prompt.inputString("암호? ");
board.viewCount = 0;
board.createdDate = System.currentTimeMillis();
this.boardDao.insert(board);
System.out.println("게시글을 등록했습니다.");
}
private void onDelete() {
System.out.printf("[%s 삭제]\n", this.title);
int boardNo = 0;
while (true) {
try {
boardNo = Prompt.inputInt("삭제할 게시글 번호? ");
break;
} catch (Exception ex) {
System.out.println("입력 값이 옳지 않습니다!");
}
}
if (boardDao.delete(boardNo)) {
System.out.println("삭제하였습니다.");
} else {
System.out.println("해당 번호의 게시글이 없습니다!");
}
}
private void onUpdate() {
System.out.printf("[%s 변경]\n", this.title);
int boardNo = 0;
while (true) {
try {
boardNo = Prompt.inputInt("변경할 게시글 번호? ");
break;
} catch (Throwable ex) {
System.out.println("입력 값이 옳지 않습니다!");
}
}
Board board = this.boardDao.findByNo(boardNo);
if (board == null) {
System.out.println("해당 번호의 게시글이 없습니다!");
return;
}
String newTitle = Prompt.inputString("제목?(" + board.title + ") ");
String newContent = Prompt.inputString(String.format("내용?(%s) ", board.content));
String input = Prompt.inputString("변경하시겠습니까?(y/n) ");
if (input.equals("y")) {
board.title = newTitle;
board.content = newContent;
System.out.println("변경했습니다.");
} else {
System.out.println("변경 취소했습니다.");
}
}
}