Java

Java_Collection Framework, Iterator

유서담 2023. 11. 21. 00:13

Collection Framwork

 

  • 자바가 자료구조를 구현해 놓은 것
    • 자료구조 - 객체의 저장, 삭제, 조회 등의 기능을 제공하는 것
  • 자바의 모든 자료구조 구현 클래스는 Collection 인터페이스를 구현한 클래스
    • Collection 인터페이스에 정의된 모든 기능을 구현하고 있다

 

자바 자료구조 특징

  • 객체만 저장할 수 있다
  • 크기가 가변적이다
  • 다양한 메소드를 지원

 

 

배열과 Collection의 비교

구분 배열 콜렉션
데이터 기본자료형, 객체 객체
길이 불변 가변
저장 인덱스필요 맨 마지막요소의 끝에 자동으로 저장
삭제 인덱스필요 삭제 후 다음요소로 
삭제 후 해당부분 데이터가 비어있음 자동으로 채워짐
Enhanced - for ( 향상된 for문) 배열의 끝까지 반복 요소가 있는 부분까지만 반복

 

Collection<E>

 

  • 모든 자료구조 클래스의 최상위 인터페이스

 

주요 메소드

메소드 종류 메소드 수행 내용
boolean add(E e) 자료구조에 새로운 요소를 추가
boolean addAll(Collection<? extends E> c) 자료구조에 다른 자료구조의 모든 요소를 추가
void clear( ) 자료구조의 모든 요소를 삭제
boolean contains(Object e) 자료구조에 지정된 객체가 존재하는지 조회
boolean isEmpty( ) 자료구조가 비어있는지 확인
Iterator<E> iterator( ) 자료구조의 각 요소를 반복해서 추출해주는 반복자객체를 반환
boolean remove(Object e) 자료구조에서 지정된 객체를 삭제
int size( ) 자료구조에 저장된 요소의 갯수를 반환
Object[ ] toArray( ) 자료구조에 저장된 요소를 배열로 반환

 

 

Collection의 주요 하위 인터페이스

 

Set<E>

 

  • 중복을 허용하지 않는다 ( 동일한 객체를 2개 저장할 수 없다)

 

주요 구현 클래스

 

HashSet<E> : 가장 많이 사용하는 Set 구현 클래스

 

객체의 동일성 비교를 구현하기 위해서 equals( ), hashcode( ) 메소드를 재정의할 필요가 있다( HashSet<E>에 저장되는 객체가 구현해야 한다 )

String, Wrapper 클래스는 equals( )와 hashcode( )가 이미 재정의 되어 있다

 

 

HashSet<E> 사용예제

 

public static void main(String[] args) {
// String 객체를 저장하는 HashSet 객체 생성하기
	HashSet<String> set = new HashSet<String>();
		
	// boolean add(E e) 메소드를 사용해서 HashSet 객체에 String 객체 저장하기
	set.add("홍길동");
	set.add("고길동");
	set.add("길동");
	set.add("노길동");
	set.add("신길동");
	set.add("주길동");
	set.add("말동");

	// 중복으로 인해서 저장되지 않음
    	set.add("말동");
	set.add("말동");
	set.add("말동");
        
    // int size() 메소드를 사용해서 HashSet 객체에 저장된 객체의 갯수 조회하기
	int size = set.size();
	System.out.println("저장된 객체의 갯수: "+size);
    
    // 향상된 for문을 사용해서 HashSet객체에 저장된 객체들을 하나씩 처리하기
	// HashSet은 index가 없다. 일반 for문으로는 HashSet 객체에 저장된 값들을 처리할 수 없다
	System.out.println("향상된 for문으로 HashSet 객체에 저장된 객체 처리");
	for(String s : set) {
		System.out.println(s);
	}
	System.out.println();
	// Stream을 사용해서 HashSet 객체에 저장된 객체들을 하나씩 처리하기
	System.out.println("Stream을 사용해서 HashSet 객체에 저장된 객체 처리");
	Stream<String> stream = set.stream();
	stream.forEach((String s)-> System.out.println(s));
    
    // boolean contains(Object o)를 사용해서 객체의 저장여부를 조회하기
	boolean isExist = set.contains("고길동");
	System.out.println("존재여부: "+isExist);
		
	// boolean isEmpty()를 사용해서 HashSet 객체가 비어있는지 조회하기
	boolean isEmpty = set.isEmpty();
	System.out.println("비어있는지 확인: "+isEmpty);
		
	// void clear() HashSet 객체에 저장된 모든 객체 삭제하기
	set.clear();
		
	System.out.println("비어있는지 확인: "+set.isEmpty());
}

 

