들어가기 전에 헷갈리지 말 것!
Collection은 인터페이스, Collections는 클래스**!!!!
과장 조금 보태서 책 부피의 0.3 정도는 이 단원이 차지하지 않을까 하는 생각을 한다. 그만큼 우리가 개발할 때 자주 사용하는 프레임워크이다! 그렇기에 더욱 무엇인지 잘 알아야 한다고 생각하고!
자바는 Java API(패키지)를 통해 개발자들이 자바 Application을 쉽게 개발할 수 있도록 돕는다. 따라서 개발 전에 어떤 패키지를 사용하면 좋을 지 찾아보고 생각해 볼 필요가 있다.
책에서는 3가지 그룹으로 자주 사용되는 클래스를 나눠주었다.
(java.util 패키지 중심)
= 데이터 군(group)을 저장하는 클래스(ex. 벡터)들을 표준화한 설계
JDK 1.2부터 컬렉션 프레임워크를 통해 다양한 종류의 컬렉션 클래스를 표준화된 방식으로 다룰 수 있도록 체계화 됨
컬렉션 = 다수의 데이터 / 프레임워크 = 표준화된 프로그래밍 방식
List와 Set은 공통된 부분이 맣아 이들을 다시 뽑아 Collection 인터페이스를 정의할 수 있었으나, Map 인터페이스는 전혀 다른 형태의 집합을 다루기 때문에 같은 상속 계층도에 포함되지 못한다.
Collection Framework의 모든 클래스들은 List, Set, Map 중의 하나를 구현하고 있고 그 이름 안에 위 세가지의 이름이 포함돼 있어 이름만으로도 클래스의 특징을 파악할 수 있다.
(Vector, Stack 등은 이 프레임워크가 생기기 이전부터 존재했으므로 위의 명명법을 따르지 않음)
add(Object o)
, addAll(Collection c)
clear
contains(Object o)
, containsAll(Collection c)
equals(Object o)
hashCode()
isEmpty()
iterator()
Iterator라는 단어가 익숙치 않아 실제로 어떻게 사용되는지 보았다.
LinkedList<Integer> list = new LinkedList<Integer>();
list.add(1);
list.add(2);
Iterator<Integer> iter = list.iterator(); // 요기!!
while (iter.hasNext()) {
System.out.print(iter.next() + " ");
}
remove(Object o)
, removeAll(Collection c)
후자는 지정된 컬렉션에 포함된 객체들을 모두 삭제
retainAll(Collection c)
지정된 컬렉션에 포함된 객체'만' 남기고 나머지는 컬렉션에서 삭제
이 메서드로 인해 변화가 있다면 true 반환 (아니면 false)
size()
toArray()
, toArray(Object[] a)
후자는 지정된 배열에 컬렉션의 객체를 저장하여 반환
중복을 허용하며 저장순서가 유지되는 컬렉션을 구현하는데 사용
List <- Vector <- Stack
<- ArrayList
<- LinkedList
listIterator()
LinkedList<Integer> lnkList = new LinkedList<Integer>();
ListIterator<Integer> iter = lnkList.listIterator();
while (iter.hasPrevious()) {
System.out.print(iter.previous() + " ");
}
set(int index, Object element)
subList(int fromIndex, int toIndex)
Set <- SortedSet(인터페이스) <- TreeSet
<- HashSet
Map <- Hashtable
<- HashMap <- LinkedHashMap
<- SortedMap <- TreeMap
clear()
entrySet()
remove(Object key)
values()
는 반환타입이 Collections이고, keySet()
은 반환타입이 Set이다. 두 반환타입이 다른 이유는 중복 여부에 있음!!public interface Map {
interface Entry {
Object getKey();
Object getValue();
Object setValue(Object value);
boolean equals(Object o);
int hashCode();
}
}
멀티스레드 프로그래밍에서는 하나의 객체를 여러 스레드가 동시에 접근할 수 있기 때문에 데이터 일관성 유지를 위해 동기화
가 필요하다!
구버전(JDK1.2 이전)의 클래스(ex. Vector, Hashtable)들은 자체적으로 동기화 처리가 돼있지만, 멀티스레드가 아닌 경우에는 불필요한 기능이므로 성능이 떨어짐
-> 새로 추가된 컬렉션은 동기화를 자체적으로 처리 하지 X
대신 필요한 경우에만 동기화 메서드를 이용해 처리하도록 함!
-> 동기화 메서드 리스트
- static Collection synchronizedCollection(Collection c)
- static List synchronizedList(List list)
- static Map synchronizedMap(Map m)
- static Set synchronizedSet(Set s)
- static sortedMap synchronizedSortedMap(SortedMap m)
- static SortedSet synchronizedSortedSet(SortedSet s)
ex. List list = Collections.synchronizedList(new ArrayList<>());
ex.
public class Vector extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable {
...
protected Object elementData[];
//자손클래스가 데이터를 저장해야 하므로 protected로 접근 제한!
...
}
Vector()
Vector(int initialCapacity)
void copyInto(Object[] anArray)
void ensureCapacity(int minCapacity)
trimToSize()
import java.util.*;
class VectorEx1 {
public static void main(String[] args) {
Vector v = new Vector(5);
v.add("1");
v.add("2");
v.add("3");
print(v);
v.trimToSize();
System.out.println("===After trimToSize() ===");
print(v);
v.ensureCapacity(6);
System.out.println("===After ensureCapacity(6) ===");
print(v);
v.setSize(7);
System.out.println("===After setSize(7) ===");
print(v);
v.clear();
System.out.println("===After clear() ===");
print(v);
}
public void print(Vector v) {
System.out.println(v);
System.out.println("size : "+v.size());
System.out.println("capacity: "+v.capacity());
}
}
trimToSize()
를 호출하면 v의 빈 공간이 사라진다. 그러나 배열은 크기를 변경할 수 없어 새로운 배열을 생성해 그 주소값을 v에 할당시킨다. 원래의 v 인스턴스는 사용되지 않아 추후 GC에 의해 제거된다.ensureCapacity()
는 최소한 크기가 6이 되도록 하여 새로운 인스턴스를 생성하여 v의 3개 요소를 복사해온다.이를 위해 책에서는 Vector 클래스를 쉽게 재구성해 보았다.
import java.util.*;
public class MyVector implements List {
protected Objec[] data = null;
protected int capacity = 0;
protected int size = 0;
public myVector(int capacity) {
if(capacity < 0) throw new IllegalArgumentException("유효하지 않은 값입니다: "+capacity);
this.capacity = capacity;
data = new Object[capacity];
}
public MyVector(int capacity) {
this(10);
}
public void ensureCapacity(int minCapacity) {
int newCapacity = capacity;
if(minCapacity > capacity) newCapacity = 2 * capacity;
if(minCapacity > newCapacity) newCapacity = minCapacity;
setCapacity(newCapacity);
}
public Object remove(int index) {
Object oldObj = null;
if(index < 0 || index >= size)
throw new IndexOutOfBoundsException("범위를 벗어났습니다.");
oldObj = data[index];
if(index != size-1) {
System.arraycopy(data, index+1, data, index, size-index-1);
}
data[size-1] = null;
size--;
return oldObj;
}
}
//아마 적다가 도중에 그만둔 것 같은데 책 있으면 다시 적어보기!!
그럼 배열의 단점이 무엇?
크기를 변경할 수 없다.
-> 변경할 시에는 새로운 배열을 생성하고 데이터 복사
(비용이 많이 들고, 만약 크기를 넓혀도 낭비가 심해짐)
비순차적인 데이터의 추가, 삭제에 시간이 많이 걸린다.
(데이터 추가 or 삭제를 위해, 많은 데이터를 옮겨야 함)
참조변수만 복사하는 것
을 의미한다. 따라서 같은 주소값의 변수를 가리키기 때문에 원본이 변화하면 새로운 복사본도 변경된다. 깊은 복사 보다 빠르지만 포인터나 참조를 다루기에 게으르다(?)같은 내용의 새로운 객체를 생성
하기 때문에 원본이 바뀌어도 복사본과는 관련이 없어진다.LinkedList
도 마찬가지로 배열의 단점을 보완한 클래스이다. 불연속적으로 존재하는 데이터를 연결한다.
class Node {
Node next;
Object obj;
}
[삭제]
[추가]
head
를 가리킴ListIterator listIterator(int index)
지정된 index에서 시작하는 ListIterator 반환
Object set(int index, Object element)
주어진 index의 값을 element로 바꿔줌
ArrayList
LinkedList
ArrayList
가 더 빠름empty()
peek()
pop()
push(E item)
search(Object o)
Deque
인터페이스 및 구현에 의해 제공됨 (Stack 클래스보다 우선적으로 사용해야 하는)Deque<Integer> stack = new ArrayDeque<Integer>();
Queue
는 인터페이스만 존재하고 클래스가 없어 이를 구현할 클래스(ex. LinkedList)를 사용해야 함Deque
, PriorityQueue
가 그 예시poll()
remove()
와의 차이 / remove()
는 NoSuchElementException
을 발생시킴)offer()
add()
대신 사용)세가지 모두 Collection 클래스에 저장된 데이터를 접근하는데 사용하는 인터페이스
hasMoreElements()
nextElement()
iterator()
를 호출하여 이를 구현한 객체를 얻음hasNext()
next()
remove()
hasNext()
next()
remove()
hasPrevious()
previous()
set(Object)
add(Object)
//정리해보는 Iterator 예시
public class IteratorEx {
public static void main(String[] args) {
ArrayList original = new ArrayList(10);
ArrayList copy1 = new ArrayList(10);
ArrayList copy2 = new ArrayList(10);
for(int i = 0; i < 10; i++) {
original.add(i+"");
}
Iterator it = original.iterator();
while(it.hasNext()) {
copy1.add(it.next());
}
System.out.println("original: "+original);
System.out.println("copy1: "+copy1);
System.out.println();
it = original.iterator();
while(it.hasNext()) {
copy2.add(it.next());
it.remove();
}
System.out.println("original: "+original);
System.out.println("copy2: "+copy2);
}
}
사실 이 단원은 내가 Set을 사용할 때 인덱스에 직접적으로 접근할 수 없어 리스트로 우회해서 사용하는 경우가 많았는데, iterator를 사용하면 더 편리할 것 같아서 읽는데 신기했다..
= Set 인터페이스를 구현한 Collection 클래스
LinkedHashSet
클래스를 활용하는 것이 좋음add()
를 통해 객체를 저장하고, 만약 중복된 객체를 저장한다면 false
를 반환equals()
와 hashCode()
를 호출하므로 적절히 오버라이딩 돼야 중복으로 간주하지 않음hashCode()
는 다음의 세가지 조건을 만족equals()
메서드를 이용한 비교에 의해 true를 얻은 객체에 대해 각각 hashCode()
를 호출해 얻은 결과는 반드시 같아야 함equals()
메서드를 호출했을 때 false를 반환하는 두 객체는 hashCode()
호출에 대해 같은 int값을 반환하는 경우가 있어도 괜찮으나, 해싱을 사용하는 컬렉션의 성능을 향상시키려면 다른 int값을 반환하는 것이 좋음Set s = Collections.synchronizedSet(new HashSet(..))
이렇게)add(E e)
, clear()
, clone()
, contains(Object o)
, isEmpty()
, size()
, remove(Object o)
iterator()
spliterator()
= 이진검색트리
라는 자료구조의 형태로 데이터를 저장하는 클래스
-> 왼쪽 자식노드의 값 < 부모노드의 값 < 오른쪽 자식노드의 값
-> 노드 추가/삭제에 시간 소요 ↑ (순차 저장 X)
-> 검색/정렬에 유리 (저장할 때 이미 정렬됨)
= 여러 개의 노드가 서로 연결된 구조
-> 각 노드에 최대 2개의 노드 연결 가능
-> 루트로 불리는 하나의 노드에서부터 계속 확장 가능
class TreeNode {
TreeNode left;
Object element;
TreeNode right;
}
headSet(Object toElement)
retailAll(Collection c)
subSet(Object fromElement, Object toElement)
tailSet(Obejct fromElement)
야물딱진 호구마의 정리.txt
추가적으로 말하자면, 둘 모두 인터페이스
로, 객체들을 정렬 or 이진검색트리를 구성하는데 필요한 메서드를 정의함
public interface Comparator {
int compare(Object o1, Object o2);
boolean equals(Object obj);
}
public interface Comparable {
public int compareTo(Object o);
}
// 기억하면 좋을만한 예시
class ComparatorEx1 {
public static void main(String[] args) {
TreeSet set1 = new TreeSet();
TreeSet set2 = new TreeSet();
int[] score = {30, 50, 10, 20, 40};
for(int i = 0; i < score.length; i++) {
set1.add(new Integer(score[i]));
set2.add(new Integer(score[i]));
}
System.out.println("set1: "+set1);
System.out.println("set2: "+set2);
}
}
class Descending implements Comparator {
public int compare(Object o1, Object o2) {
if(o1 instanceof Comparable && o2 instanceof Comparable) {
Comparable c1 = (Comparable) o1;
Comparable c2 = (Comparable) o2;
return c1.compareTo(c2)*(-1);
//-1을 곱해 기본 정렬방식의 역으로 변경
}
}
}
둘 사이의 관계 = Vector <-> ArrayList의 관계
따라서 새로운 버전인 HashMap을 사용하는 것이 되도록이면 좋다
// HashMap이 데이터를 저장하는 방식
public class HashMap extends AbstractMap implements Map, Cloneable, Serializable {
transient Entry[] table;
...
static class Entry implements Map.Entry {
final Object key;
Object value;
...
}
}
유일
해야 한다!!entrySet()
HashMap(int initialCapacity, float loadFactor)
해시함수
를 이용해 데이터를 hash table
에 저장하고 검색하는 기법
Object 클래스에 정의된 hashCode()
를 해시함수로 사용함= HashMap의 구버전인 Hashtable을 상속받아 구현한 것
= (String, String)
의 형태로 저장 => 단순화된 Collection 클래스
loadFromXML(InputStream in)
storeToXML(OutputStream os, String comment)
좀 낯선 클래스라 예시를 좀 봐야겠다..
import java.util.*;
class PropertiesEx {
public static void main(String[] args) {
Properties prop = new Properties();
prop.setProperty("timeout", "30");
prop.setProperty("language", "kr");
prop.setProperty("size", "10");
prop.setProperty("capacity", "10");
Enumeration e = prop.propertyNames();
//왜 Iterator가 아닌 Enumeration을 사용했냐면
//Property도 Collection 프레임워크 이전의 버전
while(e.hasMoreElements()) {
String element = (String) e.nextElement();
System.out.println(element+"="+prop.getProperty(element));
}
}
}
** 생각해보니까 이 클래스 application.properties로 막 DB 연결하고 이럴 때 사용했던 것 같다!!
driver=org.postgresql.Driver url=jdbc:postgresql://localhost:8080/postgres
요런 식으로?
java.util.*
에서 위의 중요한 클래스 외에도 자주 쓰이는 알아두면 유용한 클래스들을 알아보자!
JDK 1.0 ~ => Date
JDK 1.1 ~ => Calendar (추상클래스)
// 1. Calendar -> Date
Calendar cal = Calendar.getInstance();
...
Date d = new Date(cal.getTimeInMills());
// 2. Date -> Calendar
Date d = new Date();
...
Calendar cal = Calendar.getInstance();
cal.setTime(d);
= Math.random()
외에 난수를 얻는 다른 방법
그러나 기능이 Math.random()과 별 다를바 없어 가능하면 전자를 사용하길 권한다.
next자료형()
setSeed(long seed)
import java.util.*;
class RandomEx {
public static void main(String[] args) {
Random rand = new Random(1);
Random rand2 = new Random(1);
System.out.pritnln("=rand=");
for(int i = 0; i < 5; i++) {
System.out.println(i+":"+rand.nextInt());
}
System.out.pritnln("=rand2=");
for(int i = 0; i < 5; i++) {
System.out.println(i+":"+rand2.nextInt());
}
}
}
//출력값을 보면 예시에서 rand와 rand2의 값이 같다!
//(같은 종자값을 사용하므로)
나에게 지금 가장 필요한 챕터가 아닐까,, 코테 풀 때 필요해,,
= 텍스트 데이터 중 원하는 조건과 일치하는 문자열을 찾아내기 위해 사용하는 것 (미리 정의된 기호와 문자를 이용해 작성한 문자열)
Pattern: 정규식 정의
Matcher: 정규식(패턴)을 데이터와 비교하는 역할
[정규식 정의 -> 데이터 분석 순서]
find()
도 해당 문자열이 존재하면 true 반환하는 메서드c[a-z]*
c[a-z]
c[a-zA-Z]
.*
c.
c.*
.*a.*
c.*t
[b|c].{2}
\\d{3,4}
= 입력소스(화면, 파일, 문자열)로부터 문자 데이터를 읽어오는데 도움을 줄 목적으로 추가된 클래스 (JDK 1.5~)
// JDK1.5이전
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String input = br.readLine();
// JDK1.5이후
Scanner in = new Scanner(System.in);
String input = in.nextLine();
// 예시
class ScannerEx {
public static void main(String[] args) {
scanner in = new Scanner(new File("data3.txt"));
int cnt = 0;
int totalSum = 0;
while(in.hasNextLine()) {
String line = in.nextLine();
Scanner in2 = new Scanner(line).useDelimiter(",");
int sum = 0;
while(in2.hasNextInt()) {
sum += sc2.nextInt();
}
totalSum += sum;
cnt++;
}
}
}
= 긴 문자열을 지정된 구분자(delimiter)를 기준으로 토큰이라는 여러 개의 작은 문자열로 잘라내는 데 사용
단 하나
의 문자만 사용할 수 있어 복잡한 형태의 구분자로 나누려면 Scanner
나 split()
를 활용하는 것이 좋음StringTokenizer
를 생성countTokens()
hasMoreTokens()
nextToken()
class StringTokenEx {
public static void main(String[] args) {
String data = "100,,,200,300";
String[] result = data.split(",");
StringTokenizer st = new StringTokenizer(data, ",");
for(int i = 0; i < result.length; i++) {
System.out.print(result[i]+"|");
}
System.out.println("개수: "+result.length);
int i = 0;
for(;st.hasMoreTokens();i++) {
System.out.print(st.nextToken()+"|");
}
System.out.println("개수:"+i);
}
}
//split()
//100|||200|300 (개수 5)
//StringTokenizer
//빈 문자열을 토큰으로 인식하지 X
//100|200|300 (개수 3)
읽고만 넘어가용~~
JDK1.5에서의 가장 큰 변화 중 하나로, 다룰 객체의 타입을 미리 명시함으로써 형변환을 하지 않게끔 만들어준다.
?
를 사용<E>
컬렉션 클래스<저장할 객체 타입> 변수명 = new 컬렉션클래스<저장할객체타입>();
<E>
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
//예시
class Student {
String name = "";
int ban;
int num;
Student(String name, int ban, int num) {
this.name = name;
this.ban = ban;
this.num = num;
}
}
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<>();
list.add(new Student("호구마", 7, 10);
list.add(new Student("김인하", 7, 1);
Iterator<Student> iterator = list.iterator();
//여기서 형변환을 시켜주지 않아도 됨!
while(iterator.hasNext()) {
Student s = iterator.next();
System.out.println(s.name);
}
}
<T>
와 Collections.sort()타입이 미리 정해져있기 때문에 instanceof
로 원하는 타입이 들어왔는지 확인하지 않아도 되고, 코드도 간편해진다.
<K,V>
Map은 키와 값 형태로 저장되므로 지정해줘야 할 타입이 K, V로 두개이다!
ex. HashMap<String, Student> map = new HashMap<String, Student>();
map.put("자바", new Student("자바왕", 1, 1, 100, 100, 100));
자바의 정석, 2nd Edition.
http://www.tcpschool.com/java/java_collectionFramework_iterator
https://opentutorials.org/module/1335/8857
https://applefarm.tistory.com/141
https://docs.oracle.com/javase/8/docs/api/java/util/Queue.html