ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JAVA] Collection
    언어/JAVA 2022. 9. 8. 10:40

    -자료구조를 구현하고 있는 클래스
    -자료구조: 각각의 데이터들을 효율적으로 저장하고 운용하기 위한 방법론을 실제구현하기 위한 분야

    1. 배열
    - 다수개의 요소를 저장할 수 있음, 번호(첨자)에 의해 손쉬운 접근 방법을 제공
    - 크기가 고정, 데이터의 중간삽입과 중간삭제에 비효율적

     

    2. 리스트(List)
    - 크기의 제약없이 데이터를 저장할 수 있음, 데이터의 삽입과 삭제에 최적화
    - 검색에 취약 - (참조값(주소)을 저장하기 때문에) 이를 개선하기 위한 더블 링크드 리스트를 사용하기도 함
    - 데이터 저장 시 불필요한 메모리 사용

     

    자바의 모든 자료구조 클래스(컬렉션 클래스)들은 java.util 패키지를 통해 제공받을 수 있음
    위와 같은 동적 배열을 구현하고 있는 클래스들 - Vector, ArrayList(가장 많이 사용)

    링크드 리스트를 구현하고 있는 클래스 - LinkedList

    3. Set 타입의 저장방법을 구현하고 있는 클래스들
    - Set 타입 : 데이터를 저장할 때, 중복을 허용하지 않는 방법:  검색을 위해서 사용, 중복된 값을 제거하기 위해서 사용
    - HashSet(사용빈도: 중), TreeSet

    4. Map 타입의 저장방법을 구현하고 있는 클래스들
    - Map 타입 : 데이터를 Key와 Value의 형태로 저장하는 방법
    - (검색을 위해서 사용, Key의 값은 중복을 허용하지 않음,  Value의 값은 중복을 허용)
    - Hashtable, HashMap(가장 많이 사용)

    5. Vector, ArrayList 클래스
    - 두개의 클래스는 동일한 기능을 제공
    - 쓰레드 동기화의 지원여부 크기의 제약없이 데이터를 저장(동적으로 크기를 확장)
    - 배열과 같이 인덱스를 기반으로 데이터를 접근
    - 데이터의 중복을 허용
    - 데이터의 입력 순서를 유지

     

    public class Collection01 {
    	public static void main(String[] args) {
    		
    		// 동적 배열의 객체 생성 초기값으로 크기를 지정할 수 있지만 통상 크기 지정 없이 사용
    		Vector v = new Vector();
    		ArrayList a = new ArrayList();
    		
    		// 데이터 입력 add 메소드를 사용하여 데이터를 입력
    		v.add(10);	
    		v.add(20);	
    		v.add(30);
    		// 데이터의 저장은 add로 저장하는 데이터의 레퍼런스 값을 저장.
    		a.add(10);	
    		a.add(20);	
    		a.add(30);
    		// 현재 add 에 사용된 10, 20, 30은 int자료가 아니라  Integer 자료
    		
    		
    		// Vector, ArrayList 클래스의 size 메소드 : 현재 저장되어 있는 데이터의 개수를 반환
    		for(int i=0; i<v.size(); i++)
    			System.out.printf("v[%d] = %d\t\t", i, v.get(i));
    		// Vector, ArrayList 클래스의 get 메소드 : 특정 인덱스에 저장되어 있는 데이터를 반환
    								
    		System.out.println();
    		for(int i=0; i<a.size(); i++)
    			System.out.printf("a[%d] = %d\t\t", i, a.get(i));
    		
    		a.set(2,  100); // 2 번 인덱스의 값을 100으로 수정
    		System.out.println();
    		for(int i=0; i<a.size(); i++)
    			System.out.printf("a[%d] = %d\t\t", i, a.get(i));
    		
    		a.add(2, 300); // 2번을 밀어내고 그잘에 300을 추가
    		System.out.println();
    		for(int i=0; i<a.size(); i++)
    				System.out.printf("a[%d] = %d\t\t", i, a.get(i));
    		
    		a.remove(1);  //  1번 인덱스의 내용 20제거
    		System.out.println();
    		for(int i=0; i<a.size(); i++)
    				System.out.printf("a[%d] = %d\t\t", i, a.get(i));
    	}
    }

     

    컬렉션 클래스 저장 방식
    - 모든 컬렉션 클래스들은 기본적으로 Object 타입을 저장/반환 가능
    - Object 타입을 매개변수 사용하는 모든 컽렉션클래스들은 타입에 상관없이 저장 가능 하지만 저장된 데이터를 반환받는 과정에서 런타임에러가 발생될 수 있음
    - Object -> 자식클래스의타입 : 강제형변환 필요

    public class Collection02 {
    	public static void main(String[] args) {
    		ArrayList a = new ArrayList();	
    		// 다양한 타입을 저장할 수 있는 컬렉션 클래스의 객체
    		a.add(10);   // ArrayList 클래스의 멤버 메서드 public void add( Object obj ) {}
    		a.add(1.1);	
    		a.add("Hello");
    		Integer i0 = (Integer) a.get(0);
    		// 컬렉션 내부의 데이터를 추출하는 과정에서 일치하지 않는 타입으로 형변환이 일어나는 
    		// 경우 런타임 에러가 발생되어 프로그램이 강제 종료
    		// String i2 = a.get(2); // 에러 -  강제형변환없이 실행
    		// Integer i1 = (Integer)a.get(1);  // 에러  - 형변환결과 자료형이 맞지 않음
    		Double i2 = (Double)a.get(1);
    		String i3 = (String)a.get(2);
    		
    		// 위와 같이 하나의 ArrayList 에 여러 자료형태를 섞어서 저장하고 사용 가능.
    		// 다만 자료형을 섞어서 저장하고 사용할 경우 위의 에러 내역과 같이 꺼내고, 
    		// 형변활때 자료형이 맞지 않아 에러가 발생거나 형변환을 하지 않아 에러가 발생 할 확율이 높아짐.
    		// 그래서 보통은 자료형을 하나로만 통일해서 저장하는 형식을 사용하게 되고, 
    		// 규칙으로 만들어 지정한 자료형이 저장되지 못하도록 사용하기도 함
    		ArrayList<Integer> list = new ArrayList<Integer>();
    		//ArrayList list = new ArrayList();	
    		
    		list.add(100);
    		
    		// list.add("String");  // 에러 - 다른 자료형은 저장이 안됨
    		
    		// get()으로 자료를 꺼낼때  강제형변환이 없어도 됨
    		Integer i = list.get(0);  
    		
    		// 제네릭 문법:
    		// 제네릭이 적용이 안된 상태로 동일한 이름의 클래스이지만 객체 생성 시점에 자료형을 지정하여 
    		// 서로 다른 자료형을 지원하는 클래스를 생성할 수 있는 문법(메소드 오버로딩과 유사한 문법)
    		// 제네릭 문법을 사용한 ArrayList 클래스의 객체 생성 예시
    		//ArrayList<Integer> a = new ArrayList<Integer>();
    		//new 다음의 클래스명에서는 제네릭 타입을 생략할 수 있음
    		//ArrayList<Integer> a = new ArrayList<>();
    		//a.add(100);    Integer i = a.get(0);
    	}
    }

     

    HashSet 클래스
    - 데이터의 중복을 허용하지 않고 저장하는 클래스
    - 검색을 위해서 사용되는 클래스(저장할때 hash 연산의 결과로 저장하니, 검색할때도 hash 연산 결과로 검색하여 빠른 검색이 가능)
    - hash 연산 : 클래스 내에서 유일 한값을 얻어 낼수 있는 고유 알고리즘연산. 중복된 값을 제거하면서 데이터를 저장하기 위해 사용

    public class Collection03 {
    	public static void main(String[] args) {
    		Vector<Integer> v = new Vector<>();		
    		v.add(1);	v.add(2);	v.add(2);
    		System.out.println(v.size()); // 중복을 허용하기 때문에 3이 출력
    		
    		ArrayList<Integer> a = new ArrayList<>();
    		a.add(1);	a.add(2);	a.add(2);
    		System.out.println(a.size()); // 중복을 허용하기 때문에 3이 출력
    		
    		HashSet<Integer> h = new HashSet<>();
    		h.add(1);	h.add(2);	h.add(2); 
    		System.out.println(h.size()); // 중복을 허용하기 않기 때문에 2이 출력
    		
    		// 로또 번호 발생 
    		HashSet<Integer> lotto = new HashSet<>();
    		while( lotto.size() < 6) {
    			lotto.add(   (int)(Math.random() * 45) + 1   );
    		} // size 가 6개로 채워질때까지 반복
    		
    		for( Integer i : lotto )
    			System.out.printf("%d  ", i);
    	}
    }

     

    Hashtable, HashMap 클래스
    - 데이터베이스 내부의 키 값을 검색하기 위해서 만들어진 알고리즘을 기반으로 작성된 클래스
    - 검색을 위해서 사용되는 클래스
    - Key, Value를 저장할 수 있는 클래스
    - Key 는 중복을 허용하지 않으나 Value 는 중복을 허용

    //저장 예
    //HashMap hm = new HashMap(); 
    //hm.put(1, "One");	hm.put(2, "Two");	hm.put(3, "Three");
    
    public class Collection04 {
    	public static void main(String[] args) {
    		// Key 값은 문자열, Value 값은 정수로 입력받는 Hashtable 객체 생성
    		Hashtable<String, Integer> ht = new Hashtable<String, Integer>();
    		// Key 값은 정수, Value 값은 문자열로 입력받는 HashMap 객체 생성
    		HashMap<Integer, String> hm = new HashMap<Integer, String>();
    		
    		hm.put(1, "One");
    		// hm.put("One", 1); // 에러 , 객체 생성시에 제약된 제네릭형식에 맞지 않아서
    		hm.put(2, "Two");		
    		hm.put(3, "Three");
    		ht.put("One", 1);	    
    		ht.put("Two", 2);	    
    		ht.put("Three", 3);
    		
    		// 확장 사용 예
    		HashMap<Integer, ArrayList<String> > kk = new HashMap<>();
    		//  key:1   value:{"abcd" , "efg" , "hij"}
    		//  key:2   value:{"kkkk" , "ffff" , "ddddd", "ghghZ" }
    		
    		HashMap<Integer, Object> pp = new HashMap<>();
    		//  key:1   value:"abcd"
    		//  key:2   value:1234
    		//  key:3   value: 23.56
    		//  key:4   value: {"abcd" , "efg" , "hij"}
    		
    		// Hashtable, HashMap 클래스는 get 메소드에  Key 값을 전달하여 Value를 리턴받음
    		System.out.printf("\"Three\" = %d\n", ht.get("Three"));
    		System.out.printf("3 = %s\n", hm.get(3));
    		
    		// Hashtable, HashMap 클래스 내부의 데이터를 반복문을 통해서 접근하는 예
    		// 1. Enumeration 타입을 사용하는 방법
    		// Enumeration 은 열거형 자료형식(인터페이스)
    		
    		Enumeration<String> e1 = ht.keys();  // 키들의 접근 권한을 저장
    		while( e1.hasMoreElements() ) {
    			String key = e1.nextElement();  // 키값 하나를 꺼내서  String 변수에 저장
    			int value = ht.get(key);   // 키값으로 검색한 밸류값을 얻어냄
    			System.out.printf("key(%s)=Value(%d)   ", key, value);
    		}
    		
    		System.out.println();
    		// 2. for 문을 활용하여 Key 값을 순회하는 방법
    		for( Integer k : hm.keySet() ) {   //hm.keySet() : 키값들만 모아서 리스트로 생성
    			String v = hm.get(k);
    			System.out.printf("key(%d)=Value (%s)   ", k, v);
    		}
    	}
    }
    public class Collection05 {
    	public static void main(String[] args) {
    		Hashtable<String, Integer> ht = new Hashtable<>();
    		HashMap<Integer, String> hm = new HashMap<>();
    		
    		ht.put("One", 1);	ht.put("Two", 2);	ht.put("Three", 3);
    		hm.put(1, "One");	hm.put(2, "Two");	hm.put(3, "Three");
    
    		System.out.printf("ht.size() -> %d\n", ht.size());
    		System.out.printf("hm.size() -> %d\n", hm.size());
    		
    		// 중복되지 않은 키 값으로 데이터를 추가
    		ht.put("Four", 4);	ht.put("Five", 5);
    		hm.put(4, "Four");	hm.put(5, "Five");
    		System.out.printf("ht.size() -> %d\n", ht.size());
    		System.out.printf("hm.size() -> %d\n", hm.size());
    		
    		// 키 값은 중복이 없고 밸류값은 중복되게 추가
    		ht.put("Six", 1);	ht.put("Seven", 2);
    		hm.put(6, "One");	hm.put(7, "Two");
    		System.out.printf("ht.size() -> %d\n", ht.size());
    		System.out.printf("hm.size() -> %d\n", hm.size());
    		
    		for( String k : ht.keySet() ) 
    			System.out.printf("k(%s)-v(%d)  ", k, ht.get(k));
    		System.out.println();
    		for( Integer k : hm.keySet() ) 
    			System.out.printf("k(%d)-v(%s)  ", k, hm.get(k));
    		System.out.println();
    		
    		// 키 값이 중복되고, 밸류값도 중복안되게....
    		// 동일한 키값으로 다른 Value가 입력되는 경우 기존의 Value값이 수정됨
    		ht.put("Six", 6);	ht.put("Seven", 7);
    		hm.put(6, "Six");	hm.put(7, "Seven");
    		System.out.printf("ht.size() -> %d\n", ht.size());
    		System.out.printf("hm.size() -> %d\n", hm.size());
    		
    		for( String k : ht.keySet() ) 
    			System.out.printf("k(%s)-v(%d)  ", k, ht.get(k));
    		System.out.println();
    		for( Integer k : hm.keySet() ) 
    			System.out.printf("k(%d)-v(%s)  ", k, hm.get(k));
    		System.out.println();
    	}
    }

     

    사용자 정의 클래스에 equals 메소드가 오버라이딩되지 않은 경우
    - 컬렉션 내부에서 동일한 형태의 객체를 검색 및 비교할 수 없음.
    - 참조값끼리 비교되어 의도와 다른 결과를 얻음

    import java.util.ArrayList;
    
    class Point{
    	private int x;
    	private int y;
    	public Point(int x, int y) { this.x = x;  this.y = y; 	}
    	public String toString() { return "x:" + x + ",y:" + y; }
    	public boolean equals( Object obj ) {
    		if( !(obj instanceof Point) )	return false;
    		Point target = (Point)obj;
    		Boolean result = this.x==target.x  &&  this.y==target.y;
    		return result;
    	}
    }
    
    public class Collection06 {
    	public static void main(String[] args) {
    		ArrayList<Point> list = new ArrayList<Point>();
    		Point p1 = new Point(10, 20);
    		list.add( p1 );
    		list.add( new Point(20, 20) );
    		list.add( new Point(30, 30) );
    		for( Point p : list ) 
    			System.out.println( p.toString() );
    		
    		int index = list.indexOf( new Point(30,30) );
    		// .indexOf() : ArrayList의 멤버 메서드이고, 괄호안에 넣은 객체 내지 값이 
    		// 몇번째 요소로 있는지  인덱스값을 리턴. 없으면 -1을 리턴.
    		System.out.printf("(30, 30)의 위치 : %d\n", index);
    		
    		boolean con = list.contains( new Point(30, 30) );
    		System.out.printf("(30, 30)의 저장 유무 : %b\n", con);
    	}
    }

     

    hashCode  메서드의 오버라이딩은 특별한 경우를 제외하고는 권장하지 않음. 이미 중복값이 발생되지 않도록 철저히 준비된 연산을 다른 연사능로 바꿔서 실행하는 것 자체가 자바의 코딩규칙을 무너뜨릴 가능성이 있기 때문. 다만 오버라이딩은 불가능한 동작이 아니므로 가능은 하다고 이해하면 됨.

     

    Hash 알고리즘: 여러데이터를 대상으로 그 값들이 일정 연산에 의해 서로 절대 같은 위치에 저장되거나하는 일이 없도록 고유값을 결과로 얻는 연산 or 연산 방법

    Hash 기반의 클래스에서 키 값의 중복을 제거하기 위해서 실행되는 과정(키값이 사용자 정의 클래스 객체일때)
    1. 입력된(검색할) 객체의 hashCode 메소드 실행
    2. 반환받은 hashCode 메소드의 결과를  현재 저장되어 있는 모든 객체들의 hashCode 메소드의 반환값과 비교
    3. 만약 동일한 hashCode 메소드의 결과가 존재한다면 equals 메소드를 실행하여 두 객체가 동일한지 비교

    import java.util.HashMap;
    
    class Rect{
    	private int x;
    	private int y;
    	public Rect(int x, int y) { 	this.x = x;	this.y = y;	}	
    	public String toString() { return "x:" + this.x + ",y:" + this.y;}
    	public boolean equals(Object obj) {
    		if( !(obj instanceof Rect) )return false; 
    		Rect target = (Rect)obj;
    		boolean flag = ((this.x == target.x) && (this.y == target.y));
    		return flag;
    	}
    	public int hashCode() {
    		System.out.println("Rect 클래스의 hashCode 실행");
    		return this.x + this.y;
    	}
    }
    
    public class Collection07 {
    	public static void main(String[] args) {
    		HashMap<Rect, Integer> map = new HashMap<>();
    		map.put( new Rect(10, 10) , 10);
    		map.put( new Rect(20, 20) , 20);
    		map.put( new Rect(30, 30) , 30);
    		
    		for( Rect k : map.keySet()) 	
    			System.out.printf("(%s)->%d    \n", k, map.get(k));
    		System.out.printf("\n키값 중 (30, 30)의 유무 : %b\n",map.containsKey( new Rect(30, 30) ) );
    	}
    }

    '언어 > JAVA' 카테고리의 다른 글

    [JAVA] Abstract(추상클래스)  (0) 2022.09.08
    [JAVA] Formatter  (0) 2022.09.08
    [JAVA] String Class  (0) 2022.09.08
    [JAVA] Calendar Class  (0) 2022.09.08
    [JAVA] Object 클래스  (1) 2022.09.08

    댓글

Designed by Tistory.