Java Collection (List,Stack,Queue,Set,Map)

Thomas·2023년 6월 1일
0
post-thumbnail

컬렉션

Java에서 컬렉션은 배열보다 다수의 참조형 데이터를 더 쉽게 효과적으로 처리할 수 있는 기능을 많이 가지고 있습니다.

컬렉션 기능

크기 자동조정/ 추가/ 수정/ 삭제/ 반복/ 순회/ 필터/ 포함확인 등...

컬렌션 종류

List, Set, Queue, Map

  • List: 순서가 있는 데이터의 집합(데이터 중복 허용) - 배열과 비슷
  • Queue: 한쪽에서 데이터를 넣고 반대쪽에서 데이터를 뺄수 있는 집합(FIFO- First In First Out)
  • Set: 순서가 없는 데이터의 집합(데이터 중복 허용 안함) - 순서없고 중복없는 배열
  • Map: 순서가 없는 (Key,Value) 쌍으로 이루어진 데이터의 집합 (Key값 중복 허용 안 함)
  • Stack: 상자안에 맨 처음 1번 책을 넣고 2,3,4책을 넣었으면 1번 책을 다시 꺼낼려면 2,3,4책을 다 꺼내야지 꺼낼수 있는 자료구죠 (FILO - First In Last Out)

Collection은 기본형 변수가 아닌 참조형 변수를 저장

List

ArrayList

순서가 있는 데이터의 집합

Array는 최초의 길이를 알아야하지만 List는 처음에 길이를 몰라도 만들 수 있다.

Array vs List(ArrayList)
1. Array: 정적배열
2. List(ArrayList): 동적배열(크기가 가변적으로 늘어난다)
- 생성 시점에 작은 연속된 공간을 요청해서 참조형 변수들을 담아 놓는다.
- 값이 추가될 때 더 큰 공간이 필요하면 더 큰 공간을 받아서 저장하니깐 상관없다.

ArrayList<Integer> intList = new ArrayList<Integer>(); // 선언 + 생성

/* 추가 */
intList.add(99);
intList.add(15);

System.out.println(intList.get(1)) // 첫번쨰 list를 출력


/* 수정 */
intList.set(1,10); // 2번째 있는 15를 10으로 바꾸기
System.out.println(intList.get(1)) // 첫번쨰 list를 출력

/* 삭제 */
intList.remove(0)
System.out.println(intList.get(0)) // 0번쨰 list를 출력

// 출력값:
// 15
// 10
// 15

/* 클리어 */
intList.clear();

ArrayList intList = new ArrayList();
여기서 Integer 참조 변수를 사용해야한다!!!

LinkedList

메모리에 남는 공간을 요청해서 여기 저기 나누어서 실제 값을 담기
실제 값이 있는 주소값으로 목록을 구성하고 저장하는 자료구조.

실습

LinkedList vs ArrayList

기본적인 기능은 동일하지만
LinkedList 값은 나누어져 있어서 조회하는 속도가 "느리다"
But 값을 추가하거나, 삭제할 떄는 ArrayList보다 "빠르다"

Stack

스택은 수직으로 값을 쌓아놓고, 넣었다가 빼는 자료구조이다.
FILO(First In Last Out)인셈이다.
주된 기능은 push,pop,peek,isEmpty이 있다.
스택은 주로 최근 저장된 데이터를 나열하고 싶거나, 데이터의 중복 처리를 막고 싶을 떄 사용한다.

Stack<Integer> intStack = new Stack<Integer>();

intStack.push(10);
  intStack.push(3);

// 다 지워질 때 까지 출력
while(!intStack.isEmpty()) {
	System.out.println(intStack.pop());
}

peek()는 C++에 top()이랑 같은거다 가장 위에 있는 데이터를 보여준다

Queue

FIFO(Frist In First Out)
add,peek,poll(꺼낸다),isEmpty,size 기능이 있다

Queue는 생성자가 없는 인터페이스이라서
생성자가 없는 인터페이스는 new 연산자로 생성을 할 수 없어서 대신 LinkedList<>()를 써서 생성을 한다.

  Queue<Integer> intQueue = new LinkedList<>(); // queue를 생성, 선언
  
intQueue.add(10);
    intQueue.add(3);
  
  // 다 지워질 때 까지 출력
  while(!intQueue.isEmpty()) {
  	System.out.println(intQueue.poll());
  }