위 코드에 대한 결과 출력값

 

 

 

HashSet<E> equals( ), hashCode( ) 사용예제

 

public static void main(String[] args) {
		
	HashSet<User> set = new HashSet<>();
		
	set.add(new User("go", "고길동"));
	set.add(new User("no", "노길동"));
	set.add(new User("park", "박길동"));
	set.add(new User("kim", "김길동"));
	set.add(new User("Oh", "오길동"));
	set.add(new User("shin", "신길동"));
	set.add(new User("nu", "누길동"));
	set.add(new User("nu", "누길동"));
	set.add(new User("nu", "누길동"));
		
	for(User u : set) {
		System.out.println(u.id +", " +u.name);
	}
}
	
static class User {
	String id;
	String name;
		
	public User(String id, String name) {
		this.id = id;
		this.name = name;
	}
		
	/*
		* hashCode()와 equals()메소드를 재정의해서
		* id값이 같은 User객체는 같은 객체로 판단하도록 하였음
		*/
	@Override
	public int hashCode() {
		return Objects.hash(id);
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		User other = (User) obj;
		return Objects.equals(id, other.id) && Objects.equals(name, other.name);
	}
		
		
}

 

위 코드에 대한 결과 출력값

 

 

 

TreeSet<E> : 저장되는 요소가 오름차순으로 정렬되어서 저장

 

TreeSet에 저장하는 객체는 반드시 Comparable<E>를 인터페이스로 구현해야 한다.

String, Wrapper 클래스는 Comparable를 인터페이스를 구현하고 있다

 

 

TreeSet<E> 사용예제

 

public static void main(String[] args) {
	TreeSet<String> set = new TreeSet<String>();
	set.add("고길동");
	set.add("너길동");
	set.add("노길동");
	set.add("하길동");
	set.add("정길동");
	set.add("박길동");
		
	for(String s : set) {
		System.out.println(s);
	}
}

/* 결과는
*	고길동
*	너길동
*	노길동
*	박길동
*	정길동
*	하길동
*/

 

 

TreeSet<E> 사용예제

 

// 로또번호 추출하는 예제

public static void main(String[] args) {
		
	/*
		* 로또번호 추출하기
		* 	- TreeSet<E> 사용
		* 	  1. 중복된 값을 저장하지 않는다
		* 	  2. 값이 오름차순으로 정렬되어 저장된다
		* 
	 */
		
	Random random = new Random();
		
	TreeSet<Integer> lotto = new TreeSet<Integer>();
		
	while (true) {
		int num = random.nextInt(45)+1;
		System.out.println(num);
		lotto.add(num);
			
		if(lotto.size() == 6) {
			break;
		}
	}
		
	System.out.println(lotto);
}

 

결과는 매번 다르게 출력하게 된다

 

 

List<E>

 

  • 순서가 유지된다 ( 저장된 순서대로 다시 꺼낼 수 있다 )
  • 요소가 저장될 때 마다 index가 자동으로 부여된다
  • 특정위치에 요소 저장하기, 특정위치의 요소 삭제하기, 특정위치의 요소 꺼내기가 가능하다

List<E>가 지원하는 추가 메소드

 

