평범한 연구소

[JAVA] 리스트 List - ArrayList, LinkedList, Vector 본문

JAVA/기본 개념

[JAVA] 리스트 List - ArrayList, LinkedList, Vector

soyeonisgood 2022. 8. 3. 19:03

컬렉션

  • 컬렉션은 제네릭으로 작성하는 것이 원칙이다.

 

주요 컬렉션 인터페이스의 구현 클래스

인터페이스 설명 및 주요 구현 클래스
List<E> - 순서가 있는 데이터의 집합. 요소의 삽입 위치 제어 가능
- 중복 허용
- 주요 구현 클래스: ArrayList, LinkedList, Vector, Stack
Set<E> - 순서를 유지하지 않는 데이터의 집합
- 중복 허용X
- 주요 구현 클래스: HashSet, LinkedHashSet, TreeSet
Map<K,V> - 키(key)와 값(value)의 쌍으로 이루어진 데이터의 집합
- 요소의 저장 순서는 유지X
- 키(key)는 중복 허용X, 값(value)은 다른 키로 중복적인 요소 저장 가능
- 주요 구현 클래스: HashMap, Hashtable, Properties, LinkedHashMap, TreeMap

 

Collection<E> 인터페이스

  • 최상위 인터페이스.

 

List<E> 인터페이스

  • 순서가 있는 컬렉션
  • 요소를 인덱스로 관리, 인덱스로 요소 검색하거나 삭제 가능
  • 동일한 요소(객체)를 중복해서 저장 가능
  • 요소 삽입 위치 제어 가능, 중간에 데이터 추가 및 삭제 가능
  • List컬렉션은 객체자체가 저장되는 것이 아니라 객체의 번지 참조
  • null 저장 가능
  • 배열과 유사하지만, List는 가변적임. 공간 부족하면 자동으로 늘어남.
  • 동기화 지원 - Vector
  • 동기화 지원 안함 - ArrayList, LinkedList

 

ArrayList<E> 클래스

  • List 인터페이스 구현한 클래스
  • 크기 변경 가능한 배열을 구현한 클래소, 초기 용량 10
  • 목록을 저장하기 위해 내부적으로 사용되는 배열의 크기 조작하는 메소드 제공
  • 요소 추가 시 저장 용량 부족하면 자동으로 용량 늘어남
  • 자료 추가 및  삭제 시 임시 배열 만들어 자료를 복사하는 방법을 이용
  • 대량의 자료 추가하거나 삭제 시, 내부적으로 처리량 늘어나면서 성능 저하
  • 각 데이터는 인덱스를 가지므로, 한번에 참조가 가능하여 데이터 검색에 유리
  • 멀티스레드 환경에서 동기화X. 이럴땐 Vector 이용.
  • 동기화 되지 않는 것 제외하고 Vector 클래스와 유사.
  • 검색 시 속도 빠름. (LinkedList보다)
  • 가장 배열에 유사한 구조

LinkedList

  • List 인터페이스 및 Deque 인터페이스 구현 클래스
  • 동기화 되지 않음
  • 검색은 속도가 느림
  • 데이터 추가 및 삭제 시 ArrayList처럼 불필요한 복사 일어나지 않아 처리 속도 빠름
  •  중간에 삽입 삭제시는 현저히 속도가 떨어짐 -> 이럴 경우 ArrayLis
    • 앞, 뒤 추가 삭제가 빈번한 경우 효율적

 

리스트 주요 메소드 예제

  • list.add(): 요소 추가
  • list.size(): 요소의 갯수
  • list.set(): 값 변경
  • list.indexOf(): 지정한 요소를 첫 발견한 인덱스
  • list.lastIndexof(): 지정한 요소를 마지막으로 발견한 인덱스
  • list.contains(): 객체 존재 여부
  • list.get(): 요소 반환 (가져옴)
  • list.clear(): 전체 삭제
  • list.remove(): 지정한 요소만 삭제
List<String> list = new ArrayList<>(); // up-casting
String s;
		
