2020년 10월 26일
업데이트:
컬렉션(Collection)
널리 알려져있는 자료구조(Data Structure)를 바탕으로 객체들을 효율적으로 추가, 삭제, 검색
할 수 있도록 java.util
패키지에 컬렉션과 관련된 인터페이스와 클래스들을 포함하는 프레임워크이다.
자료구조
데이터(자료)를 메모리에서 구조적으로 처리하는 방법론으로써 자바에서는 이미 자체적으로 많은 자료구조를 지원한다.
컬렉션을 사용하는 이유
배열의 문제점
- 한 번 크기를 지정하면 변경할 수 없다.
- 공간 크기가 부족하면 에러가 발생 -> 할당 시 넉넉한 크기로 할당하게 됨(메모리 낭비)
- 필요에 따라 공간을 늘리거나 줄일 수 없음
- 배열에 기록된 데이터에 대한 중간 위치의 추가, 삭제가 불편하다.
- 추가, 삭제할 데이터부터 마지막 기록된 데이터까지 하나씩 뒤로 밀어내고 추가해야한다.
- 한 타입의 데이터만 저장이 가능하다.
컬렉션의 장점
- 저장하는 크기의 제약이 없다.
- 추가, 삭제, 정렬 등의 기능 처리가 간단하게 해결된다.
- 자료를 구조적으로 처리하는 자료구조가 내장되어 있어 알고리즘 구현이 필요 없다.
- 여러 타입의 데이터가 저장 가능하다.
- 객체만 저장할 수 있기 때문에 필요에 따라 기본 자료형을 저장해야 하는 경우
Wrapper
클래스를 사용한다.
- 객체만 저장할 수 있기 때문에 필요에 따라 기본 자료형을 저장해야 하는 경우
주요 인터페이스
List와 Set은 객체를 추가, 삭제, 검색하는 방법에 많은 공통점이 있기 때문에 이 인터페이스들의 공통된 메소드들만 모아 Collection 인터페이스로 정의해두고 있다. 다만 Map은 키와 값을 하나의 쌍으로 묶어 관리하는 구조로 되어 있어, List와 Set과는 사용 방법이 다르다.
List
객체들을 일렬로 늘어놓은 구조를 가지며 이 객체를 인덱스로 관리한다. 동일한 객체를 중복 저장할 수 있는데, 이 경우 동일한 번지가 참조된다. 구현 클래스로 ArrayList와 Vector, LinkedList가 있다.
ArrayList
ArrayList는 List 인터페이스의 구현 클래스로, 초기 저장 용량은 10으로 자동 생성되며 따로 지정도 가능하다. 저장 용량을 초과한 객체들이 들어오면 자동으로 저장 용량이 늘어나며, 고정 또한 가능하다.
// 생성
List<E> list = new ArrayList<E>();
// 저장 용량 지정
List<E> list = new ArrayList<E>(20);
Vector
Vector 역시 List 인터페이스의 구현 클래스로 ArrayList와 동등하지만 동기화를 제공하는 점이 ArrayList와의 차이점을 가진다. List 객체들 중 가장 성능이 좋지 않으므로 잘 사용하지 않는다.
실습 코드
// Book.java
public class Book {
private String title;
private Stirng author;
private int price;
public Book() {}
public Book(String title, String author, int price){
super();
this.title = title;
this.author = author;
this.price = price;
}
public String setTitle() {
return title;
}
public String getTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "Book [title=" + title + ", author=" + author + ", price=" + price + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((author == null) ? 0 : author.hashCode());
result = prime * result + price;
result = prime * result + ((title == null) ? 0 : title.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Book other = (Book) obj;
if (author == null) {
if (other.author != null)
return false;
} else if (!author.equals(other.author))
return false;
if (price != other.price)
return false;
if (title == null) {
if (other.title != null)
return false;
} else if (!title.equals(other.title))
return false;
return true;
}
// 단축키 알트+쉬프트+S
}
// ListService.java
public class ListService {
public void example1() {
// 컬렉션 사용 시 주의사항
// - 컬렉션에 저장할 수 있는 값은 객체만 가능함
// --> 기본 자료형을 Wrapper 클래스를 이용해서 객체화 해야 함
// - 컬렉션은 모든 타입의 객체를 저장할 수 있음
// --> 반복문 또는 직접 요소에 접근하는 경우
// 타입 검사를 하지 않으면 오류가 발생할 수 있음
List list1 = new ArrayList(); // 다형성(업캐스팅)
// 부모(인터페이스) = 자식 객체
// add(E e) : 데이터 추가
list1.add(new String("Hello"));
list1.add(new Book("자바의정석", "남궁 성", 30000));
list1.add(new Integer(100));
list1.add(300);
// get(int index) : 데이터 얻어오기
System.out.println(list1.get(0).toString());
System.out.println(list1.get(1).toString());
System.out.println(list1.get(2).toString());
System.out.println(list1.get(3).toString());
// 컬렉션 사용 시 문제점(타입 검사 X)
// size() : 저장된 데이터 개수
for(int i = 0; i < list1.size(); i++){
if(list1.get(i) instanceof String) {
System.out.println( ((String) list1.get(i)).length());
} else if(list.get(i) instanceof Book) {
System.out.println(((Book)list1.get(i)).getTitle());
}
}
}
public void exampl2() {
// 제네릭(Generics) : < >
// - 클래스, 메소드, 컬렉션 내부에서 사용할 클래스 타입을 한 종류로 제한하는 기능
// - 컬렉션에 여러 데이터 타입이 저장되면 사용 시 마다 타입 검사 필요 -> 비효율적
// --> 제네릭을 이용해서 컬렉션 객체에 저장되는 데이터 타입 제한
// ---> 타입 검사 불필요
// 제네릭의 이점 : 컴파일 단계에서 강력한 타입 체크가 가능하다.
// Book 타입만 저장할 수 있는 List
List<Book> list = new ArrayList<Book>();
System.out.println("초기값 : " + list);
// 출력 - 초기값 : [] <-- null이 아니라 Empty 상태
// 1. isEmpty() : 컬렉션 객체 내부가 비어있는지 확인
System.out.println("isEmpty() : " + list.isEmpty());
// 출력 : isEmpty() : true
// 2. add(E e) E(제네릭) -> Book ---> Book 타입만 전달받을 수 있음
// --> 제네릭에 의해 Book 객체만을 전달받게 변경됨
list.add(new Book("자바의정석", "남궁 성", 30000));
list.add(new Book("어린왕자", "생택쥐베리", 8000));
list.add(new Book("삼국지", "이문열", 200000));
System.out.println("list : " + list);
System.out.println("isEmpty() : " + list.isEmpty());
// 출력 : isEmpty() : false
// 3. add(int index, Book e) : index 위치에 삽입
// 기존 해당 인덱스에 있던 인스턴스는 다음 index로 밀림
list.add(1, new Book("해리포터", "J.K.롤링", 9000));
// 향상된 for문
// - 배열 또는 컬렉션의 모든 요소의 순차적으로 접근하는 용도로 사용
for(Book book : list) {
// - list 요소를 하나씩 반복 접근하며 접근한 요소의 값을 book에 저장
System.out.println(book);
}
// 4. set(int index, Book e)
// 해당 인덱스의 요소를 새로운 요소(e)로 변경
list.set(0, new Book("자바의정석-기초편", "남궁 성", 25000));
printList(list);
// 5. size() : 컬렉션 내부에 존재하는 인스턴스 개수 반환
System.out.println("list.size() : " + list.size());
// 6. remove(int index) : 해당 인덱스에 존재하는 인스턴스를 삭제
// remove() 호출 시 삭제되는 인스턴스 반환
System.out.println("list.remove(0) : " + list.remove(0));
printList(list);
// 7. get(int index) : 해당 인덱스에 있는 인스턴스를 얻어옴
for(int i = list.size() - 1; i >= 0; i++){
System.out.println(list.get(i));
}
// 8. subList(int index1, int index2)
// 기존 List에서 index1부터 index2 미만까지 추출하며, 원본은 유지됨
List<Book> sub = list.subList(0, 2);
printList(list);
pritnList(sub);
// 9. addAll(Collection c)
list.addAll(sub);
printList(list);
// 10. indexOf(Book e)
// list 요소를 앞에서부터 검사하여 같은 객체가 있는 인덱스 반환, 없으면 -1을 반환한다. (제일 먼저 검색되는 하나만 반환)
// (같은 객체 == 동등 객체 == 값이 같은 객체 --> equals() 오버라이딩 필요)
System.out.println("어린왕자 위치 : " + list.indexOf(new Book("어린왕자", "생택쥐베리", 8000))); // 1
System.out.println("어린왕자 위치 : " + list.lastIndexOf(new Book("어린왕자", "생택쥐베리", 8000))); // 4
// 11. clear() : 모든 인스턴스 삭제
list.clear();
System.out.println(list);
System.out.println(list.isEmpty());
}
public void printList(List<Book> list) {
for(Book book : list) {
System.out.println(book);
}
}
}
주말 숙제 List 버전
package com.kh.practice.model.service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.kh.practice.model.vo.Student;
public class StudentManagementServiceImpl implements StudentManagementService {
private Student[] stdArr = { //우리반 명단 };
// 배열 -> List로 변환
private List<Student> students = new ArrayList<Student>(Arrays.asList(stdArr));
// Arrays.asList : 배열을 list로 변환함
// 현재 students 배열의 마지막 값이 저장된 인덱스값을 저장하는 변수
// private int currentIndex = students.size() -1;
// 전체 학생 정보가 담겨있는 List를 반환하는 service
@Override
public List<Student> selectAll() {
return students;
}
// students List에서 이름이 일치하는 학생 객체 반환하는 service
// 일치하는 학생이 없으면 null 반환 (동명이인은 없다고 가정)
@Override
public Student selectName(String name) {
Student std = null; // 검색 결과 저장용 변수
for (Student temp : students) {
if (temp.getName().equals(name)) {
std = temp;
break;
}
}
return std;
}
// students List에서 전달받은 gender와 성별이 일치하는 학생을 List로 반환하는 service
@Override
public List<Student> selectGender(char gender) {
List<Student> tempList = new ArrayList<Student>(3);
for (Student temp : students) {
if (temp.getGender() == gender) {
tempList.add(temp);
}
}
return tempList;
}
// students List에서 전달받은 age와 나이가 일치하는 학생을 List로 반환하는 service
// 일치하는 학생이 없으면 null 반환
@Override
public List<Student> selectAge(int age) {
List<Student> tempList = new ArrayList<Student>(3);
for (Student temp : students) {
if (temp.getAge() == age) {
tempList.add(temp);
}
}
return tempList;
}
// 전달받은 Student 객체를 List인 students에 추가
@Override
public void insertStudent(Student std) {
students.add(std);
}
// List 요소 중 name 속성 값이 전달받은 name과 일치하는 요소를 찾아 수정
@Override
public void updateStudent(String name, Student std) {
for(int i = 0; i < students.size(); i++) {
if(students.get(i).getName().equals(name)) {
students.set(i, std);
break;
}
}
}
// 전달받은 name과 일치하는 이름을 가진 학생 정보를 List에서 제거 후 제거한 학생 정보를 반환
@Override
public Student deleteStudent(String name) {
Student std = null; // 삭제되는 학생 정보를 저장할 변수 선언
for(int i = 0; i < students.size(); i++) {
if(students.get(i).getName().equals(name)) {
std = students.remove(i);
break;
}
}
return std;
}
}
package com.kh.practice.view;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import com.kh.practice.model.service.StudentManagementService;
import com.kh.practice.model.service.StudentManagementServiceImpl;
import com.kh.practice.model.vo.Student;
public class StudentManagementViewImpl extends StudentManagementView {
private Scanner sc = new Scanner(System.in);
private StudentManagementService service = new StudentManagementServiceImpl();
// 전체 학생 조회 view
@Override
public void selectAll() {
// service.selectAll() 메소드를 호출하여
// 전체 학생 정보를 List로 반환 받아 화면에 출력
List<Student> students = service.selectAll();
System.out.println("========== 전체 학생 조회 ==========");
// 향상된 for문
for(Student std : students) {
System.out.println(std);
}
}
// 이름 검색 view
@Override
public void selectName() {
// 이름이 일치하는 학생이 있는지 검색하기 위해
// 이름을 입력받아 service.selectName(입력받은이름)를 호출하고
// 결과를 Student 타입으로 받음.
// 결과가 null이 아닐경우 검색 결과 출력,
// 결과가 null일 경우 "일치하는 학생이 없습니다." 출력
System.out.print("[이름 검색]검색할 학생 이름 입력 : ");
String name = sc.nextLine();
Student std = service.selectName(name);
if(std == null) System.out.println("일치하는 학생이 없습니다.");
else System.out.println(std);
}
// 성별 검색 view
@Override
public void selectGender() {
// 성별이 일치하는 학생이 있는지 검색하기 위해
// 성별을 입력받아 service.selectGender(입력받은성별)를 호출하고
// 결과를 List 타입으로 받아 for문을 이용하여 출력.
System.out.print("[성별 검색]검색할 성별 입력(M/F) : ");
char gender = sc.nextLine().charAt(0);
if(gender == 'M' || gender == 'F') {
System.out.println("[성별 검색 결과]");
List<Student> students = service.selectGender(gender);
for(Student std : students) System.out.println(std);
}else {
System.out.println("잘못 입력하셨습니다. (M 또는 F만 입력해주세요.)");
}
}
// 나이 검색 view
@Override
public void selectAge() {
// 나이가 일치하는 학생이 있는지 검색하기 위해
// 나이를 입력받아 service.selectAge(입력받은나이)를 호출하고
// 결과를 List 타입으로 받아 for문을 이용하여 출력.
// 단, 전달받은 list의 길이가 0일 경우
// "나이가 일치하는 학생이 없습니다." 출력
System.out.print("[나이 검색]검색할 나이 입력 : ");
int age = sc.nextInt();
sc.nextLine();
List<Student> students = service.selectAge(age);
//if(students.size() == 0) --> 객체 개수 0
if(students.isEmpty()) System.out.println("나이가 일치하는 학생이 없습니다."); // -> 객체가 없을 때
else for(Student std : students) System.out.println(std);
}
// 학생 정보 추가 view
@Override
public void insertStudent() {
// 이름, 나이, 성별을 입력받아 Student 객체를 생성하고,
// service.insertStundet(생성한 Student객체)를 호출하여 삽입.
// (반환값 없음)
System.out.println("[학생 정보 추가]");
System.out.print("이름 : ");
String name = sc.nextLine();
System.out.print("나이 : ");
int age = sc.nextInt();
sc.nextLine();
System.out.print("성별 : ");
char gender = sc.nextLine().charAt(0);
service.insertStudent(new Student(name, age, gender));
}
// 학생 정보 수정 view
@Override
public void updateStudent() {
// 수정할 학생의 이름을 입력받고 이름이 일치하는 학생의 유무를 판단위해
// service.selectName(검색할 이름)을 호출하여 Student 타입을 반환 받음.
// 일치하는 학생이 없을 경우 "일치하는 학생이없습니다." 출력 후 메소드 종료.
// 일치하는 학생이 존재할 경우 이름, 나이 ,성별을 입력받아 Student 객체를 생성하고
// service.updateStudent(검색할 이름, 생성한 학생객체)하여 학생 정보를 수정.
// (반환값 없음)
System.out.print("[수정 학생 이름 검색] 입력 : ");
String name = sc.nextLine();
Student std = service.selectName(name);
if(std == null) {
System.out.println("일치하는 학생이없습니다.");
}else {
System.out.println("[학생 정보 수정]");
System.out.print("이름 입력 : ");
String updateName = sc.nextLine();
System.out.print("나이 입력 : ");
int age = sc.nextInt();
sc.nextLine();
System.out.print("성별 입력 : ");
char gender = sc.nextLine().charAt(0);
service.updateStudent(name, new Student(updateName, age, gender));
}
}
// 학생 정보 삭제 view
@Override
public void deleteStudent() {
// 삭제할 학생의 이름을 입력 받아
// service.deleteStudent(입력받은학생이름)을 호출하고
// Student 타입을 반환 받아
// 삭제 성공 시 삭제된 학생 정보 + " 삭제되었습니다.,
// 삭제 실패 시 "일치하는 학생이 없습니다." 출력
System.out.print("[학생 정보 삭제]삭제할 학생 이름 : ");
String name = sc.nextLine();
Student std = service.deleteStudent(name);
if (std == null) System.out.println("일치하는 학생이 없습니다.");
else System.out.println(std + " 삭제되었습니다.");
}
}
댓글남기기