메소드 종류 메소드 수행 내용
void add(int index, E e) 지정된 위치에 요소를 저장
E get(int index) 지정된 위치의 요소를 꺼낸다
E remove(int index) 지정된 위치의 요소를 삭제
E set(int index, E e) 지정된 위치의 요소를 새 요소로 교체

 

 

주요 구현 클래스

 

구현 클래스 종류 구현 클래스 수행 내용
ArrayList<E> 가장 많이 사용하는 List 구현 클래스
( 전체 자료구조 클래스 중에서 가장 많이 사용 ) 
LinkedList<E> 더블링크로 List를 구현한 클래스
( 요소의 추가 / 삭제 성능이 우수 ) 
Vector<E> ArrayList와 유사하는 List구현 클래스
( 멀티스레드 환경에 안전 )
Stact<E> LIFO( Last-In-First-Out ) 으로 구현된 List 구현 클래스

 

 

ArrayList<E> : List<E> 인터페이스를 구현한 구현 클래스

 

가변길이 배열을 활용해서 List<E> 인터페이스를 구현.

순서가 보장되는 자료구조 클래스

 

 

ArrayList<E> 사용예제

 

public static void main(String[] args) {
	// String 객체를 여러 개 저장할 수 있는 ArrayList 객체를 생성함
	ArrayList<String> list = new ArrayList<String>();
		
	// boolean add(E e) - ArrayList 객체에 순서대로 객체를 저장한다
	//					 맨 마지막번째 객체 다음에 저장된다
	list.add("홍길동");
	list.add("길동");
	list.add("고길동");
	list.add("신길동");
	list.add("연희동");
	list.add("장발장");
	list.add("홍길동");
	list.add("홍길동");
		
	// int size() : ArrayList 객체에 저장된 객체의 갯수를 반환한다
	int size = list.size();
	System.out.println("저장된 객체의 개수 : " + size);
		
	// 향상된 for문으로 ArrayList 객체에 저장된 String 객체 처리하기
	for(String s : list) {
		System.out.println(s);
	}	
		
}

 

 

위 코드의 결과 출력값

 

 

ArrayList<E> 클래스 메소드 asList(T... t), of(E e) 사용예제

 

public static void main(String[] args) {
		
	// new 키워드를 이용해서 List 인터페이스 구현체인 ArrayList 객체 생성하기
	ArrayList<String> list = new ArrayList<String>();
		
	List<String> list2 = new ArrayList<String>();
	list2.add("고길동");
	list2.add("장발장");
	list2.add("신길동");
		
		
	// Arrays 클래스 메소드 asList(T... t)를 이용해서 List 인터페이스의 구현객체 생성하기
	//(T... t) = 가변길이 매개변수
	List<String> list3 = Arrays.asList("고길동","길동","신길동");
		
		
	// List 인터페이스의 클래스 메소드 of(E e)를 이용해서 List 인터페이스의 구현객체 생성하기
	// 아래의 방법의 생성한 List는 불변객체
	// 새로운 객체를 추가하거나, 기존 객체를 삭제할 수 없다
	List<String> list4 = List.of("홍길동","산길동","산신령");
		
		
	}

 

 

 

Iterator<E> 인터페이스

 

  • 자료구조내의 각 요소를 순회하는 반복자 객체의 표준 인터페이스
  • 모든 자료구조 구현클래스는 자신의 자료구조객체에 저장된 데이터를 하나씩 반복해서 조회할 수 있는 Iterator 객체를 구현하고 있다
  • 모든 자료구조 구현객체는 Iterator 인터페이스를 구현한 Iterator 객체를 제공
  • 모든 자료구조 구현객체가 제공하는 Iterator 구현객체는 구현내용은 다를 수 있지만 사용방법은 동일

= 모든 자료구조 구현객체는 동일한 사용법으로 자료구조내에 저장된 객체를 하나씩 조회할 수 있는 Iterator 구현객체를 제공하는 것

 

 

