본문 바로가기

웹개발 수업/JAVA

[Day +23]Collection / 자료구조, List, Set, Map, 제네릭스

210723 금

 

컬렉션(Collection) 프레임 워크

 

1. 컬렉션 프레임 워크

: 자료구조를 사용해서 객체들을 효율적으로 추가, 삭제, 검색할 수 있도록 자바에서 제공하는 인터페이스와 구현 클래스 패키지의 총칭

-추가, 삭제, 정렬 등의 기능처리가 간단하게 해결 되어 자료구조적 알고리즘을 구현할 필요 없음

-java.util 패키지에 포함되며, 인터페이스를 통해 정형화된 방법으로 다양한 컬렉션 클래스 이용 가능

 

*컬렉션
: 객체의 저장

*프레임 워크
: 사용 방법을 정해놓은 라이브러리

<자료구조>

1) 배열의 문제점

(1) 한 번 크기를 지정하면 변경할 수 없다.

- 공간 크기가 부족하면 에러가 발생 => 할당 시 넉넉한 크기로 할당하게 됨 (메모리 낭비)

- 필요에 따라 공간을 늘리거나 줄일 수 없음

(2) 배열에 기록된 데이터에 대한 중간 위치의 추가, 삭제가 불편하다.

- 추가, 삭제할 데이터부터 마지막 기록된 데이터까지 하나씩 뒤로 밀어내고 추가해야 함 (복잡한 알고리즘)

(3)  한 타입의 데이터만 저장 가능하다

 

2) 컬렉션의 장점

(1)  저장하는 크기의 제약이 없다.

(2) 추가, 삭제, 정렬 등의 기능 처리가 간단하게 해결된다.

- 자료를 구조적으로 처리 하는 자료구조가 내장되어 있어 알고리즘 구현이 필요 없음

(3) 여러 타입의 데이터가 저장 가능하다.

- 객체만 저장할 수 있기 때문에 필요에 따라 기본 자료형을 저장해야 하는 경우 Wrapper클래스 사용

 

3) 컬렉션의 주요 인터페이스

-List와 Set은 반대되는 개념이다

 

(1) List 컬렉션

1> 특징

배열과 비슷하게 객체를 인덱스로 관리한다.

2> 장점

-배열과 달리 저장 용량이 자동으로 증가하며, 객체를 저장할 때 자동 인덱스가 부여 된다.

-추가, 삭제, 검색을 위한 다양한 메소드들이 제공된다.

-객체 자체를 저장하는 것이 아니라 객체의 번지를 참조하기 때문에 동일한 객체를 중복 저장할 수 있다(0번 인덱스의 객체, 1번의 인덱스의 객체를 다르게 취급한다)

-null도 저장이 가능하다(이 경우 해당 인덱스는 객체를 참조하지 않는다)

3> 구현 클래스

-ArrayList, Vector, LinkedList 등

*인덱스로 객체를 관리하기 때문에 인덱스를 매개값으로 갖는다

<1> ArrayList

-List의 후손으로 초기 저장 용량은 10으로 자동 설정되며 따로 지정도 가능

-저장 용량을 초과한 객체들이 들어오면 자동으로 늘어나며 고정도 가능

-동기화(Synchronized)를 제공하지 않음

-ArrayList와 Vector는 거의 비슷하다

package com.kh.chap01_list.part01_basic.controller;

import java.util.ArrayList;

import com.kh.chap01_list.part01_basic.model.vo.Student;

public class ArrayListTest {
	
	// 컬렉션 타입을 사용하는 경우
	private ArrayList list = new ArrayList();

	//학생 추가
	public void addStudent(Student std) {
		list.add(std);
		
		
	}
	//학생 출력
	public void printStudent() {
		for(int i = 0 ; i < list.size(); i++) {
			System.out.println(list.get(i));
		}
		
	}
	//학생 삭제
	public void removeStudent(int index) {
		list.remove(index);
		
	}

}
package com.kh.chap01_list.part01_basic.controller;

import com.kh.chap01_list.part01_basic.model.vo.Student;

public class ObjectArrayTest {
	
