고래씌

[JAVA] 14-1. 컬렉션(Collection) - ArrayList 본문

JAVA/JAVA 기초

[JAVA] 14-1. 컬렉션(Collection) - ArrayList

고래씌 2023. 10. 24. 16:48

1. 컬렉션(Collection)

: 자료구조가 내장되어있는 클래스로 자바에서 제공하는 "자료구조"를 담당하는 "프레임워크"

 

▶ 자료구조 : 방대한 데이터들을 효율적으로 다룰때 필요한 개념을 클래스로 정의한 것

▶ 프레임워크 : 효율적인 기능들이 이미 정의되어 있는 틀

 

☞ 데이터들이 새롭게 추가되거나, 삭제가 되거나, 수정이 되는 기능들이 이미 정의되어있는 틀이 있다 == 컬렉션

다량의 데이터들을 관리하고자 할 때 배열 가지고 충분히 사용을 할 수 있었지만, 배열이  가진 단점들을 보완한 것이 컬렉션

 

 

1) 배열과 컬렉션의 차이점

- 배열의 단점

① 배열을 쓰고자 할 때 먼저 크기를 지정해야함. 한번 지정된 크기는 변경이 불가

만약, 새로운 값을 추가하고자 한다면 크기가 오버될 경우 새로운 크기의 배열을 만들고 기존의 내용들을 복사해서 옮겨주는 코드를 직접짜야 했음

 

② 배열 중간 위치에 새로운 데이터를 추가하거나, 삭제하는 경우 기존의 값들을 땡겨주는 코드를 직접 짜줘야함.

 

③ 한 타입의 데이터들만 저장 가능

 

- 컬렉션의 장점

① 크기에 제약이 없다.

=> 크기 지정을 해줄 필요도 없고, 만일 같은 크기지정을 해도 알아서 크기가 늘어나거나 줄어드는 코드가 내부적으로 이미 정의되어 있음

 

② 중간에 값을 추가하거나 삭제하는 경우 값들을 땡겨주는 코드가 이미 내부적으로 정의되어 있음

 

③ 여러타입의 데이터들을 저장할 수 있음(단, 제네릭설정을 통해 한 타입의 데이터들만 들어올 수 있게끔도 가능)

 

 

▶ 배열 --> 방대한 데이터들을 단지 담아만 두고 조회만 할 목적이다.

컬렉션 --> 방대한 데이터들이 빈번하게 추가, 삭제, 수정이 될 것 같다.

 

 

 

2) 컬렉션의 3가지 분류

List 계열

① 담고자하는 값(value)만 저장하는 자료구조

② 값을 저장할 때 순서가 유지됨(즉, index 개념 有)

중복값을 허용

 

- List계열 자식 클래스 : ArrayList, Vector, LinkedList => ArrayList가 자주 쓰임

 

▶ Set 계열

① 담고자하는 값(value)만 저장하는 자료구조

② 값을 저장할 때 순서를 유지하지 않음(index 개념 無)

중복값을 허용하지 않음

 

- Set계열 자식 클래스 : HashSet, TreeSet

 

▶ Map 계열 

① 담고자하는 값(value)을 키(key)에다가 저장

② 값 저장시 순서를 유지하지 않음(index 개념 無)

③ value 값을 중복을 허용하나, key값은 중복을 허용하지 않음

 

- Map계열 자식 클래스 : HashMap, HashTable, Properties

 


2. 제네릭(Generics)

: 제네릭(<E>)
별도의 제네릭 제시 없이 컬렉션 객체를 생성하면, 해당 컬렉션에는 다양한 타입의 데이터 값들이 담길 수 있음.

 

ex) List<Music> list = new ArrayList<Music>(3);

☞ 별도의 제네릭 설정으로 <Music>으로 하게되면 해당 컬렉션에는 Music객체만 담을 수 있음

 

 

1)  제네릭을 설정하는 이유

① 명시한 타입의 객체만 저장가능하도록 타입의 제한을 두기 위해서 --> 안전성 확보 가능
② 컬렉션에 저장된 객체를 꺼내서 사용할 때 매번 형변환해야하는 절차를 없애기 위해서

 


 