// 마지막에 요소 추가
list.add("서울");
list.add("부산");
list.add("대구");
list.add("인천");
list.add("서울"); // 증복 요소 추가 가능
list.add("대전");
System.out.println(list); // [서울, 부산, 대구, 인천, 서울, 대전]

// 인덱스4에 데이터 추가
list.add(4, "광주");
System.out.println(list); // [서울, 부산, 대구, 인천, 광주, 서울, 대전]

System.out.println("개수: "+list.size()); // 7

// 처음 데이터를 대한민국으로 변경
list.set(0, "대한민국");
System.out.println(list); // [대한민국, 서울, 부산, 대구, 인천, 광주, 서울, 대전]


int idx;
// 인천은 몇 번째?
idx = list.indexOf("인천");
System.out.println("인천 위치: "+idx);
		
// 세종 몇 번째?
idx = list.indexOf("세종");
System.out.println("세종 위치: "+idx); // 없으면 -1
		
// 서울 몇 번째?
idx = list.indexOf("서울");
System.out.println("서울 위치: "+idx); // 1 앞에 있는 서울의 위치
idx = list.lastIndexOf("서울");
System.out.println("서울 위치(뒤에서부터): "+idx); // 1 앞에 있는 서울의 위치

// 부산이 있나?
boolean b = list.contains("부산");
System.out.println("부산이 있나 ? "+b);
	
// 대한민국 삭제
// list.remove("대한민국");
list.remove(0);
System.out.println(list);
  • 전체 리스트 출력과 역순 출력
  • iterator()
System.out.println("전체 출력 방법1.");
for(int i=0; i<list.size(); i++) {
	s = list.get(i);
	System.out.println(s+" ");
}
System.out.println();
		
System.out.println("전체 출력 방법2.");
for(String str : list) {
	System.out.println(str+" ");
}
System.out.println();
		
// 반복자
System.out.println("전체 출력 방법3.");
Iterator<String> it = list.iterator();
while(it.hasNext()) {
	String str = it.next();
	System.out.println(str+" ");
}
System.out.println();
		
// 역순 출력
System.out.println("역순 출력 방법1.");
for(int i=list.size()-1; i>=0; i--) {
	s = list.get(i);
	System.out.println(s+" ");
}
		
// 역순 출력 2
// ListIterator: 순방향과 역방향 모두 가능
// ListIterator<String> it2 = list.listListIterator(); // 순방향으로 검색할 때
ListIterator<String> it2 = list.listIterator(); // 반복자를 가장 뒤로 보냄
while(it2.hasPrevious()) {
	String str = it2.previous();
	System.out.println(str+" ");
}
System.out.println();

 

String 배열을 List로 변환 

  • asList()  (java.util.Arrays)
    • List<String> list = Arrays.asList(ss);  // ss={"A","B","C"}
  • Collections.addAll()
    • List<String> list = Arrays.asList(ss);
    • Collections.addAll(list, ss);
  • addAll()   (List 인터페이스)
    • List<String> list = Arrays.asList(ss);
    • list.addAll(Arrays.asList(ss));
  • Stream   (Java 8부터)
    • List<String> list = Arrays.stream(ss).collect(Collectors.toList());

 

List를 String배열로 변환 

  • toArray()  (java.util.List)
    • List<String> list = new ArrayList<>();
List<String> list1 = new ArrayList<>();
list1.add("자바");
list1.add("오라클");

List<String> list2 = new LinkedList<>();
list2.add("HTML");
list2.add("CSS");

 

ArrayList, LinkedList 속도 차이

  • 뒤부터 요소가 추가되는 add()를 사용하면,  ArrayList가 조금 더 빠르다.
  • 요소를 앞에서부터 추가할 때는 LinkedList가 매우 빠르다.
public class Ex04 {

	public static void main(String[] args) {
		List<String> list1 = new ArrayList<>();
		List<String> list2 = new LinkedList<>();
		
		sub1("LinkedList", list1); // 14281700
		sub1("ArrayList",list1);  //  10123900
		
		// 앞에서 추가
		// LinkedList는 앞에 추가할 때 많이 빠름
		sub2("LinkedList", list2); // 8401000
		sub2("ArrayList",list2); // 22707900


	}
	
