2022.12.19-1 JAVA 복습 컬렉션 자료구조
15. 컬렉션 자료구조
컬렉션이란 모음 뭘 모으나? 객체를 모아서 저장한다.
객체를 저장할 수있는 자료 구조
배열도 하나의 자료구조이다. 1.같은타입만 저장가능 2.길이변경불가 길이변경하려면 배열복사를 해야함.
15장은 배열과 유사하지만 조금다름
1.같은타입만 가능이지만 다른타입도 가능하긴함. 대부분은 같은 타입만 저장한다.
2.길이 변경가능함. 길이 고정x
3.키를주고 값을 저장하는 것도 존재함. 배열과 다르게 저장하는것도잇음.
15.1 컬렉션 프레임 워크
프레임워크? = 미리 짜여진 틀이다. 자바는 객체를 저장할수있는 클래스를 미리 제공을 한다.
java.util패키지에 있다.
인터페이스 용도? 객체 사용설명서 인터페이스만 공부하면 밑에잇는 객체들 사용할수잇다.
List Set -> Collection 상속에서 만듬/Map은 저장방식 사용방식이 다름.
결국 인터페이스 사용방법만 알면 다 사용가능하다.
리스트 순서를 유지하고 저장 객체 중복저장 가능 인덱스가 잇어서 배열과 비슷하다.
set 순서정하지않음 중복저장x 집합과 비슷함.
Map은 키와 값으로 저장된 엔트리가잇어서 키값을 주고 객체를 찾는다. 객체 식별 용도로 키 사용해서 키는 중복못함.
15.2 List 컬렉션
List 컬렉션은 객체를 인덱스롤 관리하기때문에 객체를 저장하면 인덱스가 부여되고 인덱스로 객체를 검색 삭제할 수있는 기능을 제공한다.
객체를 중복저장할 수있다.
실제 객체가 저장되는게아니라 객체의 번지가 저장이되는거임 그래서 중복저장이 가능하다.
List 컬렉션안에는 ArrayList Vector LinkedList 등이 있다.
리스트가 사용하는 메소드는 다음과 같다.
List<T> 제네릭 타입이라 저장하고 싶은 타입을 저장하면됨. 어떤게 추가되어있냐에 따라 사용함.
외울필요x 보면알수있다.
add(E e) 맨끝에 추가하기 add(int index, E element) 주어진 인덱스에 추가 삽입역할
remove(int index) 인덱스지정 제거 remove(Object o) 인덱스모를때 객체 그냥 제거 있으면 true리턴 없으면 false리턴
clear() 컬렉션에 저장된 객체를 다지우기
get(int index) 얻어온다 찾아온다. 특정 인덱스의 값 가져와
구체적인 타입 제공햇으면 E에 타입 들어가는것임. 제공안하면 Object가 들어감
정적메소드도 잇음. 디폴트메소드도있음(구현객체필요).
리스트는 인덱스로 저장하기 때문에 인덱스 매개변수가 많다.
List api도큐먼트 확인하면 좋음.
15.2.1 15.2.1 ArrayList
가장 많이 사용하는 컬렉션으로 인덱스로 번지를 저장한다. 번지로 저장하기 때문에 동일한 객체도 중복으로 넣을 수잇다.
인터페이스타입 = 구현클래스타입
List<E> list = new ArrayList<E>(); //E에 지정된 타입의 객체만 저장
List<E> list = new ArrayList<>(); //E에 지정된 타입의 객체만 저장 앞뒤가 같으면 뒤에 생략가능
List list = new ArrayList(); //모든 타입의 객체를 저장 E대신 Object사용
만약 구체적인 타입 안주면? Object라 아무거나 가능한데 보통 잘 안씀
대부분은 같은 객체만 저장하도록함.
ArrayList에 add로 객체를 추가하면 인덱스 0번부터 차례대로 작성된다.
특정 인덱스의 객체를 제거하면 바로 뒤 인덱스부터 마지막 인덱스까지 앞으로 1씩 당겨짐.
마찬가지로 중간에 삽입하면 뒤로 1씩 밀려남
public class Board {
private String subject;
private String content;
private String writer;
public Board(String subject, String content,String writer ) {
this.subject = subject;
this.content = content;
this.writer = writer;
}
public String getSubject() {return subject;}
public void setSubject(String subject) {this.subject = subject;}
public String getContent() {return content;}
public void setContent(String content) {this.content = content;}
public String getWriter() {return writer;}
public void setWriter(String writer) {this.writer = writer;}}public class ArrayListExample {
public static void main(String[] args) {
//ArrayList 컬렉션 생성
//이 인터페이스에 보드 객체만 넣을 수 있음.
List<Board> list = new ArrayList<>();
// 객체추가
//list.add(생성자) add(E e)
//Board board = new Board("제목6", "내용6", "글쓴이6"); list.add(board); 이렇게 대신 바로넣은거
list.add(new Board("제목1", "내용1", "글쓴이1"));
list.add(new Board("제목2", "내용2", "글쓴이2"));
list.add(new Board("제목3", "내용3", "글쓴이3"));
list.add(new Board("제목4", "내용4", "글쓴이4"));
list.add(new Board("제목5", "내용5", "글쓴이5"));
//실제 저장된 객체 수 얻기 size() 메소드 사용
int size = list.size();
System.out.println("총 객체 수: " + size); //5개 저장했으니 총 개채수 5
System.out.println();
//특정 인덱스의 객체 가져오기 get()메소드
Board board = list.get(2);
System.out.println(board.getSubject() + "\t" + board.getContent() + "\t" + board.getWriter());
System.out.println();
//모든 객체를 하나씩 가져오기
for(int i = 0; i < list.size(); i++) {
Board b = list.get(i);
System.out.println(b.getSubject() + "\t" + b.getContent() + "\t" + b.getWriter());
}
System.out.println();
//객체 삭제
list.remove(2);
list.remove(2); //뒤에게 당겨지니 2번째 삭제후 2번이된 3번삭제
//향상된 for문으로 모든 객체 하나씩 가져오기
//for (2타입변수 : 1배열({3실행문} 1배열에서 가저올 항목이 있을경우 2변수에 항목을 저장하고 3실행문을 실행한다.
// 5장 10절
for(Board b : list) {
System.out.println(b.getSubject() + "\t" + b.getContent() + "\t" + b.getWriter());
}}}15.2.2 Vector
Vector는 ArrayList와 동일한 내부구조를 가지고 있다. 작성방법은 구현객체만 달라서 사용법이 같음.
언제 ArrayList를 쓸건지 Vector를 쓸건지를 정해야함.
차이점은? Vector는 동기화된 메소드로 구성되어 있어서 멀티스레드가 동시에 Vector()메소드를 실행할 수 없다.
어떤게 더좋냐? 알수없음. 싱글스레드는 뭘쓰던 같다. 멀티스레드에서 공유객체일땐 Vector를 사용하는게 낫다. 겹치면 오류나서!
List<E> list = new Vector<E>(); //E에 지정된 타입의 객체만 저장
List<E> list = new Vector<>(); //E에 지정된 타입의 객체만 저장
List list = new Vector(); //모든 타입의 객체를 저장 E대신 Object사용
public class VectorExample {
public static void main(String[] args) {
//Vector 컬렉션 생성
List<Board> list = new Vector<>();
//List<Board> list = new ArrayList<>();
//ArrayList로하면 두 스레드가 동시에 add()메소드를 호풀할수있기때문에 경합이 발생헤 결국 하나만 저장되서
// 총개수가 맞지않거나 오류가 발생한다.
//Vector의 add()는 동기화 메소드이므로 한번에 하나의 스레드만 실행할수잇어서 경합이 발생하지 않는다.
//작업 스레드 객체 생성
//Thread threadA = new Thread() {익명자식객체};
Thread threadA = new Thread() {
@Override
public void run() {
//객체 1000개 추가
for(int i=1; i<=1000; i++) {
list.add(new Board("제목"+i, "내용"+i, "글쓴이"+i));
}
}
};
//작업 스레드 객체 생성
Thread threadB = new Thread() {
@Override
public void run() {
//객체 1000개 추가
for(int i=1001; i<=2000; i++) {
list.add(new Board("제목"+i, "내용"+i, "글쓴이"+i));
}
}
};
//작업 스레드 실행
threadA.start();
threadB.start();
//작업 스레드들이 모두 종료될때까지 메인 스레드를 기다리게함
try {
threadA.join(); //a실행 메인,b쉬기
threadB.join(); //b실행 메인쉬기
} catch(Exception e) {
}
//저장된 총 객체 수 얻기
int size = list.size();
System.out.println("총 객체 수: " + size);
System.out.println();
}}ArrayList로하면 두 스레드가 동시에 add()메소드를 호풀할수있기때문에 경합이 발생헤 결국 하나만 저장되서
총개수가 맞지않거나 오류가 발생한다. Vector의 add()는 동기화 메소드이므로 한번에 하나의 스레드만 실행할수잇어서 경합이 발생하지 않는다.
5.2.3 LinkedList
LinkedList와 ArrayList사용방법은 동일하지만 내부 구조는 완전히 다르다.
ArrayList는 배열에 객체를 저장하지만 LinkedList는 인접객체를 체인처럼 연결해서 관리한다.
ArrayList제거시 매우 여러개잇을땐 중간에 빼면 하나씩 앞으로 당겨지니가 성능상으로 안좋아짐.
이를해결하기 위해 나온게 LinkedList 서로서로 연결되어있다.
본인을 기준으로 좌우에 뭐가 있다를 연결함.
중간에 제거시 연결정보만 바꿈 다른거는 수정할 필요가 없음.
중간 삭제 추가시 좀더 좋은 성능임.
List<E> list = new LinkedList<E>(); //E에 지정된 타입의 객체만 저장
List<E> list = new LinkedList<>(); //E에 지정된 타입의 객체만 저장
List list = new LinkedList(); //모든 타입의 객체를 저장 E대신 Object사용
public class LinkedListExample {
public static void main(String[] args) {
//ArrayList 컬렉션 객체 생성
List<String> list1 = new ArrayList<String>();
//LinkedList 컬렉션 객체 생성
List<String> list2 = new LinkedList<String>();
//시작 시간과 끝 시간을 저장할 변수 선언
long startTime;
long endTime;
//ArrayList 컬렉션에 저장하는 시간 측정
startTime = System.nanoTime();
for(int i=0; i<10000; i++) {
list1.add(0, String.valueOf(i));//제일앞에 삽입 하나씩 밀고 넣기 인덱스0에넣기
}
endTime = System.nanoTime();
System.out.printf("%-17s %8d ns \n", "ArrayList 걸린 시간: ", (endTime-startTime) );
//LinkedList 컬렉션에 저장하는 시간 측정
startTime = System.nanoTime();
for(int i=0; i<10000; i++) {
list2.add(0, String.valueOf(i)); //제일앞에 삽입 앞에 넣고 새로운 링크만 만들기 인덱스0에넣기
}
endTime = System.nanoTime();
System.out.printf("%-17s %8d ns \n", "LinkedList 걸린 시간: ", (endTime-startTime) );
}}15.3 Set컬렉션
인덱스로 관리x 중복x 저장순서 유지x null은 하나만 저장할수있다.
Set컬렉션은 수학의 집합에 비유될 수 있다. 집합은 순서와 상관없고 중복이 허용되지않기때문이다.
구슬주머니라고 보면된다.
set컬렉션에는 HashSet, LinkedHashSet, TreeSet 등이 있는데 인덱스로 관리하지 않기때문에 인덱스가 매개값으로 갖는 메소드가 없다.
Set
get()이 없음 하나씩 가져올수없음! -> iterator() 반복자를 사용해야함.
리턴타입 인터페이스 Iterator타입 가지고 있는 메소드 hasNext()가져올게 있느냐?, next()가져와라로 가져올수있음.
가져올게 없는데 next()하면 예외발생하니 hasNext()먼저 사용하자.
객체를 꺼내서 가져오는게 아니라 참조를 가져오는것임.
remove()는 set에서 아예 제거하는 것임. 유용하게쓸데가있다.
Iterator<E> iterator() 저장된 객체를 한번씩 가져오는 반복자리턴
15.3.1 HashSet
Set set = new HashSet(); //E에 저장된 타입의 객체만 저장
Set set = new HashSet<>(); //E에 저장된 타입의 객체만 저장
Set set = new HashSet() //모든 타입의 객체를 저장
타입파라미터E에는 HashSet에 저장하고 싶은 객체 타입을 지정하면된다.
HashSet은 동일한 객체는 중복저장하지 않는다.
여기서 동일한 객체란 동등객체를 말하는데 HashSet은 다른객체라도 hashCode()메소드 리턴값이 같고 equals()메소드가 true를 리턴하면 동일한 객체라고 판단한다.
12.3 Object 클래스에서 등장했었음. hashCode와 equals비교
public class HashSetExample {
public static void main(String[] args) {
//HashSet 컬렉션 생성
Set<String> set = new HashSet<String>();
//HashSet set = new HashSet();
//객체 저장
set.add("Java");
set.add("JDBC");
set.add("Servlet/JSP");
set.add("Java"); //<-- 중복 객체이므로 저장하지 않음
set.add("iBATIS");
//저장된 객체 수 출력
int size = set.size();
System.out.println("총 객체 수: " + size); //4나옴
}}이름과 나이가 동일할 경우 Member객체를 HashSet에 중복저장하지 않는다.
Member 클래스를 선언할때 이름과 나이가 동일하다면 동일한 해시코드가 리턴되도록 hashCode()를 재정의하고 equals()메소드가 true를 리턴하도록 재정의 했기때문이다.
이전예제는 String에서 이미 hashCode()와 equals()메소드가 재정의 되어있기 때문에 그냥 사용하면 알아서된다.
public class Member {
public String name;
public int age;
public Member(String name, int age) {
this.name = name;
this.age = age;
}
//hashCode 재정의 문자열에 + 나이 같다면 계속동일하게 나옴.
@Override
public int hashCode() {
return name.hashCode() + age;
}
//equals 재정의
@Override
public boolean equals(Object obj) {
if(obj instanceof Member target) { //멤버만 넣을 것이니 멤머 객체인지 확인
return target.name.equals(name) && (target.age==age) ;
} else {
return false;
}}}public class HashSetExample {
public static void main(String[] args) {
//HashSet 컬렉션 생성
Set<Member> set = new HashSet<Member>();
//Member 객체 저장
set.add(new Member("홍길동", 30));
set.add(new Member("홍길동", 30));
//저장된 객체 수 출력
System.out.println("총 객체 수 : " + set.size()); //이름이 같고 번호가 같으니 하나만 저장 = 총 객체수: 1
}15.3.2 반복자로 가져오기
두가지 반복으로 가져올수있다.
Set<E> set = new HashSet<>();
for(E e : set){}
향상된 for문에 들어올수있는 것? Iterable<E>를 상속하거나 구현하는 타입이라면 모두 가능하다.
또한 set컬렉션은 interator()메소드로 반복자를 얻어 객체를 하나씩 가져올 수있다.
Set<E> set = new HashSet<>();
Iterator<E> iterator = set.iterator();
iterator는 다음과같은 메소드를 제공한다.
public class HashSetExample {
public static void main(String[] args) {
//HashSet 컬렉션 생성
Set<String> set = new HashSet<String>();
//객체 추가 4개문자열 추가
set.add("Java");
set.add("JDBC");
set.add("JSP");
set.add("Spring");
//객체를 하나씩 가져와서 처리 특정객체 지정 불가
Iterator<String> iterator = set.iterator();
while(iterator.hasNext()) {
//객체를 하나 가져오기
String element = iterator.next();
System.out.println(element);
if(element.equals("JSP")) {
//가져온 객체를 컬렉션에서 제거
iterator.remove(); //iterator는 참조만할뿐이지 Set에서 진짜로 삭제하는거임.
}
}
System.out.println();
//객체 제거
set.remove("JDBC");
//객체를 하나씩 가져와서 처리
//for문안에서 확인하고 삭제하기 해버리면 개수모자라서 오류발생함!
//-> 위 while문처럼 가져올게있는지 가져오기로 삭제하기.
for(String element : set) {
System.out.println(element);
}}}15.4 Map컬렉션
Map컬렉션은 키와 값으로 구성된 엔트리 객체를 저장한다. 여기서 키와 값음 모두 객체이다.
키는 중복으로 저장할수 없지만 값은 중복으로 저장할수있다. 키로 찾기때문.
기존에 저장된 키와 동일한 키로 값을 저장하면 기존의 값이 없어지고 새로운 값으로 대치된다.
Entry라는 인터페이스를 구현한 객체가 Map컬렉션에 저장된다.
Entry는 키 와 값으로 저장되어있다. 키 = 객체(중복불가) 값 = 번지(중복가능)
Map 컬렉션에는 HashMap, Hashtable, LinkedHashMap, Properties, TreeMap등이 있다.
공통적으로 사용하는 메소드는 다음과 같다.
저장할때 put(key, value)
V get(Object key) 객체 가져오기
V remove(Object key) 제거할때는 키와 값 전부다 엔트리 그자체를 제거
boolean containsKey(Object key) 이 키를 가진게 존재하느냐
boolean containsValue(Object key) 이 값을 가진게 존재하느냐
isEmpty()비어있느냐?
int size()엔트리가 몇개저장되어있느냐?
Set<Map.Entry<K,V>> entrySet() // Set타입의 객체에 entry가 들어가있다. 엔트리를 set에 넣음.
Set keySet() //키들로만 Set타입의 객체로 만들기
Collection values() // 값들만 모아서 Collection타입으로 전환
메소드의 매개변수 타입과 리턴타입에 K,V라는 타입파라미터가 있는데 K는 키 V는 갑을 말한다.
15.4.1 HashMap
키와 값으로 저장할때 키로 사용할 객체가 동등객체인지 판단
hashCode와 eqauls로 동등객체 비교함. HashSet처럼.
Map<키타입, 값타입> map = new HashMap<키타입, 값타입>();
Map<String, Integer> map = new HashMap<String, Integer>();
Map<String, Integer> map = new HashMap<>(); //같으면 역시나 생략가능
Map map = new HashMap(); //아무거나
public class HashMapExample {
public static void main(String[] args) {
//Map 컬렉션 생성
Map<String, Integer> map = new HashMap<>();
//타입파라미터에 기본타입은 못넣음 Integer 중요!
//객체 저장
map.put("신용권", 85); //Integer에 기본타입 썻는데 자동 박싱되는것임.
map.put("홍길동", 90);
map.put("동장군", 80);
map.put("홍길동", 95); //키값이 홍길동 중복되므로 나중에 넣은 95만 저장
System.out.println("총 Entry 수: " + map.size());
System.out.println();
//키로 값 얻기
String key = "홍길동";
//int value = map.get(Object key);
int value = map.get(key); //자동언박싱
System.out.println(key + ": " + value);
System.out.println();
//키 Set 컬렉션을 얻고, 반복해서 키와 값을 얻기
Set<String> keySet = map.keySet(); //키가 스트링이라 셋 스트링으로함
Iterator<String> keyIterator = keySet.iterator();
//반복자얻어서 반복
while (keyIterator.hasNext()) {
String k = keyIterator.next(); //키얻음
Integer v = map.get(k); //위에서 얻은 키로 밸류값얻기 .get(key)
System.out.println(k + " : " + v);
}
System.out.println();
//엔트리 Set 컬렉션을 얻고, 반복해서 키와 값을 얻기
//Set<Map.Entry<K,V>> = entrySet()
//Set<Entry> -> Set<Entry<String, Integer>>된거임.
Set<Entry<String, Integer>> entrySet = map.entrySet();
Iterator<Entry<String, Integer>> entryIterator = entrySet.iterator();
while (entryIterator.hasNext()) {
Entry<String, Integer> entry = entryIterator.next();
String k = entry.getKey();
Integer v = entry.getValue();
System.out.println(k + " : " + v);
}
System.out.println();
//키로 엔트리 삭제- 키값주면 그 키로 엔트리삭제함.
map.remove("홍길동");
System.out.println("총 Entry 수: " + map.size());
System.out.println();
}}15.4.2 Hashtable
Hashtable은 HashMap과 동일한 내부구조를 가지고 있다.
차이점은 Hashtable은 동기화된 메소드로 구성되어 있기때문에 멀티스레드가 동시에 메소드를 실행할수없다.
따라서 멀티스레드 환경에서도 안전하게 객체를 추가 삭제할수있다.
== Vector와 ArrayList차이 처럼
HashMap사용시 오류발생가능함.
Map<String, Integer> map = new Hashtable<String, Integer>();
Map<String, Integer> map = new Hashtable<>(); //같으면 역시나 생략가능
Map map = new Hashtable(); //아무거나
15.4.3 Properties
맵계열의 구현클래스
Hashtable의 자식클래스이기때문에 이 특징을 그대로 가지고 있다. '동기화메소드'
Hashtable과의 차이점은 String타입으로 제한한 컬렉션이라는것임.
키와 값을 모두 String타입으로 제한한 컬렉션이다.
용도는 주로 .properties 프로퍼티 파일(텍스트파일)을 읽을때 사용한다.
검은색=키 =다음에 파란색 값키=값의 형태로 보이는 형태임
일반 텍스파일과 다르게 ISO 8859-1 문자셋으로 저장되며 한글일경우 //u + 유니코드로 표현되어 저장(슬래쉬u)
Properties properties = new Properties();
Map이기 때문에 맵처럼 가저와도되지만
Map인터페이스말고 properties의 별도로 정의된 메소드를 사용하기 위해서 Properties 객체로 생성함.
public class PropertiesExample {
public static void main(String[] args) {
//Properties 컬렉션 생성
Properties properties = new Properties();
//PropertiesExample.class와 동일한 ClassPath에 있는 database.properties 파일 로드
// 컴파일 오류발생 해서 try catch를 해주는게 좋음.
//책에서는 메인에 throws IOException했는데 이것은 별로 좋지 않은 방식임!
//load(InputStream inputStream)
try {
properties.load(PropertiesExample.class.getResourceAsStream("database.properties"));
} catch (IOException e) {}
//load하면 properties 에 키와 값이 저장됨
//주어진 키에 대한 값 읽기 리턴되는값, 키 모두 String 고정임
//주는 키의 값이 뭐냐?
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
String username = properties.getProperty("username");
String password = properties.getProperty("password");
String admin = properties.getProperty("admin");
//값 출력
System.out.println("driver : " + driver);
System.out.println("url : " + url);
System.out.println("username : " + username);
System.out.println("password : " + password);
System.out.println("admin : " + admin);
}}15.5 검색기능을 강화시킨 컬렉션
검색기능을 강화시킨 TreeSet 과 TreeMap을 제공한다.
각각 Set와 Map이다.
15.5.1 TreeSet
TreeSet는 이진트리를 기반으로 한 Set컬렉션이다. 왼쪽가지하나 오른쪽 가지하나
여러개의 노드가 트리 형태로 연결된 구조로 루트 노드라고 불리는 하나의 노드에서 시작해 각 노드에 최대 2개의 노드를 연결할수있는 구조를 가지고 있다.
TreeSet에 객체를 저장하면 다음 사진처럼 정렬된다. 부모 노드의 객체와 비교해서 낮은 것은 왼쪽자식노드에 높은것은 오른쪼고 자식 노드에 저장한다.
노드가 추가되면 left right추가
객체 왼쪽이 뭐냐 오른쪽이 뭐냐에 대한 정보도 저장 좌측노드번지 우측노드번지 이런식으로 들어감.
생성방법
TreeSet<E> treeSet = new TreeSet<E>();
TreeSet<E> treeSet = new TreeSet<>();
Set타입 변수에 대입해도 되지만 TreeSet타입으로 대압한 이유는 검색 관련메소드가 TreeSet에 정의되어있기때문에
TreeSet에 특화된 메소드를 사용하기 위함이다.

First last젤앞, 젤뒤
lower주어진객체보다 바로 아래 객체 higher바로위 객체리턴
Floor바닥 주어진 객체와 동등한 객체가 있으면 리턴 없으면 주어진 객체의 바로 아래의 객체를리턴
ceiling 천장 주어진 객체와 동등한 객체가 있으면 리턴 없으면 주어진 객체의 바로 위의 객체를리턴
poolFrist()제일낮은객체를 꺼내오고 컬렉션에서 제거 polllast제일높은객체 가져오고 컬렉션에서 제거
desceningIterator() 내림차순으로 정렬된 반복자를 리턴
tailSet아래에서부터 headSet위에서부터 subSet은 그냥 범위
true 제공된 값을 포함하면 true 아니면 false로하기
public class TreeSetExample {
public static void main(String[] args) {
//TreeSet 컬렉션 생성
TreeSet<Integer> scores = new TreeSet<>();
//Integer 객체 저장
//숫자 기준x로 그냥 저장해도 정렬을 시킴. 왼쪽 작은거 오른쪽 큰거 식
scores.add(87);
scores.add(98);
scores.add(75);
scores.add(95);
scores.add(80);
//정렬된 Integer 객체를 하나씩 가져오기
for(Integer s : scores) {
System.out.print(s + " ");
}
System.out.println("\n");
//특정 Integer 객체를 가져오기
System.out.println("가장 낮은 점수: " + scores.first()); //제일 낮은 객체 리턴
System.out.println("가장 높은 점수: " + scores.last()); //제일 높은 객체 리턴
System.out.println("95점 아래 점수: " + scores.lower(95)); // 95보다 작은거
System.out.println("95점 위의 점수: " + scores.higher(95)); // 95보다 큰거
System.out.println("95점이거나 바로 아래 점수: " + scores.floor(95)); //95있으면 95 없으면 바로아래
System.out.println("85점이거나 바로 위의 점수: " + scores.ceiling(85) + "\n"); //85있으면 85 없으면 바로위
//내림차순으로 정렬하기
NavigableSet<Integer> descendingScores = scores.descendingSet();
for(Integer s : descendingScores) {
System.out.print(s + " ");
}
System.out.println("\n");
//범위 검색( 80 <= ) //headSet, tailSet
NavigableSet<Integer> rangeSet = scores.tailSet(80, true); //그값포함하면 true 아니면 false로하기
for(Integer s : rangeSet) {
System.out.print(s + " ");
}
System.out.println("\n");
//범위 검색( 80 <= score < 90 )
rangeSet = scores.subSet(80, true, 90, false); //그값포함하면 true 아니면 false로하기
for(Integer s : rangeSet) {
System.out.print(s + " ");
}}}15.5.2 TreeMap
TreeSet과 똑같지만 단순한 값이 아닌 키와 값으로 저장함
TreeMap<K, V> treeMap = new TreeMap<K, V>();
TreeMap<K, V> treeMap = new TreeMap<>();
역시나 특화된 메소드를 사용하기 위해 TreeMap 타입 사용
TreeSet과 유사하지만 객체자리에 Entry가 들어감. 메소드들 접미사에 Entry가있음.
public class TreeMapExample {
public static void main(String[] args) {
//TreeMap 컬렉션 생성
TreeMap<String,Integer> treeMap = new TreeMap< >();
//엔트리 저장 (키, 값) 키들로 정렬시킴.
//키타입 스트링 값타입 인티저
treeMap.put("apple", 10);
treeMap.put("forever", 60);
treeMap.put("description", 40);
treeMap.put("ever", 50);
treeMap.put("zoo", 80);
treeMap.put("base", 20);
treeMap.put("guess", 70);
treeMap.put("cherry", 30);
//정렬된 엔트리를 하나씩 가져오기
//Map자체는 향상된 반복문에 못넣음. 그래서 Set으로 바꾸어서 저장하고 불러오기
//Set으로 Entry객체를 모아 넣기
Set<Entry<String, Integer>> entrySet = treeMap.entrySet();
for(Entry<String, Integer> entry : entrySet) {
System.out.println(entry.getKey() + "-" + entry.getValue());
}
System.out.println();
//특정 키에 대한 값 가져오기
Entry<String,Integer> entry = null;
entry = treeMap.firstEntry();
System.out.println("제일 앞 단어: " + entry.getKey() + "-" + entry.getValue());
entry = treeMap.lastEntry();
System.out.println("제일 뒤 단어: " + entry.getKey() + "-" + entry.getValue());
entry = treeMap.lowerEntry("ever");
System.out.println("ever 앞 단어: " + entry.getKey() + "-" + entry.getValue() + "\n");
//내림차순으로 정렬하기
//정렬후 set에 넣어서 출력
NavigableMap<String,Integer> descendingMap = treeMap.descendingMap();
Set<Entry<String,Integer>> descendingEntrySet = descendingMap.entrySet();
for(Entry<String,Integer> e : descendingEntrySet) {
System.out.println(e.getKey() + "-" + e.getValue());
}
System.out.println();
//범위 검색
System.out.println("[c~h 사이의 단어 검색]");
NavigableMap<String,Integer> rangeMap = treeMap.subMap("c", true, "h", false); //c이상 h미만
for(Entry<String, Integer> e : rangeMap.entrySet()) { //여기도 Set으로 바꾸어서 넣음.
System.out.println(e.getKey() + "-" + e.getValue());
}}}15.5.3 Comparable과 Comparator
비교할수잇는 / 비교자
TreeSet에 저장되는 객체와 TreeMap에 저장되는 키 객체는 저장과 동시에 오름차순으로 정렬되고 자동으로 정렬된다.
어떤 객체든 정렬될 수 있는 것은 아니고 객체가 Comparable인터페이스를 구현하고 있어야 가능하다.
예를들어 Comparable인터페이스를 구현안하는 문자, 숫자가 아닌 것을 키, 값등으로 넣엇을때 비교가 불가능하면 트리셋과 트리맵이 정렬이 안되고 에러가 발생한다.
Integer Double String 타입은 모두 Comparable을 구현하고 있기때문에 상관없는데 사용자 정의 객체를 저장할때는 반드시 Comparable을 구현해야한다.
Comparable인터페이스에는 compareTo()메소드가 정의되어있다. 따라서 사용자 정의 클래스에서 이 메소드를 재정의해서 비교결과를 정수값으로 리턴해야 한다.
Comparable<T> 객체
int compareTo(T o) 주어진 객체와 같으면 0을리턴 주어진 객체보다 적으면 음수를 리턴 주어진 객체보다 크면 양수를 리턴
숫자를 반대로 적으면 내림차순됨.
양은 중요x 부호가 중요함. a.compareTo(b); 이런식으로 비교
public class Person implements Comparable<Person>
Person자기자체를 구현객체로 Comparable 인터페이스를 구현하고 public int compareTo(Person o)메소드를 재정의
age<o.age age값과 매개변수로 들어온 age값을 비교 하기
public class Person implements Comparable<Person> {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person o) {
//Person객체에 주어진 o와 비교
if(age<o.age) {
return -1;
}
else if(age == o.age) {
return 0;
}
else {
return 1;
}}}public class ComparableExample {
public static void main(String[] args) {
//TreeSet 컬렉션생성
TreeSet<Person> treeSet = new TreeSet<>();
//객체 저장
treeSet.add(new Person("홍길동", 45));
treeSet.add(new Person("김자바", 25));
treeSet.add(new Person("박지원", 31));
//객체를 하나씩 가져오기
for(Person person : treeSet) {
System.out.println(person.name + ": " + person.age);
}이미 다른사람이 만들어놓은 클래스에Comparable 구현객체가 없을 경우 어떻게 treeset에 저장을 해야하나? 그러면 비교자 Comparator를 만들어주면된다.
비교기능이 있는 Comparable 구현객체를 TreeSet에 저장하거나 TreeMap의 키로 저장하는 것이 원칙이지만
비교기능이 없는 객체를 저장하고 싶다면 방법이 없진않다.
비교자 Comparator를 다음과 같이 제공하면된다.
생성자의 매개값으로 비교자 제공 여기서 비교자의 역할? E타입 의 객체를 비교해줌.
비교자는 Comparator 인터페이스를 구현하면된다. Comparator에는 compare()메소드가 정의되어있다.
비교자는 이 메소드를 재정의해서 비교결과를 정수값으로 리턴하면된다.
int compare(T o1, T o2) o1과 o2가 동등하다면 0리턴 o1이 o2보다 앞에 오게하려면 음수리턴 o1이 o2보다 뒤에 오게하려면 양수를 리턴
public class Fruit {
public String name;
public int price;
public Fruit(String name, int price) {
this.name = name;
this.price = price;
}}public class FruitComparator implements Comparator<Fruit>{
@Override
public int compare(Fruit o1, Fruit o2) {
if(o1.price < o2.price) {
return -1;
} else if (o1.price == o2.price) {
return 0;
} else {
return 1;
}}}public class ComparableExample {
public static void main(String[] args) {
//비교자를 제공한 TreeSet 컬렉션생성
TreeSet<Fruit> treeSet = new TreeSet<Fruit>(new FruitComparator());
//객체저장
treeSet.add(new Fruit("포도", 3000));
treeSet.add(new Fruit("수박", 10000));
treeSet.add(new Fruit("딸기", 6000));
//객체 하나씩 가져오기
for(Fruit fruit : treeSet) {
System.out.println(fruit.name + ": " + fruit.price );
}}}15.6 LIFO와 FIFO컬렉션
후입선출(LIFO Last In Fist Out)은 나중에 넣은 객체가 먼저 빠져나가고, 선입선출(FIFO First In First Out)은 먼저 넣은 객체가 먼저 빠져나가는 구조이다.
컬렉션 프레임 워크는 LIFO자료구조를 제공하는 스택 클래스와 FIFO자료구조를 제공하는 큐 인터페이스를 제공하고 있다.
스택을 이용한 대표적인 예가 JVM 스택 메모리 이다. 스택 메모리에 저장된 변수는 나중에 저장된 것부터 제거된다. 5장2절 참조
넣기 push 빼기pop
큐를 응용한 대표적인 예는 스레드풀의 작업큐이다. 작업큐는 먼저들어온 작업부터 처리한다.
넣기 offer 빼기 poll
15.6.1 Stack
LIFO자료구조를 구현한 클래스이다.
Stack<E> stack = new Stack<E>();
Stack<E> stack = new Stack<>();
주요메소드
E push(E item) 주어진객체를 스택에 넣는다.
E pop() 스택의 맨 위 객체를 빼낸다.
public class Coin {
private int value;
public Coin(int value) {
this.value = value;
}
public int getValue() {
return value;
}}public class StackExample {
public static void main(String[] args) {
//Stack 컬렉션 생성
Stack<Coin> coinBox = new Stack<>();
//동전넣기
coinBox.push(new Coin(100));
coinBox.push(new Coin(50));
coinBox.push(new Coin(500));
coinBox.push(new Coin(10));
//동전하나씩 꺼내기
//비어잇지않으면 반복
while(!coinBox.isEmpty()) { //비어잇지않으면
Coin coin = coinBox.pop();
System.out.println("꺼내온 동전: " + coin.getValue() + "원");
}}}15.6.2 Queue
Queue 인터페이스는 FIFO자료구조에서 사용되는 메소드를 정의하고 있다.
주요메소드
boolean offer(E e) 주어진객체를 큐에 넣는다.
E poll() 큐에서 객체를 빼낸다.
Queue인터페이스를 구현한 대표적인 클래스는 LinkedList이다. 그렇기때문에 LinkedList객체를 Queue인터페이스 변수에 다음과 같이 대입할 수있다.
Queue<E> queue = new LikedList<E>();
Queue<E> queue = new LikedList<>();
저장된 순서대로 가져옴
public class Message {
public String command;
public String to;
public Message(String command, String to) {
this.command = command;
this.to = to;
}}public class QueueExample {
public static void main(String[] args) {
// Queue 컬렉션생성
Queue<Message> messageQueue = new LinkedList<>();
//메세지 넣기
messageQueue.offer(new Message("sendMail", "홍길동"));
messageQueue.offer(new Message("sendSMS", "신용권"));
messageQueue.offer(new Message("sendKakaotalk", "강자바"));
//메시지를 하나씩 꺼내어 처리
while(!messageQueue.isEmpty()) {
Message message = messageQueue.poll(); //offer된 순서대로 poll이 된다.
switch(message.command) {
case "sendMail":
System.out.println(message.to + "님에게 메일을 보냅니다.");
break;
case "sendSMS":
System.out.println(message.to + "님에게 SMS를 보냅니다.");
break;
case "sendKakaotalk":
System.out.println(message.to + "님에게 카카오톡 보냅니다.");
break;
}}}}15.7 동기화된 컬렉션
컬렉션프레임워크 대부분의 클래스들은 싱글스레드환경에서 사용할수 있도록 설계되었다.
Vector와 Hashtable은 동기화된 메소드로 구성되어 있기때문에 멀티스레드 환경에서 안전하게 요소를 처리할 수 있지만.
다른것들은 구성되지않아 멀티스레드환경에서 안전하지 않다.
컬렉션을 처음부터 만들면 vector나 hashtable로 만들겠지만
리턴값으로 List Map을 받았는데 다른 동기화가 안된 ArrayList HashSet HashMap 등 컬렉션을 리턴받았을때 동기화된 컬렉션으로 바꾸어서 할필요가 있다.
비동기화된 메소드를 동기화된 메소드로 래핑하는 Collections의 synchronizedXXX()메소드를 제공한다.
List<T> list = Collections.synchronizedList(new ArrayList<E>());
Set<E> set = Collections.synchronizedSet(new HashSet<E>());
Map<K, V> map = Collections.synchronizedMap(new HashMap<K , V>());
public class SynchronizedMapExample {
public static void main(String[] args) {
// Map 컬렉션 생성
Map<Integer, String> map = Collections.synchronizedMap(new HashMap<>());
//작업스레드 객체 생성
Thread threadA = new Thread() {
@Override
public void run() {
//객체 100개추가
for(int i=1 ; i <=1000 ; i++) {
map.put(i, "내용"+i);
}
}
};
//작업스레드 객체 생성
Thread threadB = new Thread() {
@Override
public void run() {
//객체 100개추가
for(int i=1001 ; i <=2000 ; i++) {
map.put(i, "내용"+i);
}
}
};
//작업스레드 시행
threadA.start();
threadB.start();
//작업스레드들이 모두 종료될때까지 메인 스레드를 기다리게함.
try {
threadA.join();
threadB.join();
} catch (Exception e) {}
//저장된 총객체수 읽기
int size = map.size();
System.out.println("총 객체 수:" + size);
}}15.8 수정할 수 없는 컬렉션
컬렉션이란 요소 추가 삭제할 수 있는데 이것을 못하게 하는것이다.
첫번째 방법은 정적메소드인 of()로 생성할 수 있다.
List<E> immutabeList = List.of(E...element);
Set<E> immutabeSet = Set.of(E...element);
Map<K,V> immutabeMap = Map.of(K k1, V v1, K k2, V v2 ...);
두번째 방법은 List Set Map인터페이스의 정적메소드인 copyOf()를 이용해 기존 컬렉션을 복사하여 수정할 수 없는 컬렉션을 만드는것이다.
List<E> immutabeList = List.copyOf(Collection<E> coll);
Set<E> immutabeSet = Set.copyOf(Collection<E> coll);
Map<K,V> immutabeMap = Map.copyOf(Map<K,V> map);
세번째 방법은 배열로부터 수정할 수 없는 List컬렉션을 만들수있다.
String[] arr = { "A" , "B" ,"C"}
List<String> immutableList = Arrays.asList(arr);
다 자주 사용한다.
프로젝트에선 거의 수정할수있는 컬렉션으로 만듬.
ArrayList를 DTO형태로 보내기 때문에 대부분 읽기만 가능하게 보냄.
수정할수있어도 읽기전용으로 불러들이는 경우가 더 많음.
가끔씩 나오는 코드이기때문에 알아두면 좋다.
public class ImmutableExample {
public static void main(String[] args) {
//List 불변 컬렉션 생성
List<String> immutableList1 = List.of("A", "B", "C");
//immutableList1.add("D"); (x)
//Set 불변 컬렉션 생성
Set<String> immutableSet1 = Set.of("A", "B", "C");
//immutableSet1.remove("A"); (x)
//Map 불변 컬렉션 생성
Map<Integer, String> immutableMap1 = Map.of(1, "A", 2, "B", 3, "C");
//immutableMap1.put(4, "D"); (x)
//List 컬렉션을 불변 컬렉션으로 복사
List<String> list = new ArrayList< >();
list.add("A");
list.add("B");
list.add("C");;
List<String> immutableList2 = List.copyOf(list);
//Set 컬렉션을 불변 컬렉션으로 복사
Set<String> set= new HashSet< >();
set.add("A");
set.add("B");
set.add("C");;
Set<String> immutableSet2 = Set.copyOf(set);
//Map 컬렉션을 불변 컬렉션으로 복사
Map<Integer, String> map = new HashMap< >();
map.put(1, "A");
map.put(2, "B");
map.put(3, "C");
Map<Integer, String> immutableMap2 = Map.copyOf(map);
//배열로부터 List 불변 컬렉션 생성
//배열 래핑해서 List만드니까 리스트 밑 배열이라 새로 추가 불가능
String[] arr = { "A", "B", "C" };
List<String> immutableList3 = Arrays.asList(arr);
}}2022.12.19
컬렉션 자료구조는 매우 많이 사용하기때문에 잘 알아둘 필요가 있을 것 같다.
중요한 것은 꺾이지 않는 마음.