: 배열과 비슷하게 객체를 인덱스로 관리함
- 배열과 차이점
: 저장 용량이 자동으로 증가
: 객체를 저장할 때 자동 인덱스가 부여됨
: 추가, 삭제, 검색을 위한 다양한 메소드들이 제공됨
: 객체 자체를 저장하는 것이 아니라 객체의 번지를 참조함
→ 그렇기 때문에 동일한 객체를 중복 저장할 수 있는데,
이 경우 동일한 번지가 참조됨
: null도 저장이 가능하며,
이 경우 해당 인덱스는 객체를 참조하지 않음
: List 컬렉션에는 ArrayList, Vector, LinkedList 등이 있음
- List 컬렉션에서 공통적으로 사용 가능한 List 인터페이스의 메소드
: 인덱스로 객체를 관리하기 때문에 인덱스를 매개값으로 갖는 메소드가 많음
: 메소드의 매개 변수 타입과, 리턴 타입에 E라는 타입 파라미터가 있는데,
이것은 저장되는 객체의 타입을 List 컬렉션을 생성할 때 결정하라는 뜻
ex)
List 컬렉션에 객체를 추가할 때에는 add() 메소드를 사용하고,
객체를 찾아올 때에는 get() 메소드를 사용함
객체 삭제는 remove() 메소드를 사용함
List컬렉션에
String 객체를 추가, 삽입, 검색, 삭제하는 방법
List<String> list = ···;
list.add("홍길동1"); //맨 끝에 객체 추가
list.add(1, "홍길동2"); //지정된 인덱스에 객체 삽입
String str = list.get(1); //인덱스로 객체 검색
list.remove(0); //인덱스로 객체 삭제
list.remove("홍길동2"); //객체 삭제
→ List<String>으로 list 변수를 선언함
이것은 List 컬렉션에 저장되는 객체를 String타입으로 하겠다는 의미
따라서 E 타입 파라미터는 String 타입이 되는 것
그래서 add() 메소드의 매개값은 문자열이 되고
get() 메소드의 리턴값은 문자열이 됨
: List 컬렉션에 저장된 모든 객체를 대상으로
하나씩 가져와 처리하고 싶다면 인덱스를 이용하는 방법과
향상된 for문을 이용하는 방법이 있음
1) 인덱스를 이용하는 방법
List 컬렉션의 size() 메소드는
현재 저장되어 있는 객체 수를 리턴함
List<String> list = ···;
//저장된 총 객체 수만큼 루핑
for(int i=0; i<list.size(); i++) {
// i 인덱스에 저장된 String 객체를 가져옴
String str = list.get(i);
}
2) 향상된 for문을 이용하는 방법
List 컬렉션에 저장된 객체 수만큼 반복하면서
객체를 하나씩 str 변수에 대입
//저장된 총 객체 수만큼 루핑
for(String str : list) {
// list → str : String 객체를 하나씩 가져옴
}
: List 인터페이스의 대표적인 구현 클래스
- ArrayList 객체를 생성하는 방법
List<E> list = new ArrayList<E>();+
: ArrayList를 생성하기 위해서는
저장할 객체 타입을 E 타입 파라미터 자리에 표기하고
기본 생성자를 호출하면됨
ex)
String을 저장하는 ArrayList
List<String> list = new ArrayList<String>();
List<String> list = new ArrayList<>();
: 두 번째 코드와 같이 ArrayList의 E 타입 파라미터를
생략하면 왼쪽 List에 지정된 타입을 따라감
따라서 위 두 코드는 동일하게
String을 저장하는 ArrayList 객체를 생성함
: 기본 생성자로 ArrayList 객체를 생성하면
내부에 10개의 객체를 저장할 수 있는 초기 용량을 가지게 됨
: 저장되는 객체 수가 늘어나면 용량이 자동으로 증가함
: ArrayList에 객체를 추가하면 0번 인덱스부터 차례대로 저장됨
: ArrayList에서 특정 인덱스의 객체를 제거하면
바로 뒤 인덱스부터 마지막 인덱스 까지 모두 앞으로 1씩 당겨짐
마찬가지로 특정 인덱스에 객체를 삽입하면 해당 인덱스부터
마지막 인덱스까지 모두 1씩 밀려남
: 저장된 객체 수가 많고, 특정 인덱스에 객체를 추가하거나
제거하는 일이 빈번하다면 ArrayList보다는 LinkedList를 사용하는 것이 좋음
하지만 인덱스를 이용해서 객체를 찾거나 맨 마지막에 객체를 추가하는 경우에는
ArrayList가 더 좋은 성능을 발휘함
👩💻 String 객체를 저장하는 ArrayList
import java.util.*;
public class ArrayListExample {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
// Stirng 객체를 저장
list.add("Java");
list.add("JDBC");
list.add("Servlet/JSP");
list.add(2, "Database");
list.add("iBATIS");
// 저장된 총 객체 수 얻기
int size = list.size();
System.out.println("총 객체수: " + size);
System.out.println();
// 2번 인덱스의 객체 얻기
String skill = list.get(2);
System.out.println("2: " + skill);
System.out.println();
// 저장된 총 객체 수만큼 루핑
for(int i=0; i<list.size(); i++) {
String str = list.get(i);
System.out.println(i + ":" + str);
}
System.out.println();
// 2번 인덱스 객체(Database) 삭제됨
list.remove(2);
// 2번 인덱스 객체(Servlet/JSP) 삭제됨
list.remove(2);
list.remove("iBATIS");
// 저장된 총 객체 수만큼 루핑
for(int i=0; i<list.size(); i++) {
String str = list.get(i);
System.out.println(i + ":" + str);
}
}
}
💻 결과
총 객체수: 5
2: Database
0:Java
1:JDBC
2:Database
3:Servlet/JSP
4:iBATIS
0:Java
1:JDBC
List<E> list = new Vector<E>();
List<E> list = new Vector<>();
: ArrayList와 동일한 내부 구조를 가지고 있음
: Vector를 생성하기 위해서는
저장할 객체 타입을 타입 파라미터로 표기하고
기본 생성자를 호출하면 됨
: ArrayList와 다른 점은
Vector는 동기화된 메소드로 구성되어 있기 떄문에
멀티 스레드가 동시에 Vector의 메소드들을 실행할 수 없고,
하나의 스레드가 메소드를 실행을 완료해야만
다른 스레드가 메소드를 실행할 수 있음
그래서 멀티 스레드 환경에서 안전하게 객체를 추가, 삭제할 수 있음
→ 이것을 스레드에 안전하다고 표현함
import java.util.*;
// 게시물 정보 객체
class Board {
String subject;
String content;
String writer;
public Board(String subject, String content, String writer) {
this.subject = subject;
this.content = content;
this.writer = writer;
}
}
// Board 객체를 저장하는 Vector
public class VectorExample {
public static void main(String[] args) {
List<Board> list = new Vector<Board>();
//Board 객체를 저장
list.add(new Board("제목1", "내용1", "글쓴이1"));
list.add(new Board("제목2", "내용2", "글쓴이2"));
list.add(new Board("제목3", "내용3", "글쓴이3"));
list.add(new Board("제목4", "내용4", "글쓴이4"));
list.add(new Board("제목5", "내용5", "글쓴이5"));
//2번 인덱스 객체(제목3) 삭제
//뒤의 인덱스는 1씩 앞으로 당겨짐
list.remove(2);
//3번 인덱스 객체(제목5) 삭제
list.remove(3);
for(int i=0; i<list.size(); i++) {
Board board = list.get(i);
System.out.println(board.subject +
"\t" + board.content +
"\t" + board.writer);
}
}
}
💻 결과
제목1 내용1 글쓴이1
제목2 내용2 글쓴이2
제목4 내용4 글쓴이4
: List 구현 클래스임
: ArrayList와 사용 방법은 똑같은데,
내부 구조는 완전히 다름
: ArrayList는 내부 배열에 객체를 저장해서 관리하지만,
LinkedList는 인접 참조를 링크해서 체인처럼 관리함
: LinkedList에서 특정 인덱스의 객체를 제거하면 앞뒤 링크만 변경되고
나머지 링크는 변경되지 않음
특정 인덱스에 객체를 삽일할 때에도 마찬가지임
- 객체를 제거할 경우 앞뒤 링크의 수정이 일어나는 모습
: ArrayList는 중간 인덱스의 객체를 제거하면
뒤에 있는 객체의 인덱스가 1씩 앞으로 당겨지기 때문에
빈번한 객체 삭제와 삽입이 일어나는 곳에서는
ArrayList보다 LinkedList가 좋은 성능을 발휘함
List<E> list = new LinkedList<E>();
List<E> list = new LinkedList<>();
: LinkedList를 생성하기 위해서는
저장할 객체 타입을 타입 파라미터(E)에 표기하고
기본 생성자를 호출하면됨
LinkedList가 처음 생성될 때에는 어떠한 링크도
만들어지지 않기 때문에 내부는 비어 있음
👩💻 ArrayList와 LinkedList의 실행 성능 비교
import java.util.*;
/*
* ArrayList와 LinkedList에
* 10,000개의 객체를 삽입하는 데 걸린 시간을 측정한 것
*
* 0번 인덱스에 String 객체를 10,000번 추가하기 위해
* List 인터페이스의 add(int index, Element) 메소드를 이용함
*/
public class LinkedListExample {
public static void main(String[] args) {
List<String> list1 = new ArrayList<String>();
List<String> list2 = new ArrayList<String>();
long startTime;
long endTime;
startTime = System.nanoTime();
for(int i=0; i<10000; i++) {
list1.add(0, String.valueOf(i));
}
endTime = System.nanoTime();
System.out.println("ArrayList 걸린시간: " +
(endTime-startTime) + "ns");
startTime = System.nanoTime();
for(int i=0; i<10000; i++) {
list2.add(0, String.valueOf(i));
}
endTime = System.nanoTime();
System.out.println("LinkedList 걸린시간: " +
(endTime-startTime) + "ns");
}
}
💻 결과
ArrayList 걸린시간: 3824900ns
LinkedList 걸린시간: 3742299ns
→ 끝에서부터(순차적으로) 추가 또는 삭제하는 경우는
ArrayList가 빠르지만,
중간에 추가, 삭제하는 경우에는 앞뒤 링크 정보만 변경하므로
LinkedList가 더 빠름
ArrayLIst는 뒤쪽 인덱스들을 모두
1씩 증가 또는 감소시키는 시간이 필요하므로 처리 속도가 느림