본문 바로가기
Java/Java

컬렉션/List<E>) ArrayList<E>, 주요 메소드, 기본 정렬, 기타 정렬

by 박채니 2022. 3. 31.

안녕하세요, 코린이의 코딩 학습기 채니 입니다.

 

개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.


컬렉션이란?

- 동일한 타입을 묶어 관리하는 자료 구조

 

컬렉션 프레임워크란?

- 리스트, 스택, 큐, 트리 등의 자료구조에 정렬, 탐색 등의 알고리즘을 구조화해 놓은 프레임워크

- 여러 개의 데이터 묶음 자료를 효과적으로 처리하기 위해 구조화된 클래스 또는 인터페이스의 모음

 

컬렉션의 특성에 따라 구분하면 크게 List<E>, Set<E>, Map<K, V>로 나뉩니다.

메모리의 입출력 특성에 따라 기존 컬렉션 기능을 확장/조합한 Stack<E>, Queue<E>도 있습니다.(사진에선 누락)

 


☞ List<E> 컬렉션 프레임워크

- 순서를 유지하고 저장

- 중복 저장 가능

 

List<E>의 주요 메소드

구분 리턴 타입 메소드 명 기능
데이터 추가 boolean add(E element) 매개변수로 입력된 원소를 리스트 마지막에 추가
void add(int index, E element) index 위치에 입력된 원소 추가
boolean addAll(Collection<? Extends E> c) 매개변수로 입력된 컬렉션 전체를 마지막에 추가
boolean addAll(int index, Collection <? Extends E> c) index 위치에 입력된
컬렉션 전체를 추가
데이터 변경 E set(int index, E element) index 위치의 원솟값을
입력된 원소로 변경
데이터 삭제 E remove(int index) index 위치의 원솟값 삭제
boolean remove(Object o) 원소 중 매개변수 입력과 동일한 객체 삭제
void clear() 전체 원소 삭제
리스트 데이터
정보 추출
E get(int index) index 위치의 원솟값을 꺼내 리턴
int size() 리스트 객체 내에 포함된 원소의 개수
boolean isEmpty() 리스트의 원소가 하나도 없는지
여부를 리턴
리스트 배열 변환 Object[] toArray() 리스트를 Object 배열로 반환
T[] toArray(T[] t) 입력매개변수로 전달한 타입의 배열로 변환

 


☞ ArrayList

- 초기 저장 용량은 10으로 자동 설정 (따로 지정도 가능)

저장 용량을 초과한 객체들이 들어오면 자동적으로 늘어남

- 동기화 제공 X

ArrayList는 배열의 형식으로 공간이 생성됩니다.

따라서 데이터 자체가 인덱스 번호를 갖고 있으므로 특정 인덱스 위치의 데이터를 빠르게 찾아낼 수 있습니다. (장점)

 

다만, 중간에 추가/삭제를 할 경우 뒤에 있는 위치(인덱스) 정보를 모두 수정해야 하므로 중간에 추가/삭제할 경우가 많다면 속도가 다소 느릴 수 있습니다. (이런 경우, LinkedList<E> 사용하는 것이 좋음) (단점)

 

 

ArrayList<E> 인터페이스의 객체 생성

ArrayList<제네릭 타입 지정> 참조변수 = new ArrayList<제네릭 타입 지정>();
List<제네릭 타입 지정> 참조변수 = new ArrayList<제네릭 타입 지정>();
Collection<제네릭 타입 지정> 참조변수 = new ArrayList<>();	//우항의 제네릭타입지정은 생략 가능

List<Integer> aList1 = new ArrayList<Integer>();	//초기 저장 용량 = 10(기본)
List<Integer> aList2 = new ArrayList<Integer>(30);	//초기 저장 용량 = 30

제네릭(Generic)이란? (타입 제한)

- 요소 추가 시 해당 타입의 요소만 추가 가능(Type 안정성 보장)

- 요소 가져올 경우, 해당 타입으로 반환

- 기본형 사용 불가

※ 기본적인 개념만 살펴보고 추후 제네릭에 대해서 포스팅하겠습니다.

 

