[컬렉션]ArrayList, Set, Map, Tree

EUNJI LEE·2023년 4월 12일
0

JAVA

목록 보기
10/12
post-custom-banner

컬렉션(Collection)

메모리 상에서 자료를 구조적으로 처리하기 위해 자료구조를 담당하는 프레임워크인 컬렉션을 사용한다. 추가, 삭제, 정렬 등의 기능 처리가 간단하게 해결되어 자료 구조적 알고리즘을 구현할 필요가 없어진다.

배열의 문제점인 지정한 크기 변경에 대한 문제나 기록된 데이터에 중간 위치의 추가, 삭제가 불편하다는 점, 한 타입의 데이터만 저장 가능하다는 점을 컬렉션이 보완한다.

컬렉션은 자료 추가 삭제함에 따라 로직을 다시 구성할 필요 없이 해당 기능을 수행하는 메소드를 이용해서 길이를 조절할 수 있고 다양한 메소드 사용으로 추가, 삭제, 정렬 등의 기능 처리가 간단하게 해결된다. 또한 객체만 저장하기 때문에 다양한 자료형을 사용할 수 있다. 단, 기본 자료형을 저장해야 하는 경우 Wrapper 클래스를 이용한다.

Collection Interface

컬렉션의 인터페이스는 Collection 계열과 Map 개열로 나뉜다. Collection 계열에는 List 계열과 Set 계열로 다시 나뉘어지며 인터페이스는 생성해서 사용 불가하므로 구현 클래스를 생성 후, 필요한 기능을 구현한 멤버 메소드를 이용한다.

List

선형구조의 인터페이스로 중복 저장이 가능하다. 순서가 존재하기 때문에 인덱스 번호로 관리된다. 구현 클래스로는 ArrayList, Vector, LinkedList가 있다.

ArrayList

ArrayList에는 모든 객체가 저장 가능하다. ArrayList 리스트명=new ArrayList(); 방식으로 생성해서 사용한다. ArrayList는 동적 배열을 제공하기 때문에 용량을 초과한 객체들이 들어오면 자동으로 길이를 증가시켜서 저장한다. 리스트를 생성과 동시에 초기화 및 할당해서 사용하면 더이상 변경이 불가한 불변 리스트가 된다.

💡 List.of로 생성한 리스트는 불변 리스트이기 때문에 데이터 수정, 추가가 불가능하다. final과 같은 개념이라고 볼 수 있다. 데이터를 더 이상 수정할 수 없기 때문에 퍼포먼스적가 좋다. 고정해놓고 더 이상 추가로 값을 받지 않아야하는 경우에 사용할 수 있다.
List.of(객체1, 객체2, 객체3, ...); 같은 방식으로 사용한다.

ArrayList에서 제공하는 메소드들

add(); : 객체 추가 메소드

get(index); : 해당 인덱스 번호의 데이터를 반환하는 메소드

size(); : 해당 List의 크기(길이)를 구하는 메소드

ArrayList list = new ArrayList(); // 생성하면 할당이 된다. 길이는 알아서 조절함.
list.add(true); // 제일 처음 add했기 때문에 알아서 맨 처음에 저장한다. 인덱스 0
list.add(19); // 알아서 인덱스 번호를 부여
list.add(new Date());

System.out.println(list.get(0));
for (int i = 0; i < list.size(); i++) {
	System.out.println(list.get(i));
} //인덱스가 존재하기 때문에 반복문을 사용해서 순회해서 조회 가능.
  • 클래스로 객체를 생성해서 ArrayList 활용
    ArrayList animals = new ArrayList();
    animals.add(new Animal("멍멍이", 5.3, 6, "강아지"));
    animals.add(new Animal("야옹이", 4.2, 3, "고양이"));
    animals.add(new Animal("숭이", 15.3, 4, "원숭이"));
    animals.add(new Animal("어흥이", 90.5, 5, "호랑이"));
    // 1. 전체 데이터 출력
    for (int i = 0; i < animals.size(); i++) {
    	System.out.println(animals.get(i));
    }
    // 2. 이름, 나이, 몸무게만 출력
    for (Object o : animals) {
    	Animal a = (Animal) o; // 형변환해서 사용
    	System.out.println(a.getName() + " " + a.getAge() + " " + a.getWeight());
    }

