기록공간

컬렉션 프레임워크 (Collection Framework) - 3 (set, map) 본문

Java

컬렉션 프레임워크 (Collection Framework) - 3 (set, map)

입코딩 2020. 10. 1. 20:34
반응형

Set

Set은 순서가 존재하지 않는 비선형적인 자료구조이다. Set은 중복을 허용하지 않는 구조이다. 컬렉션 프레임워크에서 Set 인터페이스는 앞서봤던 List 인터페이스처럼 단독으로 객체를 만들 수 없기 때문에 하위 클래스인 HashSet, TreeSet을 통해 다운캐스팅하여 만들어야 한다.

 

HashSet

java.util.HashSet<E> 클래스는 Set 인터페이스를 구현하는 클래스로 순서의 개념이 아니라 집합의 개념으로 되어있는 자료구조이다. 이로 인해 데이터의 중복을 허용하지 않으며, 동기화를 지원하지 않는다. (비동기)

Set<String> dog = new HashSet<String>();

// Set 자료구조에 요소 추가
dog.add("코카스파니엘");
dog.add("포메라니안");  
dog.add("골든리트리버");
dog.add("웰시코기");    
dog.add("불독");

for(String s : dog)           
	System.out.print(s + " ");
System.out.println();
//--==>> 포메라니안 골든리트리버 불독 웰시코기 코카스파니엘

다음은 HashSet 컬렉션에 데이터를 넣은 후 순차적으로 출력하는 코드이다. 이렇게 출력하는 경우 개이름을 넣은 순서대로 화면에 나올 것이라 예상해볼수 있다. 하지만 출력 결과를 확인해보면 우리가 넣었던 데이터 순서와 다르다. 

Set<String> dog = new HashSet<String>();

// Set 자료구조에 요소 추가
dog.add("푸들");        
dog.add("요크셔테리어");
dog.add("말티즈");      
dog.add("진돗개");      
                        
dog.add("코카스파니엘");
dog.add("코카스파니엘");
dog.add("코카스파니엘");
dog.add("코카스파니엘");
                        
dog.add("웰시코기");    
dog.add("웰시코기");    
dog.add("웰시코기");    
dog.add("웰시코기");    
                        
dog.add("진돗개");      
                        
dog.add("골든리트리버");
dog.add("골든리트리버");
dog.add("골든리트리버");

for(String s : dog)           
	System.out.print(s + " ");
System.out.println();
//--==>> 포메라니안 골든리트리버 불독 웰시코기 코카스파니엘

이번에는 데이터 요소를 중복해서 넣고 출력하는 코드이다. 하지만 결과는 중복으로 추가된 데이터들을 모두 한 번만 출력되었다. 같은 데이터가 여러 번 반복 추가될 경우에 하나만 유효한 데이터로 구성되어 있는 것을 확인할 수 있다. 또한 추가되는 도중 에러가 발생하지 않는 것을 미루어 보아 Set이 중복 데이터를 허용하지 않는다고 해서 중복 데이터 추가시 오류가 발생하거나 하지는 않다는 것을 알 수 있다. 

 

TreeSet

java.util.TreeSet<E> 클래스는 Set 인터페이스를 상속한 SortedSet 인터페이스를 구현한 클래스로 데이터를 추가하면 데이터들이 자동으로 오름차순 정렬된다. (즉, TreeSet 클래스는 SortedSet 인터페이스를 implement 한 클래스이다)

TreeSet<Integer> ts = new TreeSet<Integer>();
                                             
ts.add(5);                                   
ts.add(3);                                   
ts.add(2);                                   
ts.add(1);                                   
ts.add(4);                                   
                                             
for(Integer num : ts)                        
	System.out.print(num + " ");             
System.out.println();    
//--==>> 1 2 3 4 5

다음 코드처럼 순서를 뒤죽박죽으로 숫자를 넣더라도 TreeSet 내부에서 정렬작업이 이뤄지기 때문에 출력하면 모두 정렬된 상태이다. 

class GradeDTO  // GradeVO
{
	private String hak;			//-- 학번
	private String name;		//-- 이름
	private int kor, eng, mat ; //-- 국어, 영어, 수학

	GradeDTO(String hak, String name, int kor, int eng, int mat)
	{
		this.hak = hak;
		this.name = name;
		this.kor = kor;
		this.eng = eng;
		this.mat = mat;
	}

	GradeDTO()
	{
		
	}

	// getter / setter 구성
	String getHak(){ return hak; }
	String getName(){ return name; }
	int getKor(){ return kor; }
	int getEng(){ return eng; }
	int getMat(){ return mat; }

	int getTot()
	{
		return kor + eng + mat;
	}

	void setHak(String hak){ this.hak = hak; }
	void setName(String name){ this.name = name; }
	void setKor(int kor){ this.kor = kor; }
	void setEng(int eng){ this.eng = eng; }
	void setmat(int mat){ this.mat = mat;}
}