3. List 계열(ArrayList)

 

1) add(E e) : 해당 리스트의 끝에 전달된 e를 추가시켜주는 메소드

=> 순차적으로 추가됨

 

List list = new ArrayList();   // 내부적으로 크기 10짜리인 배열이 생성

=> 여러 다양한 자료형을 저장할 수 있음

 

List<Music> list = new ArrayLIst<Music>(3)  // Music 타입의 객체만 들어올 수 있음. 제네릭을 사용하였기 때문에.

=> 배열이 3짜리인 배열 생성

 

package com.kh.chap01_list.part01_arrayList.model.vo;

public class Music {
	
	private String title;
	private String artist;
	
	public Music() {
		
	}

	public Music(String title, String artist) {
		super();
		this.title = title;
		this.artist = artist;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getArtist() {
		return artist;
	}

	public void setArtist(String artist) {
		this.artist = artist;
	}

	@Override
	public String toString() {
		return "Music [title=" + title + ", artist=" + artist + "]";
	}
	
	

}
package com.kh.chap01_list.part01_arrayList.run;

import java.util.ArrayList;
import java.util.List;

import com.kh.chap01_list.part01_arrayList.model.vo.Music;


public class Run {

	public static void main(String[] args) {
		
		List<Music> list = new ArrayList<Music>(3);  // Music 타입의 객체만 들어올 수 있음
		

//		System.out.println(list);  // 안에 아무것도 없는 상태(비어있음) + toString오버라이딩 되어 있음
		
		// 1. add(E e) : 해당 리스트의 끝에 전달된 e를 추가시켜주는 메소드
		list.add(new Music("ditto","뉴진스"));
		list.add(new Music("ETA","뉴진스"));
		list.add(new Music("hype boy", "뉴진스"));
		list.add(new Music("고백","장범준"));
		
		
		System.out.println(list);
	}

}

☞ 순서가 유지되면서 값이 추가됨

☞ 크기에 제약이 없음

☞ 제네릭 설정을 하지 않는다면, 다양한 타입의 값 추가 가능

 

 

2) add(int index, E e) : 리스트에 전달되는 index값 위치에 전달되는 e를 추가시켜주는 메소드

: 중간에 값 추가시 알아서 기존의 값들을 뒤로 땡겨주는 작업이 내부적으로 진행됨

 

package com.kh.chap01_list.part01_arrayList.run;

import java.util.ArrayList;
import java.util.List;

import com.kh.chap01_list.part01_arrayList.model.vo.Music;


public class Run {

	public static void main(String[] args) {
		
		List<Music> list = new ArrayList<Music>(3);  // Music 타입의 객체만 들어올 수 있음
		
		
		// 1. add(E e) : 해당 리스트의 끝에 전달된 e를 추가시켜주는 메소드
		list.add(new Music("ditto","뉴진스"));
		list.add(new Music("ETA","뉴진스"));
		list.add(new Music("hype boy", "뉴진스"));
		list.add(new Music("고백","장범준"));
		
		
		System.out.println(list);
		
		// 2. add(int index, E e) : 리스트에 전달되는 index값 위치에 전달되는 e를 추가시켜주는 메소드
		list.add(1, new Music("this love", "빅뱅"));  // list[1] = new Music("?","?")
        
		
		System.out.println(list);

	}
}

 

 

 

3) set(int index, E e) : 리스트에 해당 인덱스 값을 전달되는 e로 변경시켜주는 메소드

package com.kh.chap01_list.part01_arrayList.run;

import java.util.ArrayList;
import java.util.List;

import com.kh.chap01_list.part01_arrayList.model.vo.Music;


public class Run {

