BIgDecimal 이나 Wrapper 클래스들도 불변인데 이건 무엇을 의미할까? 특정 클래스의 인스턴스를 만드는 순간부터 이 값을 바꿀 수 없다는 것이다. 비슷하게 우리가 .of 함수를 사용해 만든 List.of()나 Map.of() 같은 모든 것들은 불변 리스트이다. 가변 리스트를 만들고 싶다면 ArrayList를 만들던가 LinkedList 혹은 Vector를 만들어야 한다.
List<String> wordsArrayList = new ArrayList<String>(words);
wordsArrayList = {Apple, Bat, Cat}
// 이제 이 안의 값들을 변경할 수 있다. 만약 LinkedList를 만들고 싶다면
List<String> wordsLinkedList = new LinkedList<String>(words);
wordsLinkedList = {Apple, Bat, Cat}
// Vector를 만들고 싶다면
List<String> wordsVector = new Vector<String>(words);
wordsVector = {Apple, Bat, Cat}
// 이것들의 장점은 값을 추가할 수 있다는 것이다 가령 ArrayList에 Dog란 값을 추가하려면
wordsArrayList.add("Dog");
wordsArrayList = {Apple, Bat, Cat, Dog}
// 이렇게 추가된것을 확인할 수 있고 나머지도 비슷한 방식으로 추가가 가능하다
ArrayList와 Vector의 경우에 사용되는 기본적 데이터 구조는 배열이다. 그러나 LinkedList의 경우에는 이용되는 데이터 구조가 LinkedList이다. 배열의 경우에는 값들이 서로 옆에 존재하여 값을 부르는 것은 굉장히 빠르지만 값을 변경하려면 복잡해진다. 가령 7번재있는 값을 제거한다면 7번째 이후에 존재 하는 값들을 전부 하나씩 왼쪽자리로 가져와야 한다는 것이다. 그러나 LinkedList의 경우에는 한 요소에서 다른 요소들의 참조가 들어 있으므로 값을 찾는 것은 느리지만 값을 변경하는 것은 간단하다. 예를 들어 다섯 번재 값을 찾으려면 첫 번재부터 참조해가면서 다섯 번재로 도달해야 한다. 이렇게 접근하는 방식은 느리다. 그러나 요소하나를 LinkList에서 지우려고 하면 단순히 그 요소를 지우고 그 전 요소가 참조하는 위치를 내가 지우고자하는 요소의 다음 요소로 바꾸기만 하면 완료된다. 여기서 이해해야 할 점은 ArrayList 와 Vector의 배경 데이터 구조는 배열이고 LinkedList의 데이터 구조는 LinkedList이다
Vector의 메소드들은 보면 대부분 동기화가 되어 있지만 ArrayList의 경우에는 그렇지 않다. 예를 들어 한 클래스 안에 25개의 동기화된 메소드들이 있다고 가정했을 때 동기화된 메소드들 안에서는 한 순간에 오직 하나의 스레드만 코드를 실행시킬 수 있는 것이다. 스레드 하나가 동기화된 메소드를 실행중일때 다른 스레드들은 그 스레드가 동기화된 메소드의 실행을 완료할때까지 기다릴 수 있다.
안전을 고려하여 중요하지 않다면 ArrayList 중요하다면 Vector를 사용하면 된다. 이것이 근본적인 차이다.
jshell> List<String> words = List.of("Apple", "Bat", "Cat");
words ==> [Apple, Bat, Cat]
jshell> List<String> wordsArrayList = new ArrayList<String>(words);
wordsArrayList ==> [Apple, Bat, Cat]
jshell> wordsArrayList.add("Dog");
$1 ==> true
jshell> wordsArrayList
wordsArrayList ==> [Apple, Bat, Cat, **Dog**]
jshell> wordsArrayList.add("Elephant");
$2 ==> true
jshell> wordsArrayList
wordsArrayList ==> [Apple, Bat, Cat, Dog, **Elephant**]
jshell> wordsArrayList.add(2, "Ball"); // 2번재 인덱스에 Ball추가
*jshell> wordsArrayList
wordsArrayList ==> [Apple, Bat, **Ball**, Cat, Dog, Elephant]
jshell> wordsArrayList.add("Ball"); //List에서는 중복도 가능
jshell> wordsArrayList
wordsArrayList ==> [Apple, Bat, Ball, Cat, Dog, Elephant, **Ball**]
jshell> List<String> newList = List.of("Yak", "Zebra");
newList ==> [Yak, Zebra]
jshell> wordsArrayList.addAll(newList); //newList전체를 추가할 수도 있다
$3 ==> true
jshell> wordsArrayList
wordsArrayList ==> [Apple, Bat, Ball, Cat, Dog, Elephant, Ball, **Yak**, **Zebra**]
안에 요소들을 변경해보자
jshell> wordsArrayList
wordsArrayList ==> [Apple, Bat, Ball, Cat, Dog, Elephant, Ball, Yak, Zebra]
jshell> wordsArrayList.set(6, "Fish"); // 6번재 인덱스를 Fish로 변경
$4 ==> "Ball"
jshell> wordsArrayList
wordsArrayList ==> [Apple, Bat, Ball, Cat, Dog, Elephant, **Fish**, Yak, Zebra]
jshell> wordsArrayList.remove(2); // 2번재 인덱스 제거
$5 ==> "**Ball**"
jshell> wordsArrayList
wordsArrayList ==> [Apple, Bat, Cat, Dog, Elephant, Fish, Yak, Zebra]
jshell> wordsArrayList.remove("Dog"); //Dog인스턴스 제거
$6 ==> true
jshell> wordsArrayList
wordsArrayList ==> [Apple, Bat, Cat, Elephant, Fish, Yak, Zebra]
jshell> wordsAr
jshell> List<String> words = List.of("Apple", "Bat", "Cat");
words ==> [Apple, Bat, Cat]
// 특정 요소들을 가지고 리스트를 초기화할 수 있는 가장 쉬운 방법
// List.of라는 정적메소드를 활용했다.
words.size() // 배열에서의 .length()처럼 List에서는 .size()라는 메소드가 있다.
3
// 배열에서는 길이 속성이 있고 ArrayList나 컬렉션에는 크기 속성이 있다.
// size()는 이 집합 속에 몇개의 요소들이 있는지 알아내는데 쓰인다.
words.isEmpty();// 비어있는지 확인
false
words.get(0); // 첫번째 인덱스에 있는 값 가져옴
// Apple
words.contains("dog"); // dog를 포함하고 있나?
false
words.indexOf("Cat"); // Cat이 어떤 인덱스에 있는지
2
words.indexOf("dog"); // 존재하지 않는 것의 인덱스를 물어보면
-1 // -1이 나옴
jshell> for(int i=0; i<words.size(); i++) {
...> System.out.println(words.get(i));
...> }
Apple
Bat
Cat
jshell> for(String word:words) {
...> System.out.println(word);
...> }
Apple
Bat
Cat
jshell> Iterator wordsIterator = words.iterator();
//Iterator는 리스트를 반복 할 수 있는 또 다른 방법이다
wordsIterator ==> java.util.AbstractList$Itr@3712b94
jshell> while(wordsIterator.hasNext()) {
...> System.out.println(wordsIterator.next());
...> }
Apple
Bat
Cat
at으로 끝나는 것들만 출력해보자
jshell> List<String> words = List.of("Apple", "Bat", "Cat");
words ==> [Apple, Bat, Cat]
jshell> List<String> wordsAL = new ArrayList<>(words);
wordsAL ==> [Apple, Bat, Cat]
jshell> for(String word:words) {//우리는 변경을 원하는게 아니므로 words를 사용할수 있다
...> if(word.endsWith("at"))
...> System.out.println(word);
...> }
...> }
Bat
Cat
at로 끝나는 문자열을 출력하는 경우에는 for문으로 하는것이 효과적이다 그런데 만약 at로 끝나는 것을 삭제하고 싶다면 어떻게 해야 될까? 기본적 List.of로는 변경할 수 없기 때문에 wordsAL을 사용할 것이다.
jshell> for(String word:wordsAL) {//변경(제거)하는 것이기 때문에 wordsAL 즉 ArrayList이용
...> if(word.endsWith("at"))
...> wordsAL.remove(word);
...> }
...> }
jshell> wordsAL
wordsAL ==> [Apple, Cat]
at로 끝나는 것들을 삭제하려고 하였으나 Cat이 남아있다. 제거의 경우에 향상된 for문을 사용하면 단어를 제거함으로써 반복이 어떻게 진행되느냐가 바뀔 수 있기 때문에 이런 경우에는 반복자를
가지고 하는 것이 좋다. for문이 진행되는 동시에 인덱스값이 바뀌기 때문에 오류가 생기는 것으로 이해했다.
반복자를 활용하여 이 문제를 해결해 보자
jshell> Iterator wordsIterator = wordsAL.iterator();
wordsIterator ==> java.util.AbstractList$Itr@3712b94
jshell> while(wordsIterator.hasNext()) {
...> if(wordsIterator.next().endsWith("at")){
...> wordsIterator.remove();
...> }
...> }
jshell> wordsAL
wordsAL ==> [Apple]
jshell>
일반적인 경우라면 for루프를 돌려도 문제가 없지만 삭제하는 경우라면 반복자를 사용하는 것이 좋다.
jshell> List values = List.of("A", 'A', 1, 1.0);
values ==> ["A", 'A', 1, 1.0]
jshell> values.get(2)
$1 ==> 1
jshell> values.get(2) instanceof Integer
$2 ==> true
// 우리가 넣어준 값은 Integer값이 아닌데 왜 이렇게 나오는 걸까?
// 중요한 것 중 하나는 리스트 안에는 기본자료형을 보관할 수 없다는 것이다. 우리는
// int 형 1이나 double형 1.0을 넣어줬는데 Wrapper클래스에서 배운 오토박싱이 된 것이다
// 리스트를 만들려 할 때 일어나는 일은 이것들이 다 오토박싱되어 래퍼 클래스가 생성되는 것이다.
jshell> values.get(1) instanceof Character
$3 ==> true
jshell> values.get(3) instanceof Double
$4 ==> true
그런데 내가 여러가지 종류가 아닌 한 가지 종류만 담고 싶다면 여기서 제네릭이 사용된다.
jshell> List<String> textValues = List.of("A", 'A', 1, 1.0);
| Error
| Error
| List<String> textValues = List.of("A", 'A', 1, 1.0);
|___________________________^------------------------^
// 문자열로 지정했지만 문자열이 아니기 때문에 당연히 오류가 발생한다
jshell> List<Integer> numbers = List.of(101, 102, 103, 104, 105);
numbers ==> [101, 102, 103, 104, 105]
jshell> numbers.indexOf(101)
$5 ==> 0
//indexOf 의 매개변수인 101을 오토박싱하여 요소를 찾고 인덱스인 0을 되돌려 준 것
numbers가 존재하는 또 다른 List를 만들어 보자
jshell> List<Integer> numbersAL = new ArrayList<>(numbers);
numbersAL ==> [101, 102, 103, 104, 105]
jshell> numbersAL.indexOf(101)
$6 ==> 0
그렇다면 101을 밑의 코드처럼 입력해서 삭제할 수 있을까?
jshell> numbersAL.remove(101)
java.lang.IndexOutOfBoundsException!!
indexOf() 메소드에서 일어나는 일은 오버로드된 메소드가 indexOf()를 위해서는 없다는 것이다.
객체를 받아들이는 단 한가지 메소드가 있기 때문이다. 우리가 하고자 한 것은 101은 Integer형으로
오토박싱 되었고, 우리는 Integer를 검색하려 했다. 하지만 우리가 remove() 메소드를 보면 객체를
매개변수로 받는 타입과 인덱스를 받는 타입이 있는데 객체를 받는 타입을 사용하고 101을 Integer
형으로 오토박싱하는 대신, 인덱스를 받는 타입과 연결되어서 101을 인덱스값으로 사용해버린 것
그래서 요소 101을 지우고 싶다면 아래처럼 해주어야 한다.
jshell> numbersAL.remove(Integer.valueOf(101))
$7 ==> true
jshell> numbersAL
numbersAL ==> [102, 103, 104, 105]
잘 지워진 것을 확인할 수 있다.
jshell> List<Integer> numbers = List.of(123, 12, 3, 45);
numbers ==> [123, 12, 3, 45]
//List.of은 불변 List이기 때문에 정렬을 하기 위해서 밑에 ArrayList로 Numbers값을
// 넣어 주겠다.
jshell> List<Integer> numbersAL = new ArrayList<>(numbers);
numbersAL ==> [123, 12, 3, 45]
jshell> numbersAL.sort();
| Error:
| required: java.util.Comparator<? super java.lang.Integer>
| numbersAL.sort();
|^-------------^
// 정렬을하려는데 비교자가 필요하다고 오류가 나온다
// 기본적으로 List Interface안의 정렬 메소드를 사용하고 싶다면 비교자를 사용해야 하는데
// 여기서는 다른 메소드로 진행 하겠다
jshell> Collections.sort(numbersAL);
jshell> numbersAL
numbersAL ==> [3, 12, 45, 123]
// Collections.sort() 에는 매개 변수로 정렬하고 싶은 ArrayList를 넣을 수 있다.
// Sort는 Collections안에 존재하는 정적메소드이기 때문에 이렇게 사용해줬다.
조금 더 복잡한 클래스에서 보자
package collections;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
// 우리는 Comparable을 통해 정렬이 가능하도록 만들었다. 그러나 상황에 따라 오름차순과
// 내림차순이 다르게 나온다면? 이 때 사용하는 것이 Comparator이다
//6.비교자를 구현해보자 Student 객체를 비교할 것이기 때문에 <Student> 를 붙여주었다
class DescendingStudentComparator implements Comparator<Student> {
@Override
public int compare(Student student1, Student student2) {
return Integer.compare(student2.getId(), student1.getId());
}
// 내림차순을 구현할 것이라 comparable에서 this that위치 바꾸면 반대로 나오는 것처럼
// student2,student1순으로 compare메소드에 넣어주었다
// 위의 형태를 클래스명만 바꾸고 새로 만들어서 여러가지를 정렬하는 형태로 마음대로 구현 가능
}
public class StudentCollectionRunner {
public static void main(String[] args) {
//1.학생들의 리스트를 만들어 준다.
List<Student> students = List.of(new Student(1, "Ranga"), new Student(100, "Addy"), new Student(2, "Eve"));
//2.우리는 이제 학생들의 리스트를 정렬하고 싶으므로 학생들의 ArrayList를 만든다. 즉 리스트를 변경하고 싶다는 뜻
ArrayList<Student> studentsAL = new ArrayList<>(students);
System.out.println(studentsAL);
//3.비교가능한 인터페이스 구현만 통과할수 있다고 sort쪽에 오류가 뜬다.
//Comparable인터페이스를 구현하는 클래스들만 통과시킬 수 있다.
// Integer 나 Double 클래스 들에서는 Comparable 인터페이스에서 Overriding이 되어있지만
// 지금의 경우에는 Overriding해줘야 한다.
//Comparable인터페이스를 Student클래스에 구현해야 한다.
Collections.sort(studentsAL);
System.out.println("Asc" + studentAL);
// Comparable 을 이용한 오름차순 정렬
Collections.sort(studentsAL, new DescendingStudentComparator());
System.out.println("des" + studentsAL);
// Comparator 를 이용한 내림차순 정렬
}
}
package collections;
//4. Comparable을 추가하면 Student에 오류가 뜨는데 이는 Comparable인터페이스에
//구현되지 않은 메소드가 있기 때문이다. 컨트롤1을 해줘서 메소드를 구현해준다.
public class Student implements Comparable<Student> {
private int id;
private String name;
public Student(int id, String name) {
super();
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return id + " " + name;
}
//5.this은 현재 객체고 that은 우리가 비교하려는 객체이다. 반대로 뒤집고 싶으면
//compare안 this와 that의 위치를 바꿔주면 된다.
@Override
public int compareTo(Student that) {
return Integer.compare(this.id, that.id);
// Integer.compare() 메소드는 x < y 일때 -1 출력, x == y 일때 0 출력, x > y 일때 1을 출력
}
}
jshell> Set<String> set = Set.of("Apple", "Banana", "Cat");
set ==> [Banana, Apple, Cat]
jshell> set.add("Apple"); //set은 중복이 불가하고 기본적으로 변경 허용안한다.
| java.lang.UnsupportedOperationException thrown: //오류
jshell> Set<String> hashSet = new HashSet<>(set);
hashSet ==> [Apple, Cat, Banana]
// 우리가 입력한 순서와는 다르게 출력 되는걸 볼 수 있다. Set은 요소의 위치에 신경 쓰지 않는다
hashSet.add("Apple");
// false Apple이 이미 들어가 있기 때문에 fasle가 나오는 것
hashSet.add(2, "Apple"); //에러
// 고로 Set은 위치에 기반해 더하거나 제거하지 못한다. Set 은 유일한 값들을 저장하는데 쓰인다
HashTable은 매우 빠른 검색구조를 제공한다. 요소의 삽입은 때때로 LinkedList보다 느릴 수 있지만 배열에 비해서는 훨씬 빠르다. HashTable의 효율성은 언제나 우리의 해싱함수의 효율성에 기반한다. 자바에서는 해싱함수를 해시코드를 이용해 구현한다.
Tree의 대단한 점은 요소의 순서를 정해 저장하는데 도움을 준다는 것이다.
// 1.hashset을 만들고 숫자를 넣어주었다.
jshell> Set<Integer> numbers = new HashSet<>();
numbers ==> []
jshell> numbers.add(765432);
$1 ==> true
jshell> numbers.add(76543);
$2 ==> true
jshell> numbers.add(7654);
$3 ==> true
jshell> numbers.add(765);
$4 ==> true
jshell> numbers.add(76);
$5 ==> true
jshell> numbers
numbers ==> [765432, 7654, 76, 765, 76543]
// 무작위 순서인것을 확인 가능하다. 즉 삽입 순서대로 저장되지 않는다는 것.
jshell> Set<Integer> numbers = new LinkedHashSet<>();
numbers ==> []
jshell> numbers.add(765432);
$1 ==> true
jshell> numbers.add(76543);
$2 ==> true
jshell> numbers.add(7654);
$3 ==> true
jshell> numbers.add(765);
$4 ==> true
jshell> numbers.add(76);
$5 ==> true
jshell> numbers
numbers ==> [765432, 76543, 7654, 765, 76]
// LinkedHastSet은 요소들을 삽입 순서대로 보관한다. 정렬된 순서대로 저장되는것이 아니다.
jshell> numbers.add(7654321);
$5 ==> true
jshell> numbers
numbers ==> [765432, 76543, 7654, 765, **7654321**]
jshell>
// 하지만 Set이기 때문에 중복된 값을 가질수는 없다.
// TreeSet으로 만들어보자
jshell> Set<Integer> numbers = new TreeSet<>();
numbers ==> []
jshell> numbers.add(765432);
$1 ==> true
jshell> numbers.add(76543);
$2 ==> true
jshell> numbers.add(7654);
$3 ==> true
jshell> numbers.add(765);
$4 ==> true
jshell> numbers.add(76);
$5 ==> true
jshell> numbers
numbers ==> [76, 765, 7654, 76543, 765432]
// TreeSet에서는 정렬된된 순서로 저장한다.
jshell> numbers.add(7);
$5 ==> true
jshell> numbers
numbers ==> [**7**, 76, 765, 7654, 76543, 765432]
jshell>
package collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
public class SetRunner {
// 유일한 것을 원하면 Set을 써야한다 그리고 그 순간 내려야 할 결정은 삽입 순서를 유지할지
//정렬 순서를 유지할지 정하는 것이다.
public static void main(String[] args) {
List<Character> characters = List.of('A', 'Z', 'A', 'B', 'Z', 'F');
Set<Character> treeSet = new TreeSet<>(characters);
System.out.println(treeSet);// [A, B, F, Z] 정렬되어서 나온다
Set<Character> linkedhashset = new LinkedHashSet<>(characters);
System.out.println(linkedhashset);// [A, Z, B, F] 입력한대로 중복제외 나옴
Set<Character> hashset = new HashSet<>(characters);
System.out.println(hashset);// [A, B, F, Z] 넣은 순서나 정렬에 관계없이 나옴
}
}
jshell> TreeSet<Integer> numbers = new TreeSet<>(Set.of(65, 54, 34, 12, 99));
numbers ==> [12, 34, 54, 65, 99]
jshell> numbers.floor(40); // 40 포함 40 밑
$1 ==> 34
floor() method is inclusive, lower() is exclusive
jshell> numbers.floor(34); // 34 포함 34 밑
$2 ==> 34
jshell> numbers.lower(34); // 34 미만
$3 ==> 12
ceiling() method is inclusive, higher() is exclusive
jshell> numbers.ceiling(36); // 36 포함 위
$4 ==> 54
jshell> numbers.ceiling(34); // 34 포함 위
$5 ==> 34
jshell> numbers.higher(34); // 34 초과
$6 ==> 54
jshell> numbers.subSet(20, 80); // 20 80 사이
$7 ==> [34, 54, 65]
jshell> numbers.subSet(34, 54); // 34 이상 54 미만
$8 ==> [34]
jshell> numbers.subSet(34, 65); 3// 4 이상 65 미만
$9 ==> [34, 54]
jshell> numbers.subSet(34, true, 65, true); // 34 65포함 사이
$10 ==> [34, 54, 65]
jshell> numbers.subSet(34, false, 65, false); // 34 65제외하고 사이
$11 ==> [54]
jshell> numbers.headSet(50); // 50 아래 전부
$12 ==> [12, 34]
jshell> numbers.tailSet(50); // 50 위 전부
$13 ==> [54, 65, 99]
jshell>
Queue는 작업하고 싶은 순서대로 정렬할 때 쓰인다. PriorityQueue 에서는 기본적으로 우리가 지정한 순서대로 정렬돼 있다. 하지만 나주에 임의의 비교자 구현을 통해 이 순서를 바꿀 수 있다.
jshell> Queue<String> queue = new PriorityQueue<>();
numbers ==> [12, 34, 54, 65, 99]
jshell> queue.poll();
$1 ==> null
// Queue에서 요소를 꺼낼 수 있는 메소드는 .poll()이고 큐가 빈값이기 때문에 null 반환
jshell> queue.offer("Apple"); //요소 하나 추가
$2 ==> true
jshell> queue.addAll(List.of("Zebra", "Monkey", "Cat")); // 여러 요소 추가
$3 ==> true
jshell> queue
queue ==> [Apple, Cat, Monkey, Zebra] // 알파벳 순으로 정렬됨
jshell> queue.poll();
$4 ==> "Apple"
jshell> queue
queue ==> [Cat, Monkey, Zebra]
jshell> queue.poll();
$5 ==> "Cat"
jshell> queue.poll();
$6 ==> "Monkey"
jshell> queue.poll();
$7 ==> "Zebra"
jshell> queue.poll();
$8 ==> null // 하나씩 빠져서 결구 빈 상태가 됨
// 전시회나 영화관에서 줄 서는 것처럼 작동한다. 요소의 우선순위를 지정할 수 있다는 차이점이 있다.
package collections;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;
//2.Comparator을 이용해주고 StringLen..에서 미구현 메소드 추가해준다
class StringLengthComparator implements Comparator<String> {
//3.오름차순으로 나오게끔 만들어 주었다
@Override
public int compare(String value1, String value2) {
return Integer.compare(value1.length(), value2.length());
}//순서를 반대로 바꾸고 싶다면 Integer.compare안에 있는 두 인수의 자리를 바꾸어 주면 된다.
}
public class QueueRunner {
public static void main(String[] args) {
Queue<String> queue = new PriorityQueue<>();
queue.addAll(List.of("Zebra", "Monkey", "Cat"));
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll()); // Cat Monkey Zebra null 자연스러운 순서대로 나온다
//1.문자열 길이 기준 짧은 것 부터 처리한다고 가정해보자
Queue<String> queue1 = new PriorityQueue<>(new StringLengthComparator());
queue1.addAll(List.of("Zebra", "Monkey", "Cat"));
System.out.println(queue1.poll()); //cat
System.out.println(queue1.poll()); //zebra
System.out.println(queue1.poll()); //monkey
System.out.println(queue1.poll()); //null 문자열이 짧은 순으로 나온다.
}
}
Map인터페이스의 제일 중요한 점은 Collection 인터페이스를 연장하지 않는다는 것이다. 그렇기 때문에 Collection과 관련된 모든 작업이 맵에선 적용되지 않는다. 즉 Map은 Collection 프레임워크의 일부지만 Collectrion 인터페이스를 구현하지 않는다. 맵은 Key - Value를 쌍으로 저장하는데 쓰인다. 예를 들어 A,C,A,C,E,C,M,D,H,A 이런 문자들이 있을 때 A는 몇개인지 C는 몇개인지 저장한다면 {(”A”,3), (”C”,3)}를 입력한다. 이런 상황에 Map를 쓴다.
HashMap을 이루는 데이터 구조는 HashTable 이기 때문에 HashMap 안에서는 분류되지 않고 순서도 없다.
HshTable 과 HashMap의 차이는 무엇일까? HashTable 또한 해싱 기법을 데이터 구조에 사용한다. 즉 연산에 관해서는 두 가지의 차이가 없다는 뜻이다. 하지만 HashTable은 Vector와 비슷하게 모든 메소드들이 동기화 되어있다. 그렇기 때문에 HashMap보다 더 안전하다. 또 다른 차이점은 HashMap은 HashTable과는 다르게 Key값을 null값과 저장할 수 있게 해준다는 것이다.
LinkedHashMap은 LinkedHashSet과 비슷하게 삽입순서가 유지된다. 그러나 정렬되지 않았고 삽입 순서이기 때문에 HashMap에 비해서 삽입과 제거가 조금 느리다. 하지만 요소간에 연결이 되어있어 요소를 도는 반복은 훨씬 빠르다.
TreeMap은 기반 데이터 구조가 Tree이기 때문에 데이터는 정렬된 순서로 저장된다. 그리고 평소처럼, 트리가 존재할 때는 데이터가 정렬되어 있기 때문에 우리는 이 특정 인터페이스만이 아닌 다른 인터페이스도 구현한다. Navigable Map이라는 인터페이스이다.
맵을 만드려면 두 가지 다른 종류를 입력해야 한다. 열쇠의 종류가 무엇인지와 값의 종류가 무엇인지 열쇠부분에 사용할 건 문자열이고 값은 정수이다. 한 문자열에서 특정 문자가 몇 번이나 발생하는지를 저장하고 싶다고 가정하자
jshell> Map<String, Integer> map = Map.of("A", 3, "B", 5, "Z", 10);
map ==> {Z=10, A=3, B=5}
다른 경우에도 그렇듯이, .of를 사용하면 값을 삽입할 수 없다. 기본적으로 of을 이용해 만들면 불변
jshell> map.get("Z"); Z에 있는 값을 보자
$1 ==> 10
jshell> map.get("A");
$2 ==> 3
jshell> map.get("C");
$3 ==> null
jshell> map.size(); // 다른 Collection과 비슷하게 size등이 있다.
$4 ==> 3
jshell> map.isEmpty();
$5 ==> false
jshell> map.containsKey("A"); A를key를 포함하고 있나
$6 ==> true
jshell> map.containsKey("F");
$7 ==> false
jshell> map.containsValue(3); 3이란 값을 가지고 있나
$8 ==> true
jshell> map.containsValue(4);
$9 ==> false
jshell> map.keySet(); 데이터 구조와 Key의 Collection만 가져온다
$10 ==> [Z, A, B]
jshell> map.values();
$11 ==> [10, 3, 5]
jshell
hashmap으로 맵 내의 데이터를 다뤄 보자.
jshell> Map<String, Integer> map = Map.of("A", 3, "B", 5, "Z", 10);
map ==> {Z=10, A=3, B=5}
jshell> Map<String, Integer> hashMap = new HashMap<>(map);
hashMap ==> {A=3, Z=10, B=5}
jshell> hashMap.put("F", 5);
$1 ==> null 안에 f가 없었기 때문에 null이 나왔다
jshell> hashMap
hashMap ==> {A=3, Z=10, B=5, F=5}
jshell> hashMap.put("Z", 11);
$2 ==> 10 원래 z자리에 있던 10이 반환되고 11이 입력된 것
jshell> hashMap
hashMap ==> {A=3, Z=11, B=5, F=5}
jshell>
jshell> HashMap<String, Integer> hashMap = new HashMap<>();
hashMap ==> {}
jshell> hashMap.put("Z", 5);
$1 ==> null
jshell> hashMap.put("A", 15);
$2 ==> null
jshell> hashMap.put("F", 25);
$3 ==> null
jshell> hashMap.put("L", 250);
$4 ==> null
jshell> hashMap
hashMap ==> {A=15, F=25, Z=5, L=250}
// hashmap은 정렬된 순서도, 삽입된 순서도 아님 하지만 HashMap에서 순서는 상관이 없기 때문에
// 효과적이다. 우리가 순서가 상관없는 값들을 다룬다면 HashMap은 완벽한 데이터 구조이다.
jshell> LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<>();
linkedHashMap ==> {}
jshell> linkedHashMap.put("Z", 5);
$5 ==> null
jshell> linkedHashMap.put("A", 15);
$6 ==> null
jshell> linkedHashMap.put("F", 25);
$7 ==> null
jshell> linkedHashMap.put("L", 250);
$8 ==> null
jshell> linkedHashMap
hashMap ==> {Z=5, A=15, F=25, L=250}
// LinkedHashMap은 삽입 순서를 저장한다.
jshell> TreeMap<String, Integer> treeMap = new TreeMap<>();
treeMap ==> {}
jshell> treeMap.put("Z", 5);
$9 ==> null
jshell> treeMap.put("A", 15);
$10 ==> null
jshell> treeMap.put("F", 25);
$11 ==> null
jshell> treeMap.put("L", 250);
$12 ==> null
jshell> treeMap
treeMap ==> {A=15, F=25, L=250, Z=5}
jshell>
// Treemap은 정렬된 순서대로 나온다.
toCharArray()
// 는 문자열을 한 글자씩 쪼개서
// 이를 char타입의 배열에 집어넣어주는 친절한 메소드이다.
// String(문자열)을 char형 배열로 바꾼다.
String s1 = "Hello World";
char[] charArr = s1.toCharArray();
// 추가로, char형 배열을 합쳐서 하나의 String(문자열)로 만들 수 있다.
String s2 = new String(charArr);
char을 이용한 방법
import java.util.HashMap;
import java.util.Map;
public class MapRunner {
public static void main(String[] args) {
String str = "This is an awesome occasion. " + "This has never happend before";
//1.문자열을 만들어 주었고 이 특정한 문자열에서 각 글자가 몇 번이나 있는지 알아내려고 한다
Map<Character, Integer> occurances = new HashMap<>();
char[] characters = str.toCharArray();
for (char chars : characters) {
// 글자를 가져오고
Integer integer = occurances.get(chars);
if (integer == null)// null이면 존재하지 않는다는 것
{
occurances.put(chars, 1);// 초기화
} else {
occurances.put(chars, integer + 1);
}
// 기존에 글자가 있으면 값을 늘려준다
// 기존에 글자가 없다면 1로 초기화
}
System.out.println(occurances);
}
}
String을 이용한 방법
package collections;
import java.util.HashMap;
import java.util.Map;
public class MapRunner {
public static void main(String[] args) {
String str = "This is an awesome occasion. " + "This has never happend before";
//1.문자열을 만들어 주었고 이 특정한 문자열에서 각 글자가 몇 번이나 있는지 알아내려고 한다
Map<String, Integer> stroccurances = new HashMap<>();
String[] words = str.split(" ");// 띄어쓰기를 제외한 단어끼리 모아줌
for (String word : words) {
// 글자를 가져오고
Integer integer = stroccurances.get(word);
if (integer == null)// null이면 존재하지 않는다는 것
{
stroccurances.put(word, 1);// 초기화
} else {
stroccurances.put(word, integer + 1);
}
// 글자가 있으면 값을 늘려준다
// 글자가 없다면 1로 초기화
}
System.out.println(stroccurances);
}
}
jshell> TreeMap<String, Integer> treeMap = new TreeMap<>();
collections
java.util.
treeMap ==> {}
jshell> treeMap.put("F", 25);
$1 ==> null
jshell> treeMap.put("Z", 5);
$2 ==> null
jshell> treeMap.put("L", 250);
$3 ==> null
jshell> treeMap.put("A", 15);
$4 ==> null
jshell> treeMap.put("B", 25)
$5 ==> null
jshell> treeMap.put("G", 25);
$6 ==> null
jshell> treeMap
treeMap ==> {A=15, B=25, F=25, G=25, L=250, Z=5} //정렬되어 나온다.
jshell> treeMap.higherKey("B"); 더 큰값이 나옴
$7 ==> "F"
jshell> treeMap.higherKey("C");
$8 ==> "F"
jshell> treeMap.ceilingKey("B"); 같거나 더 큰값이 나옴
$9 ==> "B"
jshell> treeMap.ceilingKey("C");
$10 ==> "F"
jshell> treeMap.lowerKey("B"); 낮은 값이 나옴
$11 ==> "A"
jshell> treeMap.floorKey("B"); 같거나 낮은 값이 나옴
$12 ==> "B"
jshell> treeMap.firstEntry(); 첫번째 정렬값
$713 ==> A=15
jshell> treeMap.lastentry(); 마지막 정렬값
$14 ==> Z=5
jshell> treeMap.subMap("C", "Y"); 왼쪽 이상 오른쪽 미만
$7 ==> {F=25, G=25, L=250}
jshell> treeMap.subMap("B", "Z");
$7 ==> {B=25, F=25, G=25, L=250}
jshell> treeMap.subMap("B", true, "Z", true); 왼쪽 이상 오른쪽 이하
$7 ==> {B=25, F=25, G=25, L=250, Z=5}
jshell>
Hash - 어떤 컬렉션의 이름에서 Hash를 보면 순서도 없고 정렬도 되어 있지 않다는 걸 기억해라.
Linked - 요소들이 서로 연결되어 있다는 것이다. 데이터를 정렬된 방식으로 저장하진 않지만 삽입 순서대로 데이터를 저장한다.
Tree - 데이터가 정렬된 순서로 저장된다는 것을 알 수 있다. Tree는 기본적으로 데이터가 정렬되어 있기 때문에 NavigableSet이나 NavigableMap 구현이 있을 것이다.