기본적으로 제공하는 자료형(String, Wrapper 클래스 등)의 경우 그것에 맞게 알아서 오름차순 정렬을 해주지만, 사용자가 직접 지정한 클래스의 경우 자바가 오름차순의 기준을 모르기 때문에 사용자가 어떻게 정렬작업을 수행할 것인지 미리 설정해줘야 한다.

// GradeDTO 클래스를 어떠한 기준으로 정렬할 것인지를 
// 비교가 가능하게 사용자가 직접 설정해줘야 한다.
class MyComparator<T> implements Comparator<T>
{
	// TreeSet에 있는 compare를 재정의
	@Override
	public int compare(T o1, T o2)
	{
		GradeDTO s1 = (GradeDTO)o1;
		GradeDTO s2 = (GradeDTO)o2;

		// 이름 기준(오름차순)
		return s1.getName().compareTo(s2.getName());
	}
}

class Test
{
	public static void main(String[] args)
	{
		// TreeSet 자료구조 인스턴스 생성
		TreeSet<GradeDTO> set2 = new TreeSet<GradeDTO>(new MyComparator<GradeDTO>());

		//GradeDTO dto1 = new GradeDTO("2000883", "주재완", 90, 80, 70);

		set2.add(new GradeDTO("2000883", "주재완", 90, 80, 70));
		set2.add(new GradeDTO("2020824", "이준구", 91, 81, 71));
		set2.add(new GradeDTO("2020831", "백해진", 98, 78, 88));
		set2.add(new GradeDTO("2020816", "허수민", 98, 78, 88));
		set2.add(new GradeDTO("2020835", "안혜지", 96, 31, 90));

		// Iterator를 활용한 set 요소 전체 출력
		/*
		Iterator<GradeDTO> it2 = set2.iterator();
		while(it2.hasNext())
			System.out.print(it2.next() + " ");
		System.out.println();
		*/
		
		for(GradeDTO g : set2)
		{
			System.out.printf("학번: %s, 이름: %s, 국어: %2d, 영어: %2d, 수학: %2d, 총점: %3d\n", 
				g.getHak(), g.getName(), g.getKor(), g.getEng(), g.getMat(), g.getTot());
		}
		//--==>> 학번: 2020831, 이름: 백해진, 국어: 98, 영어: 78, 수학: 88, 총점: 264
		//		 학번: 2020835, 이름: 안혜지, 국어: 96, 영어: 31, 수학: 90, 총점: 217
		//		 학번: 2020824, 이름: 이준구, 국어: 91, 영어: 81, 수학: 71, 총점: 243
		//		 학번: 2000883, 이름: 주재완, 국어: 90, 영어: 80, 수학: 70, 총점: 240
		//		 학번: 2020816, 이름: 허수민, 국어: 98, 영어: 78, 수학: 88, 총점: 264
	}
}

 

Map

java.util.Map 인터페이스 또한 Set과 같은 비선형 자료구조이다. Set과 다른 점은 키(Key)를 값(Value)에 매핑(Mapping)하는 기능을 제공한다는 점이다. 때문에 키가 중복되지 않는다면 똑같은 값을 중복으로 적재할 수 있다. 동일한 키를 등록할 수 없고, 유일해야 하며, 각 키는 한개의 값만을 매핑해야 한다. 즉, 하나의 키 값에 대응하는 하나의 값을 갖는 자료구조이다. 

 

하위 클래스에는 Hashtable과 HashMap 등이 있다.

 

Hashtable

Hashtable 클래스는 해시 테이블 구조를 객체 모델링한 클래스이다. 해시 테이블은 키(Key)와 그에 상응하는 데이터(Value)로 구분된 데이터 구조이기 때문에 검색이 용이하여 사용 빈도가 높은 편에 속한다. 데이터를 저장하고, 검색하기 위해서 키(Key)로 접근하며, 이 구조에서는 키 값을 부여하면 해당 데이터가 변환된다. (주의할 점 : Hashtable 클래스는 key 또는 value 값으로 null을 허용하지 않는다)

 

주요 메소드

put(k, v)

private static final String[] names                                
	= {"박민하", "박혜인", "안혜리", "진영은", "김승범", "이진주"};
                                                                   
private static final String[] tels                                 
	= {"010-3208-8216", "010-9050-5317", "010-8458-2671"           
	      ,"010-3650-7828", "010-3241-0260", "010-3013-5469"};     

// Hashtable 자료구조 인스턴스 생성                            
Hashtable<String, String> ht = new Hashtable<String, String>();
                                                               
// ht 라는 Hashtable 자료구조에 배열(names, tels)에 담겨있는   
// 데이터를 요소로 담아내기                                    
// -> put(k, v);                                               
for(int i = 0; i < names.length; i++) // tels.length           
{                                                              
	ht.put(names[i], tels[i]);                                 
}                                                              

put() 메소드는 Hashtable 컬렉션에 값을 넣은 기능을 하는 메소드이다. 키 값 k와 데이터 값 v를 매개변수로 받는다.

 

containsKey()

// 앞내용 생략...