	public static void main(String[] args) {
		

		
		List<Music> list = new ArrayList<Music>(3);  // Music 타입의 객체만 들어올 수 있음

		
		// 1. add(E e) : 해당 리스트의 끝에 전달된 e를 추가시켜주는 메소드
		list.add(new Music("ditto","뉴진스"));
		list.add(new Music("ETA","뉴진스"));
		list.add(new Music("hype boy", "뉴진스"));
		list.add(new Music("고백","장범준"));
		
		
		System.out.println(list);
		
		// 2. add(int index, E e) : 리스트에 전달되는 index값 위치에 전달되는 e를 추가시켜주는 메소드
		list.add(1, new Music("this love", "빅뱅"));  // list[1] = new Music("?","?")
		// 중간에 값 추가시 알아서 기존의 값들을 뒤로 땡겨주는 작업이 내부적으로 진행됨
		
		System.out.println(list);
		
		
		// 3. set(int index, E e) : 리스트에 해당 인덱스 값을 전달되는 e로 변경시켜주는 메소드
		list.set(1, new Music("바다의 왕자","박명수"));   // list[1] = new Music("?","?")
		
		System.out.println(list);

	}

}

 

☞ list.set(1, new Music("바다의 왕자","박명수"));   

=> 1번 인덱스의 값이 변경된 것을 확인

 

 

 

4) remove(int index) : 리스트에 해당 인덱스의 값을 삭제시켜주는 메소드

ex) list.remove(1);

=> 1번 인덱스에 있는 것을 삭제하고 뒤쪽에 있던 인덱스를 앞으로 옮김

package com.kh.chap01_list.part01_arrayList.run;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.kh.chap01_list.part01_arrayList.model.vo.Music;


public class Run {

	public static void main(String[] args) {
		
		List<Music> list = new ArrayList<Music>(3);  // Music 타입의 객체만 들어올 수 있음

		// 1. add(E e) : 해당 리스트의 끝에 전달된 e를 추가시켜주는 메소드
		list.add(new Music("ditto","뉴진스"));
		list.add(new Music("ETA","뉴진스"));
		list.add(new Music("hype boy", "뉴진스"));
		list.add(new Music("고백","장범준"));
		
		
		System.out.println(list);
		
		// 2. add(int index, E e) : 리스트에 전달되는 index값 위치에 전달되는 e를 추가시켜주는 메소드
		list.add(1, new Music("this love", "빅뱅"));  // list[1] = new Music("?","?")

		
		System.out.println(list);
		
		
		// 3. set(int index, E e) : 리스트에 해당 인덱스 값을 전달되는 e로 변경시켜주는 메소드
		list.set(1, new Music("바다의 왕자","박명수"));   // list[1] = new Music("?","?")
		
		System.out.println(list);
		
		// 4. remove(int index) : 리스트에 해당 인덱스의 값을 삭제시켜주는 메소드
		list.remove(1);

		System.out.println(list);

	}

}

=> 1번 인덱스 삭제됨

 

 

 

5) size() : 리스트에 담겨있는 데이터의 수를 반환해주는 메소드

System.out.println("리스트에 담긴 데이터 수 : " + list.size());
System.out.println("리스트에 마지막 인덱스 : " + (list.size()-1));

 

 

6) get(int index) : E => 리스트에 해당 index위치의 데이터를 반환해주는 메소드

 

Music m = list.get(1);   // 데이터를 반환할때도 Music 객체만 반환. 자료형이 일치하기 때문에 에러발생 X
System.out.println(list.get(1));
System.out.println(list.get(1).getTitle());   // 제목만 가져올수 있음

 

 

 

★만약 제너릭을 안하고 List list = new ArrayList(); 를 선언해주었다면?
Music m = list.get(1);
System.out.println(list.get(1).getTitle());


모두 에러 발생. 다양한 자료형을 꺼내올 수 있어서 이때는 직접 강제형변환을 해주어야 한다!

 

 

      ↓↓

 


Music m = (Music) list.get(1);
 System.out.println( ((Music)list.get(1)).getTitle());          

 

☞ 에러 발생 X

 

package com.kh.chap01_list.part01_arrayList.run;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.kh.chap01_list.part01_arrayList.model.vo.Music;


public class Run {