Set

순서가 없고, 중복 없음
Set는 그냥 쓸순 있지만, HashSet, TreeSet등으로 응용해서 같이 사용 가능
Set은 생성자가 없는 껍대기라서 바로 생성할 수 없음 그래서 생성자가 존재하는 HashSet를 이용

Set<Integer> intSet = new HashSet<>();

intSet.add(1);
intSet.add(12);
intSet.add(31);
intSet.add(10);

for(int i : intSet) {
	System.out.println(i); // 1 10 12 31
}

만약에 정렬이 안되는 Set을 사용하고 싶으면

Set<Integer> intSet = new LinkedHashSet<>();

위에 있는 LinkedHashSet을 사용하면 된다!

intSet.contains(2) // true/false 반환

만약에 intSet안에 2가 있으면 true를 반환한다.

Map

key - value (pair)
key라는 값으로 유니크하게 보장이 돼야 한다

Map -> HashMap, TreeMap으로 응용

  Map<String, Integer> intMap = new HashMap<>();
  
  intMap.put("일", 11);
    intMap.put("이", 12);
    intMap.put("삼", 13);
    intMap.put("삼", 14); // 중복 key
    intMap.put("삼", 15); // 중복 key

Map은 key값이 중요해서 중복을 허용을 안 한다.
그래서 위에 삼,삼,삼 key가 겹치는 상황이 올 수 있다

  for(String key: intMap.keySet()) {
  	System.out.println(key); // 이 일 삼
  }

그렇기 떄문에 key를 for문으로 돌리면
이, 일, 삼이 나오는 걸 알수있다.
그런데 어떤 value가 지워졌는지 알수 없으니 for으로 value도 출력해보자

    for(Integer value: intMap.values()) {
	  System.out.println(value); // 12 11 15
  }

13,14는 15 value로 인해 덮여쓰기가 된걸 알수 있다.

key 조회

System.out.println(intMap.get("삼"));

삼이라는 value를 가져오고 싶으면 위에 가장 마지막으로 덮어진 15가 출력되는걸 볼수 있다.

keySet() vs entrySet()

map에서 회문 출력을 할 때 keySet을 이용해서 출력을 했었는데 entrySet()을 사용하면 더 효율적인 반복문을 작성 할 수 있다고 해서 이 두개의 차이점을 추가적으로 공부하고 싶어졌다

keySet()

일단 나는 아래의 코드에서 for loop + keySet() 조합으로 회문 출력을 했었다

Map<Integer,String> stringMap = new HashMap<Integer,String>();

stringMap.put(1,"Apple");
stringMap.put(1,"PineApple");
  
for(Integer val : stringMap.keySet()) {
	System.out.println(val + ". " + stringMap.get(val));
}

그래서 for문 + keySet()을 이용해서 key와 value 부분을 가져올 수 있습니다

iterator

우리가 keySet()을 배웠고 for문을 배웠으니 이제 iterator도 배워볼 차례이다.

iterator는 자바의 collection 프레임워크에서 collection에 저장되어 있는 요소들을 읽어오는 방법을 표준화 한것이다. Set, List, Map 인터페이스 모두 iterator로 요소들을 읽어 올 수 있다.

iterator method

  1. hasNext(): 다음 요소에 읽어 올 요소가 있는지 확인 하는 메소드.
    다음 요소가 있으면 true, 없으면 false를 반환한다.
  2. next(): 다음 요소를 가져온다.
  3. remove(): next()로 읽어온 요소를 삭제한다.

    remove()는 따로 여기서 다루지 않을 예정이다.

    메소드 호출 순서는 hasNext() -> next() -> remove()

keySet() + iterator

Iterator 방식으로도 물론 회문출력이 가능하다.

// key값만
Iterator<Integer> itr = stringMap.keySet().iterator();

// hasNext() 반한값이 boolean
while (itr.hasNext()){
	Integer key = itr.next();
	String value = stringMap.get(key);
	System.out.println(key + " : " + value);
}

단 Iterator<Wrapper 자료형> 여기서 Wrapper 자료형은 무조건 Key값의 자료형을 넣어줘여한다!

Map<Integer,String> stringMap = new HashMap<Integer,String>();

여기서 key의 Wrapper 자료형이 Integer이기 떄문에

Iterator<Integer> itr = stringMap.keySet().iterator();

으로 선언을 해주었다.