	//객체 배열을 사용하는 경우
	private Student[] arr = new Student[3];
	private int size = 0 ; //객체 배열의 인덱스 및 현재 저장 된 요소 관리용 변수

	
	//학생 추가 메소드
	public void addStudent(Student std) {
		if(size < arr.length) {
		arr[size++] = std;
		}else {
			// 기존 배열의 크기를 넘어서는 경우에 대한 알고리즘을 직접 구현해야 함
			Student[] newArr = new Student[arr.length + 10];
			System.arraycopy(arr, 0, newArr, 0, arr.length);
			newArr[size++] = std;
			arr = newArr;
		}
	}
	
	
	//학생 출력 메소드
	public void printStudent() {
		for(int i = 0 ; i < size ; i++) {
			System.out.println(arr[i]);
		}
	}
	
	
	//학생 삭제 메소드
	public void removeStudent(int index) {
		for(int i = index; i < arr.length - 1 ; i++) {
			// 해당 인덱스는 지워주고 뒤의 값을 앞으로 당겨주는 알고리즘을 직접 구현해야 함
			arr[i] = arr[i+1];
			
		}
		size--;
	}
	
	//특정 인덱스에 학생 삽입하고 싶다면?
	// => 해당 인덱스부터 값을 뒤로 밀어주는 알고리즘을 구현해야 하며
	// 만약 배열의 길이가 부족하다면 그 이전에 배열 크기도 늘려줘야 한다
}
package com.kh.chap01_list.part01_basic.model.vo;

public class Student {
	private String name;
	private int score;
	
	public Student() {}

	public Student(String name, int score) {
		super();
		this.name = name;
		this.score = score;
	}

	public String getName() {
		return name;
	}

	public int getScore() {
		return score;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setScore(int score) {
		this.score = score;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", score=" + score + "]";
	}
	
}
package com.kh.chap01_list.part01_basic.run;

import com.kh.chap01_list.part01_basic.controller.ArrayListTest;
import com.kh.chap01_list.part01_basic.controller.ObjectArrayTest;
import com.kh.chap01_list.part01_basic.model.vo.Student;

public class Run {

	public static void main(String[] args) {
		
		//ObjectArrayTest test = new ObjectArrayTest();
		ArrayListTest test = new ArrayListTest();
		
		System.out.println("학생 3명 추가");
		test.addStudent(new Student("정한", 90));
		test.addStudent(new Student("부승관", 100));
		test.addStudent(new Student("금성무", 60));
		
		System.out.println("현재 등록 된 학생 출력");
		test.printStudent();
		
		System.out.println("학생 1명 더 추가");
		test.addStudent(new Student("신사임당", 90));
		test.printStudent();
		//"main" java.lang.ArrayIndexOutOfBoundsException: 3 => 이 오류 발생
		
		
		/*
		 * 학생 3명 추가
			현재 등록 된 학생 출력
		Student [name=정한, score=90]
		Student [name=부승관, score=100]
		Student [name=금성무, score=60]
		학생 1명 더 추가
		Student [name=정한, score=90]
		Student [name=부승관, score=100]
		Student [name=금성무, score=60]
		Student [name=신사임당, score=90]

	  */
		
		
		System.out.println("현재 등록 된 학생 출력");
		test.printStudent();
		
		System.out.println("1번 인덱스 학생 제거");
		test.removeStudent(1);
		
		System.out.println("현재 등록된 학생 출력");
		test.printStudent();
	}
	
	
}

<2> Vector

-구버전 st(Array와 비교 시)

-List의 후손

-ArrayList와 동등하지만 동기화(Synchronized)를 제공한다는 점이 ArrayList와 차이점 

-List 객체들 중에서 가장 성능이 좋지 않음

-동기화를 하기 때문에 성능이 조금 더 떨어진다

 

<3> LinkedList

1] 특징

-List의 후손으로 인접 참조를 링크해 체인처럼 관리

-객체끼리 서로 참조하고 있음(주소값을 가지고 있다는 뜻)

 

2] 장점

-특정 인덱스에서 객체를 제거하거나 추가하게 되면 바로 앞/뒤 링크만 변경하면 되기 때문에 객체 삭제와 삽입이 빈번하게 일어나는 곳에서는 ArrayList보다 성능이 좋음

-기본 사용에서는 ArrayList가 더 효율적이나 객체의 추가와 제거가 빈번하게 일어나는 상황에서는 LinkedList가 성능이 더 좋다

package com.kh.chap01_list.part02_list.controller;

import java.util.ArrayList;
import java.util.List;