	public static void main(String[] args) {
		
		List<Music> list = new ArrayList<Music>(3);  // Music 타입의 객체만 들어올 수 있음

		
		// 1. add(E e) : 해당 리스트의 끝에 전달된 e를 추가시켜주는 메소드
		list.add(new Music("ditto","뉴진스"));
		list.add(new Music("ETA","뉴진스"));
		list.add(new Music("hype boy", "뉴진스"));
		list.add(new Music("고백","장범준"));
		
		
		System.out.println(list);
		
		// 2. add(int index, E e) : 리스트에 전달되는 index값 위치에 전달되는 e를 추가시켜주는 메소드
		list.add(1, new Music("this love", "빅뱅"));  // list[1] = new Music("?","?")
		
		
		// 3. set(int index, E e) : 리스트에 해당 인덱스 값을 전달되는 e로 변경시켜주는 메소드
		list.set(1, new Music("바다의 왕자","박명수"));   // list[1] = new Music("?","?")

		
		// 4. remove(int index) : 리스트에 해당 인덱스의 값을 삭제시켜주는 메소드
		list.remove(1);
		// 1번 인덱스에 있는 것을 삭제하고 뒤쪽에 있던 인덱스를 앞으로 옮김

		
		
		// 6. get(int index) : E => 리스트에 해당 index위치의 데이터를 반환해주는 메소드
		Music m = list.get(1);   // 데이터를 반환할때도 Music 객체만 반환. 자료형이 일치하기 때문에 에러발생 X
		System.out.println(list.get(1));
		System.out.println(list.get(1).getTitle());   // 제목만 가져올수 있음


	}

}

 

 

 

7) contains(E e) : boolean => 매개변수로 넘어온 값이 현재 컬렉션에 포함되어있는지 확인

 

ArrayList<Integer> Iarr = new ArrayList<>(3);   // java7버전부터 추가된 내용. 뒤에 <같은 자료형>을 작성하지 않아도 작동됨
Iarr.add(1);
Iarr.add(9);
Iarr.add(900);

 

System.out.println(Iarr.contains(90));

false 출력됨

 

 

8) indexOf(E e) : int => 매개변수로 넘어온 값이 현재 컬렉션에 몇번째 인덱스에 존재하는지 반환 / 없다면 -1을 반환

 

ArrayList<Integer> Iarr = new ArrayList<>(3);   
Iarr.add(1);
Iarr.add(9);
Iarr.add(900);

System.out.println(Iarr.indexOf(9000));

=> -1 반환

 

 

▶ 베이직 for문
- 0번 인덱스 ~ 마지막 인덱스까지의 데이터 출력
for(int i=0; i<list.size(); i++) {
    System.out.println(list.get(i));
}

 

 

▶ 향상된 반복문 : for each문
- for( 값을 받아줄 변수 : 순차적으로 접근할 배열 또는 컬렉션)

- 제네릭 설정 전(강제형변환 필요)
for( Object o : list  ) {

}  

- 제네릭 설정 후
for(Music o : list) {
System.out.println(o);
}

 

 

※ Iterator 반복자

: 컬렉션 내부 인자들을 Iterator에 담아서 관리(String의 StringTokenzier 비슷함)

		Iterator<Music> iter = list.iterator();   // 컬렉션을 iterator 객체로 변환
		
		while(iter.hasNext()) {           
			Music music = iter.next();    // next함수가 호출되었을때 다음 한칸으로 이동
			System.out.println(music);
		}

 

 

9) subList(int index1, int index2) : 해당 리스트로부터 index1에서부터 index2까지의 데이터 값들을 추출해서 새로운 List로 반환시켜주는 메소드

 

☞ list.subList(0, 2) ==> 0 <== 인덱스 < 2

		List<Music> sub = list.subList(0, 2);
		System.out.println(sub);

 

 

10) addAll(Collection c) : 해당 리스트에 다른 컬렉션에 있는 데이터들을 통째로 추가해주는 메소드

		list.addAll(sub);
		
		System.out.println(list);     // Collection 은 List,와 set의 부모(조상)임

 

 

 

11) isEmpty() : 해당 리스트가 비어있는지 묻는 메소드(비어있으면 true / 비어있지 않으면 false 반환)

12) clear() : 해당 리스트를 싹 비워주는 메소드

		// 11. isEmpty() : 해당 리스트가 비어있는지 묻는 메소드(비어있으면 true / 비어있지 않으면 false 반환)
		System.out.println("리스트가 비어있습니까? " + list.isEmpty());
		
		
		// 12. clear() : 해당 리스트를 싹 비워주는 메소드
		list.clear();
		System.out.println("리스트가 비어있습니가? " + list.isEmpty());

 

 