EntrySet()

for(Map.Entry<Integer,String> pair : stringMap.entrySet()){
	System.out.println(pair.getKey() + " : " + pair.getValue());
}

여기서 키와 값을 같이 가져와야하는 경우 Map.Entry<K, V> 인터페이스의 entrySet() 메서드를 사용합니다. Map.Entry 인터페이스는 Map 객체의 키와 값을 접근할 수 있도록 해주는 getKey(), getValue() 함수가 존재합니다.

확실히 keySet보다는 직관적으로 key와 value를 출력해준다.

EntrySet() + iterator

다음으로 EntrySet()을 iterator로 key와 value부분을 출력 해보도록 하겠습니다

iterator<Map.Entry<Integer,String>> iter = stringMap.entrySet().iterator();
 
while(iter.hasNext()){
	Map.Entry<Integer,String> it = iter.next();
	System.out.println(it.getKey() + " : " + it.getValue());
}

여기선 iterator<여기에 Map.Entry<K, V> 인터페이스 넣어줘야 한다.>
위에 for문으로 EntrySet()에 접근 할 때 Map.Entry<Integer,Sting> pair를 써서 pair를 통해 getKey()와 getValue()를 쓸수 있듯이 Iterator에서도 똑같이 써주면 된다.

keySet() vs entrySet()

keySet()

keySet() 메서드는 맵의 모든 키를 포함하는 Set 컬렉션을 반환하는데 이 Set 컬렉션은 맵의 키를 중복 없이 담고 있습니다. 이러한 특성으로 인해 keySet()을 사용하여 맵의 키를 순회하거나 특정 키에 대한 조회 및 수정 작업을 수행할 수 있습니다.

하지만 keySet()을 통해 얻은 키를 사용하여 값을 직접 조회하는 경우, 매번 조회할 때마다 맵을 순회해야 하므로 효율적이지 않을 수 있습니다.

entrySet()

entrySet() 메서드는 맵의 각 키-값 쌍을 나타내는 Map.Entry 인터페이스의 객체를 담은 Set 컬렉션을 반환하는데, 각 Map.Entry 객체는 getKey(), getValue() 메서드로 키 와 값을 얻을 수 있다.

이렇게 반환된 Set 컬렉션을 사용하면 맵의 키와 값을 동시에 접근하고 조작할 수 있습니다. entrySet()을 사용하여 맵의 키와 값을 동시에 순회하는 경우, 조회 작업이 효율적이고 간편합니다.

따라서 keySet()의 장점은 키에 집중해서 작업을 수행할 수 있다.
entrySet()은 키와 값을 동시에 다룰 수 있어 효율적인 작업이 가능하다.

List/Set + iterator

iterator를 사용해서도 Set 또는 List의 자료구조의 데이터를 회문출력을 할 수있다.

List

대표적으로 리스트 회문출력 2가지 방식 설명하겠다

1.

List<String> stringList = new ArrayList<String>();

for(int i = 0; i < stringList.size(); i++) {
	System.out.println( (i+1) + ". " + stringList.get(i));
}
2.
List<String> stringList = new ArrayList<String>();
  
Iterator<String> it = stringList.iterator();
	int cnt1 = 0;
	while(it.hasNext()) {
		++cnt1;
		System.out.println(cnt1 + ". " + it.next());
}

Set

Set<String> stringList = new HashSet<String>();
  
Iterator<String> iterSet = stringSet.iterator();
	int cnt = 0;
	while(iterSet.hasNext()) {
	++cnt;
	System.out.println(cnt + ". " + iterSet.next());
}

List 기준으로 iterator 방법을 추천을 드린다.
그 이유는 나중에라도 자료구조를 List에서 Set으로 변경시 iterator의 변경은 자료구조만 바꾸주면 끝난다. 즉 회문 부분은 변경 할 필요가 없는 것이다.
만약에 for문으로 하면 에러가 난다.

이런 에러가 날텐데...
그 이유는 Set에는 .get() 메소드가 없기 때문이다

자바의 Collection 부분은 다른 언어에 비해 뭔가 복잡하면서 한 번 제대로 이해하면 잘 쓰일거 같다는 생각이 든다! 하지만 C++를 주로 쓴 나로썬... c++이 그립다..
과연 자바 collection을 자유자재로 쓰고 싶다.

profile
Backend Programmer

0개의 댓글