메소드 사용해보기

 

데이터 추가

List<Double> list = new ArrayList<>();
list.add(1.1);
list.add(2.2);
list.add(3.3);
list.add(1.1);
		
//add(T) - 마지막에 추가
//add(index:int, T) - 해당 index에 삽입
list.add(1, 9.9);
		
System.out.println(list);

@콘솔출력값
[1.1, 9.9, 2.2, 3.3, 1.1]

 

//다른 리스트 요소 추가
List<Double> other = new ArrayList<>();
other.add(3.45);
other.add(4.56);
other.add(5.67);
list.addAll(1, other);

@콘솔출력값
3
[1.1, 3.45, 4.56, 5.67, 8.8, 3.3, 1.1]

 

//다른 리스트(Collection)를 인자로 하는 생성자
List<Double> another = new ArrayList<>(list);
System.out.println(another);

@콘솔출력값
[1.1, 3.45, 4.56, 5.67, 8.8, 3.3, 1.1]

 

데이터 변경

//set(index:int, T) - 해당 index의 요소를 대체
list.set(1, 8.8);

@콘솔출력값
[1.1, 8.8, 2.2, 3.3, 1.1]

 

데이터 삭제

//remove(index:int):T - 해당 index의 요소를 제거
list.remove(2);

@콘솔출력값
[1.1, 8.8, 3.3, 1.1]

 

//removed(삭제할 요소Object):boolean - 삭제여부를 리턴
boolean removed = list.remove(2.2);
System.out.println(removed);

@콘솔출력값
false
[1.1, 8.8, 3.3, 1.1]

 

//clear():void 모든 요소 제거
list.clear();

 

특정 요소 확인

//특정요소 포함여부
//contains(Object):boolean - 해당 요소 존재 여부 리턴
boolean isExist = list.contains(1.1);
System.out.println(isExist);

@콘솔출력값
true
[1.1, 8.8, 3.3, 1.1]

 

//특정요소의 index 확인
//indexOf(Object):int - 해당 요소가 몇 번지에 있는 지 확인
int index = list.indexOf(1.1);
System.out.println(index);

@콘솔출력값
0
[1.1, 8.8, 3.3, 1.1]

 

//lastIndexOf(Object):int - 뒤에서부터 처음 검색된 요소의 인덱스가 몇 번지인 지 확인
int lastIndex = list.lastIndexOf(1.1);
System.out.println(lastIndex);

@콘솔출력값
3
[1.1, 8.8, 3.3, 1.1]

 

//equals 오버라이드 : 요소가 모두 일치할 때 두 리스트의 equals는 true를 반환
//요소의 개수, 순서, 번지수별 요소가 모두 같을 때
System.out.println(list.equals(other));
System.out.println(list.equals(another));

@콘솔출력값
false
true

 

정보 추출

//isEmpty():boolean - 리스트가 비어있으면 true
System.out.println(list.isEmpty());

 

모든 요소 열람

//1. for문
for(int i = 0; i < list.size(); i++) {
	System.out.print(list.get(i) + (i != list.size()-1 ? " " : "\n"));
}
		
//2. for-each문
for(Double d : list) {
	System.out.print(d + " ");
}
System.out.println();
		
//3. iterator
Iterator<Double> iter = list.iterator();
while(iter.hasNext()) {
	Double d = iter.next();
	System.out.print(d + " ");
}
System.out.println();

@콘솔출력값
1.1 3.45 4.56 5.67 8.8 3.3 1.1
1.1 3.45 4.56 5.67 8.8 3.3 1.1 
1.1 3.45 4.56 5.67 8.8 3.3 1.1

 

해당 메소드들을 이용하여 배열로 관리했던 학생 정보 관리를 구현해보았습니다.

List<ArryStudent> stuList = new ArrayList<>();
		
//3명 관리
stuList.add(new ArryStudent(1, "홍길동"));
stuList.add(new ArryStudent(2, "신사임당"));
stuList.add(new ArryStudent(3, "이순신"));
		