import com.kh.chap01_list.part02_list.model.vo.Music;

public class ListController {

	
	/*
	 * List계열의 특징 : 순서 유지, 중복 저장 가능
	 * List 계열의 종류 : ArrayList(동기화X), Vector(동기화O), LinkedList
	 * LinkedList는 객체 삽입, 삭제가 빈번하게 이뤄지는 경우 ArrayList보다 효율이 좋음
	 * Vector는 ArrayList의 구버전이며 리스트 계열의 사용 메소드는 동일하므로 ArrayList를 대표로 테스트
	 */
	public void doList() {
		//List는 인터페이스이므로 객체 생성이 불가능하다
		//List list = new List();
		
		//List의 후손 클래스로 객체 생성(다형성 : 부모 레퍼런스로 자식 객체를 다룸)
		//List list = new ArrayList();
		
		
		
		//Q. 노란줄(경고성 메세지)가 많이 뜨는 이유?
		//A. 어떤 타입을 지정하는지 규정하지 않아서 안정성에 문제가 있다고 알려주는 메세지
		//ArrayList<Music> list = new ArrayList<Music>(3);에서
		//<Music>을 추가해 줌
		//제네릭을 <Music>으로 지정해주면 노란줄을 사라지고 String 타입은 저장 불가능하게 됨
		
		//Q. 제네릭을 선언하는 이유(장점)
		//A. 1) 안정성(다른 타입 추가 시 컴파일 에러 발생)
		//2) 반환형 다운 캐스팅 불필요
		
		ArrayList<Music> list = new ArrayList<Music>(3);
		// 크기  지정 가능. 지정하지 않으면 기본이 10개
		
		//1. add(E e) : 리스트의 끝에 인스턴스 데이터 추가
		//E => Element 요소라는 뜻으로 실제 컬렉션에 담기는 타입을 말한다
		list.add(new Music("부승관", "거침없이"));
		list.add(new Music("부승관", "wi-fi"));
		list.add(new Music("세븐틴", "박수"));
		//list.add("끝");
		
		System.out.println(list);
		//1. 크기의 제한이 없음(초기 크기 3에서 자동으로 증가)
		//2. 여러 타입의 객체를 저장 가능(Music 객체가 아닌 String 객체도 담김)
		
		
		//[Music [artist=부승관, title=거침없이], 
		//Music [artist=부승관, title=wi-fi], Music [artist=세븐틴, title=박수], 끝]
		
		
		//2. add(int index, E e) : 인덱스 지정하여 해당 인덱스에 인스턴스 추가
		list.add(1, new Music("앤마리", "2002"));
		System.out.println(list);
		
		//[Music [artist=부승관, title=거침없이], Music [artist=앤마리, title=2002], 
		//Music [artist=부승관, title=wi-fi], Music [artist=세븐틴, title=박수], 끝]
		// => 중간에 삽입한 것
		
		
		//3. set(int index, E e) : 지정한 인덱스의 값을 전달한 인스턴스로 변경
		list.set(2, new Music("혁오", "첨밀밀"));
		System.out.println(list);
		//[Music [artist=부승관, title=거침없이], Music [artist=앤마리, title=2002], 
		//Music [artist=혁오, title=첨밀밀], Music [artist=세븐틴, title=박수], 끝]
		
		
		//4. size() : 리스트 안에 몇 개의 데이터가 들어 있는지 확인
		System.out.println(list.size());
		
		
		//5. remove(int index) : 해당 인덱스의 인스턴스 삭제
		list.remove(1);
		System.out.println(list);
		//[Music [artist=부승관, title=거침없이], Music [artist=혁오, title=첨밀밀], 
		//Music [artist=세븐틴, title=박수], 끝]
		
		//6. get(int index) : 해당 인덱스의 인스턴스 조회
		//Music m = (Music)list.get(0); //다운 캐스팅(부모 => 자식)은 명시화
		//뮤직 객체 지정 후에는 다운캐스팅 불필요
		Music m = list.get(0);
		System.out.println(m);
		//Music [artist=부승관, title=거침없이]
		
		
		//7. contains(Object o) : 포함하고 있는지 참 거짓 리턴
		boolean bool = list.contains(new Music("혁오", "첨밀밀"));
		System.out.println(bool);
		//Music 클래스에 equals 메소드가 오버라이딩 되어 있지 않아서
		//contains 메소드 내부에서 필드 값 비교를 위해 equals를 호출했을 시
		//Object 클래스의 equals 메소드가 호출 되어 주소 값 비교를 하고 있어 false
		//=> Music 클래스에 필드값 비교를 하는 equals 메소드를 오버라이딩 하자!
		
		
		//8. indexOf(Object o) : 해당 값을 가진 인덱스 리턴
		int index = list.indexOf(new Music("혁오", "첨밀밀"));
		System.out.println(index);
		
		//답 : 1
		
		
		//9. subList(int index1, int index2) : index1부터 index2 이전까지 List로 추출하여 리턴
		List<Music> sub = list.subList(0, 2);
		System.out.println(sub);
		//[Music [artist=부승관, title=거침없이], Music [artist=혁오, title=첨밀밀]]

		
		//10. addAll(Collection c) : 리스트의 끝에 컬렉션 추가
		list.addAll(sub);
		System.out.println(list);
		//[Music [artist=부승관, title=거침없이], Music [artist=혁오, title=첨밀밀], 
		//Music [artist=세븐틴, title=박수], 끝, 
		//Music [artist=부승관, title=거침없이], Music [artist=혁오, title=첨밀밀]]
		
		//11. isEmpty() : 리스트 안의 값이 비었는지 참 거짓 리턴
		System.out.println(list.isEmpty());
		//false
		
		//12. clear() : 전체 삭제
		list.clear();
		System.out.println(list.isEmpty());
		//true
		
		
	}

}
package com.kh.chap01_list.part02_list.model.vo;