13) sort(List list) : 컬렉션 내부의 값들을 정렬시켜주는 함수

 

내가 직접 정의한 객체를 정렬하기 위해서는 "정렬기준"을 새롭게 만들어줘야함

▶ 객체 정렬방법 2가지



Comparable 인터페이스 구현
    기본 정렬(한가지 방법만 가능)을 클래스에서 구현
   정렬시 사용하는 compareTo메소드를 오버라이딩 하는 방식

   → 정렬기준을 한가지만 세울수 있음

② Comparator 인터페이스
객체의 기본정렬 외에 추가적으로 여러개의 정렬기준을 제시하고자 할 때 사용.
ex) 가수이름 오름차순 정렬, 내림차순 정렬, 음악제목 오름차순, 내림차순 정렬을 각각 만들고 싶을때 별도의 Comparator구현 클래스를 작성하면 된다.
compare 메소드 오버라이딩  

 

 

 

Comparable은 자기 자신과 파라미터로 들어오는 객체를 비교하는 것

Comparator는 파라미터로 들어오는 두 객체를 비교하는 것

 

 

■ Comparable

- 반환값

동등 : 0

현재객체가 더 높음 : 1

상대객체가 더 높음 : -1

package com.kh.chap01_list.part01_arrayList.model.vo;

public class Music implements Comparable<Music>{
	
	private String title;
	private String artist;
	
	public Music() {
		
	}

	public Music(String title, String artist) {
		super();
		this.title = title;
		this.artist = artist;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getArtist() {
		return artist;
	}

	public void setArtist(String artist) {
		this.artist = artist;
	}

	@Override
	public String toString() {
		return "Music [title=" + title + ", artist=" + artist + "]";
	}
	
	@Override
	public int compareTo(Music o) {
		// 매개변수의 Music객체는 현재객체와 비교하고자하는 객체
		
		// 현재객체 : this
		// 상대객체 : Music o
		
		// 반환값이 0 : 동등, 1: 현재객체 더 높다, -1 : 상대객체가 더 높다
		
		// artist 기준 오름차순 정렬
		
		
		return this.artist.compareTo(o.artist);
	}
	

}

☞ Music 클래스에 public class Music implements Comparable<Music> 를 추가해주면 자동으로 compartTo 메소드가 추가됨.

 

 

		// 1번 Comparable 기본정렬 : 가수이름 오름차순
		Collections.sort(list);      
		System.out.println(list);

		
		// 가수이름 내림차순
		Collections.sort(list, Collections.reverseOrder());
		System.out.println(list);

☞ 정렬된 것을 확인

 

 

 

 

■ Comparator 정렬

별도의 Comparator구현 클래스를 작성후 compare 메소드 오버라이딩

 

package com.kh.chap01_list.part01_arrayList.model.vo;

import java.util.Comparator;

public class MusicTitleAscendingCompartor implements Comparator<Music>{

	
	// 제목기준 오름차순
	@Override
	public int compare(Music o1, Music o2) {
		return o1.getTitle().compareTo(o2.getTitle());
	}

	
}
		Comparator<Music> comp2 = new MusicTitleAscendingCompartor().reversed();  // reversed 역순
		Collections.sort(list, comp2);
		
		System.out.println(list);

 

 

 

Q. 반복문을 통해 컬렉션에 담긴 요소를 체크한 후 삭제하기
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(1);
list2.add(1);
list2.add(1);
list2.add(3);
list2.add(4);
list2.add(5);

 

- 방법 ①

		// 삭제완료 후 컬렉션에 저장된 데이터 출력
		for(int i=0; i<list2.size(); i++) {
			if(list2.get(i) == 1) {
				list2.remove(i);  // 삭제되는 순간 list의 사이즈가 1 작아져서 바로 다음에 오는 객체를 검사하지 못함.
				i--; // 이걸로 해결이 가능하다
			}
		}

 

-방법 ②

