hyeongwoo's blog

[Java] 컬렉션 본문

JAVA

[Java] 컬렉션

toribro 2024. 2. 12. 22:55

자바에서는 여러 데이터들을 저장하기위해서 배열을 사용한다. 하지만 배열의 치명적인 단점이 있는데 한 번 크기를 지정하면 크기를 변경할 수 없고, 변경하고자 한다면 새로운 크기의 배열을 만들고 기존의 값을 전부 복사해야한다.

하지만 컬렉션을 이용하면 크기를 지정할 필요없이 일정 크기가 되면 알아서 크기를 늘려주어 새로운 값은 추가 해주기만 하면 된다.

 자료구조

  자료구조란 데이터(자료)를 메모리에서 구조적(추가, 삭제, 조회, 정렬, 수정)으로 처리하는 방법론이다.  자바에서 자료구 조 및 알고리즘을 구현해 놓은 라이브러리가 있는데 이를 컬렉션이라고한다.

컬렉션

자료구조 개념이 내장되어있는 클래스로 자바에서 제공하는 자료구조를 담당하는 프레임워크이다. 제네릭 기반으로 구형이 되어있다.  자바에서 컬렉션은  java.util  패키지에 내장되어있다.

자바 컬렉션에는 List,Set,Queue Map 인터페이스가 있다. 

  • List 계열  순서를 유지하고 중복 저장 가능하다.
  • Set 계열: 순서를 유지하지 않고 중복저장 하지 않는다.
  • Queue 계열: 선입선출의 자료구조이다. 중복저장은 가능하다.
  • Map 계열: 키와 값의 쌍으로 저장한다. (키는 중복 저장이 안되지만 값은 중복 저장 가능하다.)

Map인터페이스는 Collection<E> 인터페이스를 상속받지않는다.

각 컬렉션의 구현 클래스

 

List<E>

  •  List<E> 계열의 자료형에는 ArrayList,LinkedList,Vector가 있다. 각 자료형을 알아보자.

 

ArrayList<E>

  •  배열 기반 자료구조이다. 배열처럼 메모리에 연속적으로 저장된다.
  •  배열은 크기가 고정이지만, ArrayList는 크기를 다 채우면 자동으로 크기를 늘려준다.
  •  첫번째 원소에 추가,삭제 마지막 원소 추가 삭제, 리스트 특정원소에 접근이 빠르다.
  •  중간 원소의 삽입, 삭제는 오래 걸릴 수 있다. (모든 저장공간을 한칸식 밀고 당기고 해야하기때문)

ArrayList 저장방법

0 1 2 3 4 5

메모리공간에 연속적으로 저장된다.

 

List<E> 계열중 가장 많이 쓰이는 ArrayList의 구현 메소드를 통해 리스트의 기능을 알아보자.

 

ArrayList  요소 추가,삭제,조회

  •   ArrayList에서 요소를 추가,삭제,조회하는 코드이다. 코드를 통해 각 메소드에 대해서 알아본다.
import java.util.ArrayList;

public class Sample{

	public static void main(Strings[] args){
    	ArrayList<String> sArr=new ArrayList<String>();
        
        //add()사용하여 리스트에 요소 추가
        sArr.add("가");
        sArr.add("나");
        sArr.add(1,"다"); //두번째 위치에 "다" 요소 추가
        sArr.add("라");
        sArr.add("마");
        sArr.add("바");
        
        //get()사용하여 특정 인덱스에 있는 값 조회
        System.out.println(sArr.get(0));
        
        //remove()를 사용하여 특정 인덱스나 요소 삭제
        sArr.remove(0); //첫번째 인덱스 요소 삭제
        sArr.remove("가")//"가"를 갖는 요소 삭제
        
        System.out.println(sArr.remove("나"));//요소 삭제후 요소가 삭제되었으면 true 삭제되지
        //않거나 없는 요소이면 false를 반환한다.
    }

}

 

ArrayList  크기

  • size() 메소드를 이용해 ArrayList 크기를 출력한다.