	public static void sub1(String title, List<String> list) { // 뒤에 추가
		long s, e;
		
		s = System.nanoTime();
		for(int i=0; i<200000;i++) {
			list.add(String.valueOf(i));
		}
		e = System.nanoTime();
		
		System.out.println(title+" : "+(e-s));
		
		list.clear();
	}
	
	public static void sub2(String title, List<String> list) { // 앞에 추가
		long s, e;
		
		s = System.nanoTime();
		for(int i=0; i<200000;i++) {
			list.add(0, String.valueOf(i));
		}
		e = System.nanoTime();
		
		System.out.println(title+" : "+(e-s));
		
		list.clear();
	}

}
public static void main(String[] args) {
	Queue<String> q = new LinkedList<>();
		
	q.offer("자바");
	q.offer("오라클");
	q.offer("서블릿");
	q.offer("스프링");
	q.offer("자스");

	// hean 값 반환 후 삭제
	while(q.peek()!=null) { // peek(): head 반환. 없으면 null
		String s = q.poll(); // pool(): head 반환 후 삭제.
		System.out.println(s);
	}
		
}

 

<Vector >

  • List 인터페이스 구현 클래스
  • ArrayList와 거의 동일
  • 멀티스레드 환경에서 동기화 된다
  • 동기화 필요 없는 경우, ArrayList 사용을 권장
  • Vector만 갖는 메소드
    • list.capacity(): 현재 벡터의 용량 반환
    • list.firstElement() = list.get(0)
    • list.lastElement() = list.get(list.size()-1)
    • list.trimToSize(): 저장된 객체 갯수만큼 용량 최소화
    •  
    public static void main(String[] args) {
		Vector<String> list = new Vector<>();
		System.out.println("초기용량: "+list.capacity()); // 10
		
		list.add("a1");
		list.add("a2");
		list.add("a3");
		list.add("a4");
		list.add("a5");
		list.add("a6");
		list.add("a7");
		list.add("a8");
		list.add("a9");
		list.add("a10");
		list.add("a11");
		list.add("a12");
		list.add("a13");
		list.add("a14");
		list.add("a15");
		
		System.out.println("개수: "+list.size()); // 15
		System.out.println("용량: "+list.capacity()); // 20. 용량이 부족하면 10씩 늘어남
	
		list.add(0, "korea");
		System.out.println(list);
		
		System.out.println("처음: "+list.get(0));
		System.out.println("처음: "+list.firstElement());
		
		System.out.println("마지막: "+list.get(list.size()-1));
		System.out.println("마지막: "+list.lastElement());
		
		list.remove(3);
		list.remove(7);
		
		list.clear();
		System.out.println("개수: "+list.size()); // 0
		System.out.println("용량: "+list.capacity()); // 20
		
		// 용량을 개수로 줄이기
		list.trimToSize();
		System.out.println("용량: "+list.capacity()); // 0		
		
	}
Vector<String> vv = new Vector<>();
vv.add("자바");
vv.add("스프링");
vv.add("오라클");
		
Enumeration<String> e = vv.elements();
while(e.hasMoreElements()) {
	String s = e.nextElement();
	System.out.println(s+" ");
}
System.out.println();

 

 

<Stack>

  • Vector<E> 클래스 상속 받은 하위 클래스
  • LIFO 구조 갖는 스택을 구현한 클래스
  • push, pop 작업 및 스택의 가장 위 객체 확인하거나 비어있는지 확인
  • Stack(): 빈 스택 생성
  • peek(): 맨 위 객체 반환 후 제거X
  • pop(): 맨 위 객체 반환 후 제거
  • push(): 스택에 데이터 추가
public class Ex06 {

	public static void main(String[] args) {
		Stack<String> ss = new Stack<>();
		
		// 스택에 데이터 추가
		ss.push("서울");
		ss.push("부산");
		ss.push("대구");
		ss.push("인천");
		ss.push("광주");
		ss.push("대전");
		
		while(! ss.empty()) {
			String s = ss.pop();
			System.out.print(s+" ");
		}
		System.out.println();

	}

}