//2명 추가
stuList.add(new ArryStudent(4, "김철수"));
stuList.add(new ArryStudent(5, "박채니"));
		
//2번지 1명 삭제
stuList.remove(2);
		
//2번지 1명 추가
stuList.add(2, new ArryStudent(10, "개똥이"));
		
System.out.println(stuList);

@콘솔출력값
[ArryStudent [no=1, name=홍길동], ArryStudent [no=2, name=신사임당], 
ArryStudent [no=10, name=개똥이], ArryStudent [no=4, name=김철수],
ArryStudent [no=5, name=박채니]]

훨씬 간편해진 것을 확인할 수 있습니다.


☞ 요소 정렬

1) 기본 정렬(1개) - 해당 타입 클래스에서 Comparable 인터페이스를 구현 → compareTo() 정렬기준 메소드 오버라이딩

2) 기타 정렬(n개) - 별도의 Comparator 인터페이스 구현 클래스 작성 → compare() 정렬기준 메소드 오버라이딩

 

List<Integer> intList = new ArrayList<>();
intList.add(5);
intList.add(4);
intList.add(3);
intList.add(2);
intList.add(1);
		
//오름차순 기본 정렬
Collections.sort(intList);
System.out.println(intList);
		
//기본 정렬 내림차순
Comparator<Integer> comp = Collections.reverseOrder();	//기본정렬을 뒤집기하는 Comparator
Collections.sort(intList, comp);
System.out.println(intList);

@콘솔출력값
[1, 2, 3, 4, 5]
[5, 4, 3, 2, 1]

[5, 4, 3, 2, 1]의 순서를 가진 List가 있다고 가정해보았을 때, sort()를 이용하여 오름차순 정렬을 하였습니다.

내림차순 정렬을 하기 위해서 기본정렬을 뒤집기 하는 Comparator를 이용하여 뒤집기 해주었습니다.

 

 

커스텀 클래스 정렬하기

List<ArryStudent> stuList = new ArrayList<>();
		
stuList.add(new ArryStudent(2, "신사임당"));
stuList.add(new ArryStudent(4, "김철수"));
stuList.add(new ArryStudent(1, "홍길동"));
stuList.add(new ArryStudent(5, "박채니"));
stuList.add(new ArryStudent(3, "이순신"));

이처럼 ArryStudent 클래스의 List들이 위와 같이 들어있을 때 4가지 방식으로 정렬을 할 수 있겠습니다.

 

① no 오름차순 정렬 (기본정렬)

1-1) 해당 클래스에서 Comparable<> 구현

public class ArryStudent implements Comparable<ArryStudent>

 

1-2) compareTo() 메소드 오버라이드

@Override
	public int compareTo(ArryStudent other) {
//		해당 코드와 같음
//		if(this.no < other.no)
//			return -1;
//		else if(this.no == other.no)
//			return 0;
//		else
//			return 1;
		
		return this.no - other.no;
	}

기본정렬 기준 필드를 대상으로 비교하여 음수, 0, 양수를 리턴하며 sort() 내부적으로 해당 리턴 값을 가지고 정렬을 합니다.

 

1-3) 정렬하기

Collections.sort(stuList);
System.out.println(stuList);

@콘솔출력값
[ArryStudent [no=1, name=홍길동], ArryStudent [no=2, name=신사임당], 
ArryStudent [no=3, name=이순신], ArryStudent [no=4, name=김철수], 
ArryStudent [no=5, name=박채니]]

1~5까지 잘 정렬된 것을 확인할 수 있습니다.

 

② no 내림차순 정렬 (기타정렬)

오름차순 정렬이 잘 되어있다면 내림차순 정렬은 쉽습니다.

Comparator<ArryStudent> comp = Collections.reverseOrder();
Collections.sort(stuList, comp);
System.out.println(stuList);

@콘솔출력값
[ArryStudent [no=5, name=박채니], ArryStudent [no=4, name=김철수], 
ArryStudent [no=3, name=이순신], ArryStudent [no=2, name=신사임당], 
ArryStudent [no=1, name=홍길동]]