...생략...
System.out.println(sArr.size());//크기 출력

 

ArrayList 요소 포함여부

  • contains(요소)메소드를 이용해 ArrayList에 특정 요소가 포함되어있는지 확인한다.
..생략..
System.out.println(sArr.contains("다"));//"다"를 가지고 있는 요소가 포함 되어있는지 확인 true 나 false를 반환

 

ArrayList 요소 수정

  • set(인덱스,바꿀요소)를 이용해 특정 인덱스 요소를 바꾼다.
..생략..
sArr.set(0,"바");//첫번째 인덱스 요소를 "바"로 변경

 

ArrayList 추출

  • subList(시작인덱스, 마지막 인덱스)를 이용해 시작 인덱스부터 마지막 인덱스-1까지 리스트를 추출하고 새로운 리스트로 반환한다.
..생략..
ArrayList<String> =sArr.subList(0,3); //인덱스 0부터 2까지 리스트 추출후 새로운 리스트로 반환

 

ArrayList 비어있는 값 확인

  • isEmpty()를 이용해 리스트 컬렉션이 비어있는 지 확인하다.
..생략..
sArr.isEmpty()//리스트가 비어있으면 true 요소가 하나라도 존재하면 false를 반환

 

ArrayList 컬렉션 전체 추가

  • addAll(추가할 컬렉션)을 이용해 컬렉션을 통째로 추가시킨다.