		// 가장좋은 방법 == Iterator 사용
		Iterator<Integer> iter2 = list2.iterator();
		while(iter2.hasNext()) {
			int s = iter2.next();
			if(s == 1) iter2.remove();  // 현재 참조하고 있는 collection 요소를 제거
		}

 

 

14) Collections.shuffle(List list);    배열 랜덤으로 섞기

        ArrayList<Integer> list2 = new ArrayList<>();
        list2.add(1);
        list2.add(1);
        list2.add(1);
        list2.add(3);
        list2.add(4);
        list2.add(5);
        
        Iterator<Integer> iter2 = list2.iterator();
        while(iter2.hasNext()) {
            int s = iter2.next();
            if(s == 1) iter2.remove();  // 현재 참조하고 있는 collection 요소를 제거
        }
        

        Collections.shuffle(list2);
        System.out.println(list2);
        System.out.println("======================");

 

 

 

15) 연산자 비교

list.get(2) == smaple     ==> 피드에 초기화된 값이 동일해도 주소값은 다르기 때문에 'false'출력

list.get(2).equals(sample))   => 필드에 초기화된 값이 동일할 경우 같은 객체로 인식하기 때문에 'true'출력

		// equals 메소드 오버라이딩 후 Music 객체 비교
		System.out.println(list.get(2));
		
		// == 연산자를 이용한 비교
		Music sample = new Music("ditto", "뉴진스");
		System.out.println(sample);
		System.out.println(list.get(2) == sample);    // 주소값은 다름
		System.out.println(list.get(2).equals(sample));   // 필드에 초기화된 값이 동일할 경우 같은 객체로 인식

 

 

 

16) Object o가 Music타입인지 타입검사 및 hashCode()

- 두 객체간에 필드값이 동일하다면 같은 객체로 인식할 수 있도록 override를 함

- equals 메소드를 오버라이딩한다면 항상 hashCode도 함께 오버라이딩해줘야함
두 객체간에 equals결과가 true라면(같은 필드값을 가지고 있다면), hashCode로 반환된 값도 동일해야함

package com.kh.chap01_list.part01_arrayList.model.vo;

import java.util.Objects;

public class Music implements Comparable<Music>{
	
	private String title;
	private String artist;
	
	public Music() {
		
	}

	public Music(String title, String artist) {
		super();
		this.title = title;
		this.artist = artist;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getArtist() {
		return artist;
	}

	public void setArtist(String artist) {
		this.artist = artist;
	}

	@Override
	public String toString() {
		return "Music [title=" + title + ", artist=" + artist + "]";
	}
	
	/*
	 * equals에서 사용한 필드들을 똑같이 이용하여 hashCode를 생성해줘야함.
	 */
	
	@Override
	public int hashCode() {
		return Objects.hash(title, artist);
	}
	
	/*
	 * 두 객체간에 필드값이 동일하다면 같은 객체로 인식할 수 있도록 override를 함
	 * 
	 * equals 메소드를 오버라이딩한다면 항상 hashCode도 함께 오버라이딩해줘야함
	 * 두 객체간에 equals결과가 true라면(같은 필드값을 가지고 있다면), hashCode로 반환된 값도 동일해야함
	 */
	
	
	@Override
	public boolean equals(Object o) {
		
		// Object o가 Music타입인지 타입검사
		if(!(o instanceof Music)) {
			return false;
		}
		
		Music other = (Music) o;   // 다른 자료형의 객체가 넘어왔다면 형변환 에러가 발생하기 때문
		
		if(!this.title.equals(other.title)) {
			return false;
		}
		
		if(!this.artist.equals(other.artist)) {
			return false;
		}
		
		return true;
		// 현재객체 : this
		// 상대객체 : Object o
	}

	@Override
	public int compareTo(Music o) {
		// 매개변수의 Music객체는 현재객체와 비교하고자하는 객체
		
		// 현재객체 : this
		// 상대객체 : Music o
		
		// 반환값이 0 : 동등, 1: 현재객체 더 높다, -1 : 상대객체가 더 높다
		
		// artist 기준 오름차순 정렬
		
		
		return this.artist.compareTo(o.artist);
	}
}