💡 ArrayList는 Object를 자료형으로 받고 있기 때문에 Animal 클래스로 형변환 해서 사용해줘야한다. 자료형을 받아올 때도 Object 자료형임에 주의한다.

 // 3. 몸무게가 10kg 이상인 동물 이름, 종류, 몸무게 출력
 for (int i = 0; i < animals.size(); i++) {
 	Animal a = (Animal) animals.get(i);
 		if (a.getWeight() >= 10) {
 			System.out.println(a.getName() + " " + a.getType() + " " + a.getWeight());
 		}
 }
 // 5. 저장된 동물들의 나이를 한살씩 증가시키고 저장된 전체 동물 출력
 for (int i = 0; i < animals.size(); i++) {
 	Animal a = (Animal) animals.get(i); // 각 객체에 순서대로 접근하고
 	int age = a.getAge(); // 나이를 받아와서 변수에 저장
 	a.setAge(age + 1); // set메소드를 이용해서 값을 대입
 	System.out.println(a.toString()); //toString 오버라이딩
 }

✅ 굳이 int 자료형 변수를 따로 선언하지 않아도 a.setAge(a.getAge+1); 로 처리해도 가능하다.

isEmpty(); : ArrayList가 비어있는지 확인하는 메소드. 반환형은 boolean 자료형으로 비어있는 경우 true를 반환하고 데이터가 있는 경우 false를 반환한다.

ArrayList test = new ArrayList();
System.out.println(test.isEmpty());
System.out.println(animals.isEmpty());
if (!animals.isEmpty()) { //비어있는게 아닐때 if문 실행
	for (Object a : animals) {
		System.out.println(a); //animals가 인덱스 순서대로 출력
	}
}

💡 public boolean isEmpty() { return size == 0; }isEmpty() 메소드는 결국 메소드의 사이즈를 비교해서 boolean 자료형으로 반환한 것으로 if(list.size()==0)과 같다.
아래와 같은 두 가지 방법으로도 나타낼 수 있다.

//삼항연산 활용
System.out.println(test.size() == 0 ? "비어있습니다." : "데이터가 있습니다.");
//if문 활용
if (animals.size() > 0) {
	System.out.println("animals에는 데이터가 있습니다");
}

iterator() 메소드

반복문을 사용하지 않고 ArrayList를 순회할 수 있는 또 하나의 방법으로 데이터가 저장되어있는 전체 객체를 한번씩 가져오는 메소드이다. iterator 방식으로 데이터를 뽑아오는데 데이터를 하나씩 불러올 때는 iterator에서 제공하는 next() 메소드를 이용한다. iterator 방식으로 불러온 데이터는 값을 가져오면 사라지기 때문에 계속해서 사용하고 싶을 땐 변수에 따로 저장을 해서 저장된 값을 불러오는 방식으로 이용해야 한다.

Iterator it = animals.iterator();
while (it.hasNext()) { // hasNext : 뒤에 데이터가 있으면 true, 없으면 false
		System.out.println(it.next());
		System.out.println(it.next());
}

💡 while문 안에 System.out.println(it.next()); 는 animals 인덱스가 짝수이기 때문에 오류가 발생하지 않는 것이다. 만약 index 사이즈가 3이었다면 while문이 두번째 실행됐을 때 첫 줄에서는 오류가 발생하지 않지만 두번째 줄에서 index를 초과했기 때문에 오류가 발생한다.

ArrayList에서 특정 데이터를 추가, 수정, 삭제하는 메소드들

add(index, element); : 원하는 위치에 원하는 객체를 추가하는 메소드

System.out.println("====animals 데이터 추가====");
animals.add(1, new Animal("부엉", 4.3, 2, "부엉이"));
for (int i = 0; i < animals.size(); i++) {
	System.out.println(i + 1 + " " + animals.get(i));
}
💡 추가하면 해당 위치에 있던 데이터를 덮어쓰는 것이 아닌 기존 인덱스를 하나 뒤로 밀어서 저장하고 해당 위치에는 추가한 값을 대입한다.

set(index, element); : 원하는 위치에 원하는 객체를 수정하는 메소드

animals.set(1, new Animal("깡총이", 8.2, 1, "토끼"));

remove(index); : 원하는 인덱스의 데이터를 삭제하는 메소드

remove(Object o); : 원하는 매개변수와 일치하는 값을 삭제하는 메소드

animals.remove(1);
animals.remove(new Animal("부엉", 4.3, 2, "부엉이"));