String findName2 = "윤홍준";                                       
if(ht.containsKey(findName2))                                      
	System.out.println(findName2 + " 데이터가 존재합니다.");       
else                                                               
	System.out.println(findName2 + " 데이터가 존재하지 않습니다.");
System.out.println();                                              
//--==>> 윤홍준 데이터가 존재하지 않습니다.    


String findName3 = "이진주";                                       
if(ht.containsKey(findName3))                                      
	System.out.println(findName3 + " 데이터가 존재합니다.");       
else                                                               
	System.out.println(findName3 + " 데이터가 존재하지 않습니다.");
System.out.println();                                              
//--==>> 이진주 데이터가 존재합니다.                               

containsKey() 메소드는 Hashtable 컬렉션에 해당 키 값이 존재하는지 여부를 알려주는 기능을 한다.

 

contains()

// 앞내용 생략...

String findTel1 = "010-7283-1339";                         
if(ht.contains(findTel1))                                         
	System.out.println(findTel1 + " 데이터가 존재합니다.");       
else                                                              
	System.out.println(findTel1 + " 데이터가 존재하지 않습니다.");
System.out.println();                                             
//--==>> 010-7283-1339 데이터가 존재하지 않습니다.                
     
     
String findTel2 = "010-9050-5317";                         
if(ht.contains(findTel2))                                         
	System.out.println(findTel2 + " 데이터가 존재합니다.");       
else                                                              
	System.out.println(findTel2 + " 데이터가 존재하지 않습니다.");
System.out.println();                                             
//--==>> 010-9050-5317 데이터가 존재합니다.                       

contains() 메소드는 Hashtable 컬렉션에 해당 데이터 값이 존재하는지 여부를 알려주는 기능을 한다.

 

remove()

// 생략...

ht.remove("진영은");                                                     
                                                                        
//   삭제(remove()) 이후 key 가 존재하는지 확인                         
if(ht.containsKey("진영은"))                                            
	System.out.println("영은이가 존재합니다.");                         
else                                                                    
	System.out.println("영은이 어디갔어?!!!!");                         
System.out.println();                                                   
//--==>> 영은이 어디갔어?!!!!                                           
                                                                        
//   삭제(remove()) 이후 value 가 존재하는지 확인                       
if(ht.contains("010-3650-7828"))                                        
	System.out.println("010-3650-7828 데이터가 존재합니다.");           
else                                                                    
	System.out.println("010-3650-7828 데이터가 존재하지 않습니다.");    
System.out.println();                                                   
//--==>> 010-3650-7828 데이터가 존재하지 않습니다.                               

remove() 메소드는 매개변수가 키 값이 Hashtable 컬렉션에 존재하는 경우 삭제하는 기능을 한다. 이 때 키 값만 삭제되는 것이 아니라 키 와 매핑되어 있는 데이터 값도 함께 삭제된다.

 

중복된 key 입력

// 중복된 key 입력                                                
ht.put("김승범", "010-1234-1234");                                
                                                                  
System.out.println(ht.get("김승범"));                             
System.out.println();                                             
//--==>> 010-1234-1234                                            
//-- 기본 "010-3241-0260" 에서 "010-1234-1234"로 변경되었음을 확인
//	 (덮어쓰기 개념)                                              

이미 존재하는 데이터의 키 값을 중복으로 추가하는 경우 기존 데이터 값을 새로 추가한 데이터 값으로 덮어쓴다.  

 

HashMap

HashMap 클래스는 Hashtable과 마찬가지로 Map 인터페이스에서 상속받은 클래스이다. HashMap 클래스의 기능은 Hashtable과 동일하지만 동기화 기능이 없기 때문에 동시성 문제가 없다면 (즉, 멀티 스레드 프로그램이 아닌 경우라면) HashMap을 사용하는 것이 성능을 향상시킬 수 있다.

 

또 다른 차이점은 HashMap은 null 값을 허용한다. 

Map<String, String> map = new HashMap<String, String>();                                
                                                                                        
// map 이라는 해시맵 자료구조에 요소 추가                                                                                                                      
map.put("드라마", "이태원클라스");                                                      
map.put("영화", "알포인트");                                                            
map.put("만화", "원피스");                                                              
                                                                                                                                                                                                    
// null 관련 테스트                                                                     
map.put(null, null);                                                                    
map.put("소설", null);                                                                  
map.put(null, "생각하는사람");                                                          
                                                                                        
System.out.println(map);                                                                
//--==>> {null=생각하는사람, 소설=null, 드라마=이태원클라스, 영화=알포인트, 만화=원피스}
//-- 위의 데이터 입력 유형에 따라 모든 종류의 데이터 입력이 가능하지만                  
//   마지막 null 을 key 로 매핑된 "생각하는사람"이                                      
//   최초 null 을 key 로 매핑하는 null을 덮어쓰는 상황이 발생하게 된다.                 
//   즉, HashMap은 null을 하나의 고유한 key 값으로 간주한다. (인식한다.)                
                                                                                        
반응형
Comments