주요 메소드

메소드 종류 메소드 수행 내용
boolean hasNext( ) 반복할 요소가 남아있는 true를 반환
E next( ) 현재 반복대상이 되는 요소를 꺼낸다
void remove( ) 현재 반복대상이 되는 요소 삭제

 

 

Iterator<E>

 

  • Collection<E>  인터페이스의 모든 구현 클래스들은 Iterator iterator( ) 메소드를 구현

 

  • ArrayList<E> 객체의 iterator( )를 실행하면 ArrayList의 각 요소를 순회하는 Iterator  구현객체가 획득된다
  • HashSet<E> 객체의 iterator( )를 실행하면 HashSet의 각 요소를 순회하는 Iterator  구현객체가 획득된다
  • LinkedList<E> 객체의 iterator( )를 실행하면 LinkedList의 각 요소를 순회하는 Iterator 구현객체가 획득된다

 

Iterator 실행예제

 

public static void main(String[] args) {
		
	List<String> list = Arrays.asList("홍","고","길","동","황");
		
	System.out.println("일반 for문을 사용해서 반복처리하기 - List 계열만 가능");
	int size = list.size();
		
	for(int index = 0; index<size; index++) {
		String name = list.get(index);
		System.out.println(name);
	}
		
	System.out.println("향상된 for문을 사용해서 반복처리하기");
	for (String name : list) {
		System.out.println("이름: "+name);
	}
		
	System.out.println("Iterator를 사용해서 반복처리하기");
	Iterator<String> itr = list.iterator();
	while (itr.hasNext()) {	// hasNext()로 반복할 요소가 남았는지 확인하기
		String name = itr.next();	// next로 요소 꺼내기
		System.out.println(name);
			
	}
		
	System.out.println("Stream을 사용해서 반복처리하기");
	list.stream().forEach(name -> System.out.println(name));
}

 

위 코드에 대한 출력결과

 

 

Iterator 실행중 요소 삭제예제

 

public static void main(String[] args) {
		
	List<String> list = new ArrayList<String>();
	list.add("가");
	list.add("나");
	list.add("다");
	list.add("라");
	list.add("마");
	list.add(null);
	list.add("바");
	list.add("바");

	// 컬렉션의 remove(Object o)로
	// 리스트객체에서 처음으로 발견되는 객체만 삭제한다
	list.remove("바");
	System.out.println(list);
		
	// 향상된 for문으로 반복처리 중에 요소 삭제하기
	// 향상된 for문으로 반복처리하는 도중에 컬렉션에 저장된 객체를 삭제할 수 없다
	for(String name : list) {
		if("바".equals(name)) {
//			list.remove(name); //예외발생 ConcurrentModificationException
		}
	}
		
	// Iterator로 반복처리 중에 요소 삭제하기
	Iterator<String> itr = list.iterator();
	while (itr.hasNext()) {
		String name = itr.next();
		if ("바".equals(name)) {
			itr.remove();
		}
		System.out.println();
	}
		
	// 컬렉션에 대한 삭제를 하는 작업이면 Iterator를 사용 , 삭제하지 않으면 향상된 for문을 사용하는게 편리하다
}

 

위 코드에 대한 출력결과값. "바"가 하나 삭제된 것을 알 수 있다

 

 

결론

 

  • Collection를 구현한 자료구조 구현클래스들은 자신의 자료구조에 저장된 각 요소를 순회하는 자신만의 Iterator 구현객체를 제공한다
  • 각 자료구조 구현클래스들의 Iterator 구현내용은 다를 수 있지만, 사용방법은 동일한 구현 Iterator 객체를 제공한다
  • 자료구조객체에 저장된 데이터를 순회하면서 현재 반복대상이 되는 객체를 삭제해야하는 경우에는 반드시 Iterator를 사용해야 한다
    • 향상된 for문의 내부에서는 자료구조객체에 저장된 객체를 삭제할 수 없다