public class Music {
	
	private String artist;
	private String title;
	
	public Music() {}
	
	public Music(String artist, String title) {
		super();
		this.artist = artist;
		this.title = title;
	}

	public String getArtist() {
		return artist;
	}

	public String getTitle() {
		return title;
	}

	public void setArtist(String artist) {
		this.artist = artist;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	@Override
	public String toString() {
		return "Music [artist=" + artist + ", title=" + title + "]";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((artist == null) ? 0 : artist.hashCode());
		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;
		Music other = (Music) obj;
		if (artist == null) {
			if (other.artist != null)
				return false;
		} else if (!artist.equals(other.artist))
			return false;
		if (title == null) {
			if (other.title != null)
				return false;
		} else if (!title.equals(other.title))
			return false;
		return true;
	}
	
	
	

}
package com.kh.chap01_list.part02_list.run;

import com.kh.chap01_list.part02_list.controller.ListController;

public class Run {

	public static void main(String[] args) {
		ListController lc = new ListController();
		lc.doList();
	}

}

 

(2) Set 컬렉션

-순서를 유지하지 않고 저장한다.

-중복 저장이 안된다. 

-주머니 안에 공을 넣는다고 상상하라. 

 

(3) Map 컬렉션 

-키와 값을 묶어서 저장하는 특징이 있다(List와 Set은 값만 저장)

 


제네릭스(Generics)

 

1) 장점 

- 컴파일 시 강한 타입 체크 가능(실행 시, 컴파일 시 에러 방지)

- 타입 변환 제거 가능

 

2) 제네릭스 타입과 표현식

(1) 기본

-타입을 파라미터로 가지는 클래스와 인터페이스 선언 시 클래스 또는 인터페이스 이름 뒤에 “< >” 부호를 붙이고 사이에는 타입 파라미터 위치

 

[표현식]

클래스명<클래스타입> 레퍼런스 = new 생성자<클래스타입>();

클래스명<클래스타입> 레퍼런스 = new 생성자<>(); // JDK 1.7 부터 적용, 타입 추론

 

[사용예시]

ArrayList list1 = new ArrayList();

ArrayList list2 = new ArrayList<>();

 

(2) 제네릭스가 설정된 레퍼런스를 인자로 넘기는 경우

예) ArrayList list = new ArrayList();

BookManager bm = new BookManager();

bm.printInformation(list);

 

//BookManager Class

public void printInformation(ArrayList list){ ….

}

*메소드 쪽에서 받아주는 매개 변수도 제네릭스가 적용되어야 한다

 

(3) 제네릭스가 설정된 레퍼런스를 리턴하는 경우

예) public ArrayList getInformation(){

ArrayList list = new ArrayList();

return list;

}

*메소드의 반환형에도 제네릭스가 적용되어야 한다