💡 원하는 인덱스의 데이터를 삭제했을 때는 해당 데이터를 삭제하고 뒤에 인덱스에 데이터가 있으면 앞으로 당겨와서 정렬한다.
매개변수를 이용해서 일치하는 값을 찾아낼 때는 hashCode와 equals가 오버라이딩 되어있어야 사용 가능하다.
equals만 오버라이딩 되어있으면 객체의 데이터 값만 일치한 다른 객체로 구분하므로 객체 고유값까지 동등 비교할 수 있도록 오버라이딩 해야한다.

ArrayList의 데이터들을 관리할 수 있는 메소드

contains(Object o) : 특정 객체가 리스트에 포함되어있는지 확인하는 메소드. boolean형을 반환형으로 가지며 마찬가지로 hashCode와 equals가 오버라이딩되어있어야 한다.

indexOf(Object), lastIndexOf(Object) : 동일한 객체를 찾아서 그 인덱스를 반환해주는 메소드

toArray() : ArrayList로 저장된 데이터를 배열로 변환해주는 메소드

asList() : 배열을 List형태로 변환하는 메소드

clear(); : ArrayList에 저장된 데이터를 한번에 삭제하는 메소드

boolean result = animals.contains(new Animal("야옹이", 4.2, 6, "고양이"));
System.out.println(result);

int index = animals.indexOf(new Animal("야옹이", 4.2, 6, "고양이"));
System.out.println("야옹이의 인덱스 : " + index);

Object[] objArr = animals.toArray(); //배열로 변환
List animals2 = Arrays.asList(objArr); //객체 리스트로 변환

animals.clear();
System.out.println(animals.isEmpty()); //true 출력

LinkedList 클래스

List 인터페이스를 구현한 클래스로 사용 방법은 ArrayList와 유사하다. 다만 동적 배열 형식이 아닌 인접한 각 주소가 링크로 연결된 체인 형식이기 때문에 ArrayList보다 데이터의 추가, 삭제 등이 많이 일어나는 상황에서 퍼포먼스가 좋아진다.

LinkedList linkList=new LinkedList();
linkList.add("choi");
linkList.add("lee");
linkList.add("kim");
System.out.println(linkList.get(0)); //choi
linkList.forEach((e)->System.out.println(e)); //전체 데이터 순회
		
System.out.println(linkList.getFirst()); //첫번째 불러온다
System.out.println(linkList.getLast()); //마지막을 불러온다

Set

저장 순서가 없고 중복 객체를 걸러내서 저장하는 자료 구조로 구현 클래스로 HashSet, LinkedHashSet, TreeSet을 사용한다. ArrayList와 사용하는 메소드가 비슷하지만 index를 사용하는 메소드들이 전부 빠져있다.
HashSet set명=new HashSet(); 생성해서 사용한다.

💡Set 특징

  1. 데이터를 중복 해서 저장하지 않는다.
  2. 데이터를 지정할 수 있는 구분자(인덱스, 키)가 없다.
  3. 단일 값을 찾아올 수 없기 때문에 break를 사용해서 해당 값을 찾아서 걸러내야 한다.
HashSet set=new HashSet();
set.add("lee");
set.add("kim");
set.add("park");
System.out.println(set); //[park, lee, kim]

💡 정해진 순서가 없기 때문에 값을 입력한 순서대로 불러오지 않는다. 특정 값을 인덱스로 찾아낼 수 없다.
입력한 순서를 그대로 불러오고 싶을 땐 LinkedHashSet을 사용하면 데이터를 추가한 순서대로 불러올 수 있다. 마찬가지로 인덱스는 없다.

Set을 순회해서 출력

//1. Iterator 인터페이스를 이용해서 출력
Iterator it=set.iterator();
while(it.hasNext()) {
	String value=(String)it.next();
  //자료형을 정하지 않았기 때문에 기본적으로 Object형으로 들어간다
	System.out.println(value);
}

//2. forEach문 이용 ->iterator를 이용하지 않아도 출력할 수 있다.
for(Object o:set) {
		System.out.println(o);
}

//3. forEach() 메소드 제공
set.forEach((o)->System.out.println(o));
  • set을 활용해서 로또 번호 출력하는 기능 만들기
    HashSet lotto=new HashSet();
    while(lotto.size()<7) {
    	lotto.add((int)(Math.random()*45)+1);
    }
    lotto.forEach((l)->System.out.print(l+" "));