..생략..
sArr.addAll(Arrays.asList("그","느","드));//Arrays.asList()를 이용해 리스트를 추가

 

ArrayList 정렬

  • sort()함수를 이용해 리스트의 요소들을 특정 기준으로 정렬한다.

 

ArrayList 전체 조회 (iterator사용)

... 생략..
//반복자 사용
Iterator<String> iter= sArr.iterator();
while(iter.hasNext()){
	System.out.println(iter.next());
}

 

Vector<E>

Vector의 구조와 기능은 ArrayList와 다를게 없다. 메소드도 ArrayList와 같다. 하지만 차이점이 있다면 vector는 멀티 쓰레드 환경에서 동기화를 지원한다. 

동기화

  •  멀티 쓰레드 환경에서 여러 스레드가 동시에 같은 자원에 접근할때 발생할 수 있는 문제를 방지하기위해 사용하는 것이다.
  •  공유 자원에 대한 동시 접근을 제어하여 데이터의 일관성과 무결성을 유지한다.
    • 즉, 한 스레드가 접근시 다른 스레드의 접근을 막느다.

자바에서는 synchronized 키워드를 사용하여 메서드나 블록을 동기화 시킬수있다. 자바 Vector 컬렉션의 메소드에는 synchronized(동기화)된 메소드로 구성되어있어 멀티 스레드 환경에서 동기화를 지원하는 것이다.

Vector는 멀티쓰레드환경에서 동기화만 지원하고 스레드 안전성을 보장하지만 다른 기능은 ArrayList와 같다.  오히려 단일 스레드 상황에서는 성능이 떨어 질 수 있다.

멀티 쓰레드 환경이 아니라면 ArrayList를 사용하는것이 훨씬 빠르다. ArrayList도 동기화를 명시적으로 지정해주면 멀티 쓰레드 환경에서도 사용할 수 있으니 ArrayList를 사용하는 것이 좋고 현대 자바에서는 ArrayList를 사용하는 것이 일반 적이다.

LinkedList<E>

링크 형태로 저장되는 리스트다 

  • 메모리에 연속적으로 저장되지않고 각 요소의 인스턴스들이 다음요소 인스턴스를 참조를 하며 가리킨다. 구현 메소드는 ArrayList와 같다. 
  • 중간 요소의 삽입 삭제시 속도가 빠르다.
  • 특정 요소의 접근은 각 요소가 서로  참조를 하기 때문에 느리다.
  • 저장된 인스턴스의 참조 과정이 복잡하여 전체적으로 ArrayList보다 느릴 수 있다.

구현 메소드는 ArrayList와 같다.

 

 

Set<E>

  • 중복을 허용하지 않는 자료구조
    • Set<E> 계열에는  HashSet ,TreeSet이 있다.

 

HashSet<E>

  • 내부적으로 HashMap을 사용하여 요소를 저장한다. 
  • HashMap은 요소의 해시테이블을 사용하여 저장위치를 결정한다.
  • 해시코드를 사용하여 직접 접근이 가능하기 때문에  검색 속도는 o(1) 의 시간복잡도를 가진다.

 

  • HashSet<E> 추가 삭제 메소드
import java.util.ArrayList;

public class Sample{

	public static void main(Strings[] args){
    
                HashSet<String> hsi =new HashSet<String>();

               //String 에는 equals(), hashcode()메소드가 오버라이딩 되어있으므로 같은 문자열은 같은 객체로 인식

                hsi.add("반갑습니다");//중복
		hsi.add("여러분");//중복
		hsi.add("안녕하세요");
		hsi.add("반갑습니다");//중복
		hsi.add("여러분");//중복
            

                hsi.remove("여러분");//삭제
                System.out.println(hsi);
    
    }

}

 

Set은 인덱스가 존재하지 않으므로 get() (요소 접근) 메소드가 존재하지 않는다. set은 전체를 순회하면서 조회해야한다. Set은 인덱스로 접근이 불가하다.

Hashset<E> 컬렉션 추가

..생략..
hsi.addAll(Arrays.asList("가","나"));
System.out.println(hsi); //"가" ,"나" 를 요소를 가지고있는 리스트를 통째로 추가한다.

 

HashSet<E> 요소 포함여부

..생략..
System.out.println(hsi.contains("d")); //false

 

HashSet<E> 비어있는지 확인

..생략..
System.out.println(hsi.isEmpty()); //false

 

HashSet<E> 전체 조회

... 생략..
// 반복자 사용
Iterator<String> iter= hsi.iterator();
while(iter.hasNext()){
	System.out.println(iter.next());
}

 

 

TreeSet<E>

  •   이진탐색트리 구조 중 성능을 향상시킨 레드-블랙트리 로 이루어져있는 set
  •   데이터 추가 삭제는 시간이 걸리지만 검색과 정렬은 빠르다.
  •   생성자에 Comparator 인터페이스를  넘겨 정렬 방법을 지정해야 할 경우도 있다.
    • TreeSet타입이 커스텀 객체이면 직접 Comparator 인터페이스를 재정의해서 생성자로 넘겨야한다.

 

TreeSet 정렬 예시코드

public class Test {
	
    //커스텀 클래스 정의
	static class Custom {
		
		private String name;

		public Custom(String name) {
			super();
			this.name = name;
		}

		@Override
		public String toString() {
			return name;
		}
	 }
	

	public static void main(String[] args) {
	
		//각 객체를 비교해서 정렬을 시킨다.(String은 기본 오름차순으로 정렬)
		TreeSet<String> tree =new TreeSet<String>();
		tree.add("다");
		tree.add("나");
		tree.add("가");
		tree.add("라");
                System.out.println(tree);
		
		
        // 타입이  커스텀 객체가 들어오면 
        //생성자에 Comparator 인터페이스를 넘겨 정렬 방법을 정해야한다.
        
        //여기서는 람다형식으로 생성자에 Comparator 인터페이스를 넘겼다.
        //Comparator : 반환값이 1이면 순서를 바꾸고 -1이면 바꾸지 않는다.
        
              TreeSet<Custom> customset =new TreeSet<Custom>((e1,e2)->{
                  return (e1.getName().compareTo(e2.getName())>0)?1:-1;
                // 앞문자가 뒤의 문자보다 후순위이면 교체 -> 오름차순 정렬
              );
        
		customset.add(new Custom("타"));
		customset.add(new Custom("아"));
		customset.add(new Custom("바"));
		customset.add(new Custom("가"));
		System.out.println(customset); //오름차순 정렬
		
  }

}

 

삭제,크기,값출력 메소드는  HashSet과 같다.

 

 

 

Map<K,V>

  •  키와 값으로 이루어진 컬렉션인다.
  •  순서를 보장하지 않는다.
  •  키값은 중복 허용을 하지 않느다.

 키와 값을 추가,조회,삭제

 

HashMap<K,V>

  • TreeSet<E> 과 마찬가지로 이진탐색트리 구조를 갖는다.
  • 검색과 정렬속도가 빠르다. O(1)의 시간복잡도를 가진다.
  •  key와 value 으로 이루어진 entry 객체로 저장된다.

 

키와 값을 추가,조회,삭제

import java.util.HashMap;

public class MapTest{

	public static void main(String[] args){
    	
            HashMap<String,Integer> map= new HashMap<String,Integer>();

            //맵 (키,값) 추가
            map.put("가",1);
            map.put("나",2);

            //맵 키값으로 값조회

            //get()함수, 인덱스를 키값으로 value값 출력, 키값 반환형 타입으로 반환한다.
            Integer r1=map.get("가");
            Integer r2=map.get("나");
            System.out.println(r1.intValue());//1
            System.out.println(r2.intValue());//2

            //remove()함수, 해당 키값을 찾아 (키,값) 세트를 삭제한다.
            map.remove("가"); //"가"를 키값으로하는 (키,값)삭제
    
      }


}

 

 

 맵 전체 조회 ,(반복자 사용)

.... 코드 생략...

// 1.keySet()이용
//Map의 키값들을 set으로 변환
Set<String> keys=map.keySet();

Iterator<String> iter = keys.iterator(); //반복자를 사용하여 순회

while(iter.hasnext()){
  String key=(String)iter.next();
  Integer value=map.get(key);
  System.out.println(key+":"+value);
}



//2. entrySet()이용
Set<Entry<String,Integer>> entrySet=map.entrySet();
Iterator<Entry<String,Integer>> itEntry=entrySet.iterator(); //반복자 사용

while(itEntry.hasNext()){
    Entry<String,Integer> entry=(Entry<String,Integer>)itEntry.next();
    String key=(String)entry.getkey();
    Integer value=(Integer)entry.getvalue();
    
    System.out.println(key+":"+value);
}

 

TreeMap<k,y>

  • 키값을 기준으로 정렬되는 map
  • 키캆이 커스텀 객체이면 직접 Comparator 인터페이스를 재정의해서 생성자로 넘겨야한다.
public class Test {
	
	static class Custom {
		
		private String name;

		public Custom(String name) {
			super();
			this.name = name;
		}

		@Override
		public String toString() {
			return name;
		}

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}
		
		
		
		
	}
	

	public static void main(String[] args) {
	
		// 키값 기준으로 정렬한다.
		TreeMap<String ,Custom> treeMap =new TreeMap<String,Custom>();
		treeMap.put("다", new Custom("c"));
		treeMap.put("나", new Custom("d"));
		treeMap.put("가", new Custom("d"));
			
		System.out.println(treeMap);
		
        //키값이 커스텀 객체이면 Comparator인터페이스를 넘겨서 직접 정해야한다.
		TreeMap<Custom ,String> treeMap2 =new TreeMap<Custom,String>((e1,e2)->{
			
			return (e1.getName().compareTo(e2.getName())>0)?-1:1;
			
		});
				
		treeMap2.put(new Custom("c"), "c");
		treeMap2.put(new Custom("d"), "d");
		treeMap2.put(new Custom("e"), "e");
			
		System.out.println(treeMap);
		
	}

}

 

 

 

 

'JAVA' 카테고리의 다른 글

[Java] 접근제한자  (0) 2024.02.13
[Java] 제네릭  (0) 2024.02.12
[Java] final  (0) 2024.02.10
[Java] IO_(입출력스트림)  (0) 2024.02.02
[Java] None_Static영역과 Static영역  (0) 2024.02.02