Comparator를 통해서 오름차순을 뒤집기 해주었습니다.

 

 

③ name 오름차순 정렬 (기타정렬)

기타정렬이므로, Comparable 인터페이스를 구현하여 compareTo() 메소드 오버라이딩하는 기본정렬의 방식으로는 정렬이 어렵습니다.

3-1) Comparator 인터페이스 구현 클래스를 작성

Comparator<ArryStudent> studentNameAscComp = new studentNameAscComp();
public class studentNameAscComp implements Comparator<ArryStudent> {

	@Override
	public int compare(ArryStudent o1, ArryStudent o2) {
	
		return 0;
	}
}

 

3-2) compare() 메소드 오버라이드

@Override
	public int compare(ArryStudent o1, ArryStudent o2) {
		//문자열의 기본 정렬 기준 메소드
		return o1.getName().compareTo(o2.getName());
	}

기준 필드를 비교하여 음수, 0, 양수를 리턴하면 마찬가지로 sort()메소드 내에서 해당 리턴 값을 가지고 정렬을 합니다.

name값은 String이기 때문에 특별한 정렬기준이 없어서 JVM이 제공해주고 있는 String의 기본 정렬 기준 메소드 compareTo()를 이용하여 오버라이드 해주었습니다.

 

3-3) 정렬하기

Comparator<ArryStudent> studentNameAscComp = new studentNameAscComp();
Collections.sort(stuList, studentNameAscComp);
System.out.println(stuList);

@콘솔출력값
[ArryStudent [no=4, name=김철수], ArryStudent [no=5, name=박채니], 
ArryStudent [no=2, name=신사임당], ArryStudent [no=3, name=이순신], 
ArryStudent [no=1, name=홍길동]]

이름 순서대로 잘 정렬되었습니다.

 

④ name 내림차순 정렬 (기본정렬)

마찬가지로 오름차순 정렬이 잘 되어있다면 내림차순 정렬은 쉽습니다.

Comparator<ArryStudent> studentNameDescComp = Collections.reverseOrder(studentNameAscComp);
Collections.sort(stuList, studentNameDescComp);
System.out.println(stuList);

@콘솔출력값
[ArryStudent [no=1, name=홍길동], ArryStudent [no=3, name=이순신], 
ArryStudent [no=2, name=신사임당], ArryStudent [no=5, name=박채니], 
ArryStudent [no=4, name=김철수]]

Comparator 객체를 받는 reverseOrder()를 이용하여 역순으로 뒤집어주었습니다.

 

 

만일 저장한 순서가 중요한 경우라면, 별도의 list를 복제해서 정렬을 해줘야 합니다.

//저장한 순서가 중요한 경우, 별도의 list복제해서 정렬!
ArrayList<ArryStudent> _list = (ArrayList<ArryStudent>)stuList;
ArrayList<ArryStudent> studentCopyList = (ArrayList<ArryStudent>)_list.clone();
Collections.sort(studentCopyList);

System.out.println(studentCopyList);
System.out.println(stuList);

@콘솔출력값
[ArryStudent [no=1, name=홍길동], ArryStudent [no=2, name=신사임당], 
ArryStudent [no=3, name=이순신], ArryStudent [no=4, name=김철수], 
ArryStudent [no=5, name=박채니]]

[ArryStudent [no=1, name=홍길동], ArryStudent [no=3, name=이순신], 
ArryStudent [no=2, name=신사임당], ArryStudent [no=5, name=박채니],
ArryStudent [no=4, name=김철수]]

List 타입으로는 clone()메소드가 제공 되지 않기 때문에 ArrayList타입으로 얕은 복사를 해준 후 (List 타입 → ArrayList 타입으로 다운캐스팅을 해줘야합니다.) clone() 메소드를 이용해 복제를 해줍니다.

이 때 clone()메소드는 Object타입을 반환해주기 때문에 다시 한 번 더 (ArrayList<ArryStudent) ArrayList 타입으로 다운캐스팅을 해줍니다.

 

이렇게 되면 List를 복제하여 관리할 수 있습니다.