💡 Set과 List는 호환이 가능하다. students라는 Set형을 List studentList=new ArrayList(students); 처럼 선언해서 List형으로 바꿔서 사용할 수 있다.

Map

keyvalue로 구성되어 있으며 키와 값은 모두 객체이다. 키는 찾을 데이터를 구분해야 하기 때문에 중복 저장을 허용하지 않고 값은 중복 저장도 상관없다. key를 중복해서 선언했을 때 프로그램 실행 자체가 안 되는 것은 아니지만 기존에 선언했던 값 위에 새로 선언한 키의 값이 덮어씌워진다.

key는 보통 String으로 많이 쓰며 종종 Interger로도 사용한다. value는 vo객체, String, Integer등의 데이터를 저장한다.

put(key, value) : 데이터를 저장하는 메소드. 저장은 key, value를 동시 설정하는 방식이다.

get(key) : key를 이용해서 저장된 value를 불러오는 메소드.

size() : Map에 저장된 데이터를 확인하는 메소드.

remove(key) : 해당 key를 삭제하는 메소드

containsKey(key) : map에 해당 key가 있는지 확인하는 메소드. boolean 자료형을 반환한다.
해당 key값이 존재하면 중복 저장을 하지 않는 로직을 작성할 때 활용할 수 있다.

containsValue(value) : map에 특정 value가 있는지 확인하는 메소드. boolean 자료형을 반환한다.

HashMap fruits=new HashMap();
fruits.put(1, new Fruit("귤","제주도",10,15000));
fruits.put(2, new Fruit("사과","예산",5,25000));
fruits.put(3, new Fruit("포도","영동",10,35000));
fruits.put(4, new Fruit("배","나주",10,50000));
System.out.println(fruits); //{key=value, ... }형식으로 출력
System.out.println(fruits.size());
fruits.remove(3); //여기서 3은 index가 아닌 key값이다.

Map, List 호환

Map은 List로 호환이 되지만 그 반대로는 사용이 안 된다. Map.values() value값들만 Collection으로 가져오는 메소드를 사용한다.

List data=new ArrayList(map.values());
//key값 대신 index가 존재하기 때문에 데이터만 가져온다.
for(int i=0;i<data.size();i++) {
		System.out.println(data.get(i));
}

Map에 저장된 데이터 전체를 순회하는 방법

Map.keySet() : map에 저장된 전체 key값을 가져오는 메소드. Set을 반환하기 때문에 iterator 사용이 필수적이다.

entrySet() : key, value를 전부 가져오는 메소드. Map안에 Entry interface를 사용한다.

Set keys=map.keySet();
Iterator it=keys.iterator();
while(it.hasNext()) {
	System.out.println(map.get(it.next()));
}

Set entry=map.entrySet();
Iterator it2=entry.iterator();
while(it2.hasNext()) {
	Map.Entry all=(Map.Entry)it2.next();
	System.out.println(all.getKey()+" "+all.getValue());
}
✅ iterator은 한 번 뽑아내고 나면 지속 사용할 수 없기 때문에 재사용 하기 위해서는 다른 변수에 저장해서 사용한다.

💡 Map.of(key, value)불변 데이터 생성

Map도 of 메소드를 이용해서 불변 데이터 생성 가능하다. key와 value가 짝을 이루기 때문에 파라미터의 갯수는 항상 짝수가 된다.

TreeSet, TreeMap

검색 기능을 강화시킨 컬렉션으로 계층 구조를 활용해 이진 트리 자료 구조를 구현한다. Tree 구조는 위에서부터 노드 별로 갈라져서 하위 노드들이 존재하기 때문에 순서가 있다. 노드가 나뉘어있기 때문에 기준을 구분해서 자료를 조회하기 때문에 찾는 속도가 빠르다. 단, compareTo()를 재정의 해서 “순서의 기준”을 정해줘야 한다.

기본 정렬은 오름차순으로 하고 내림차순은 기준을 따로 구현해준다. 기본 정렬은 숫자와 문자열 타입만 가능하며 그 외의 타입이 Comparable을 상속 받는 객체인 경우 compareTo() 메소드를 오버라이딩해서 기준을 재정의 할 수 있다.

TreeSet studentTree=new TreeSet();
studentTree.add(new Student());
studentTree.add(new Student());
studentTree.forEach((t)->System.out.println(t));
profile
천천히 기록해보는 비비로그
post-custom-banner

0개의 댓글