ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java Comparable, Java Comparator 자바 정렬 개념 정리
    IT/Java 2019. 5. 12. 19:01

    #개요

    자바 정렬 관련해서 여기저기 포스트를 찾아보다 가장 직관적으로 잘 설명된 블로그를 발견했다.

    나도 겉핥기식으로만 알고있었던 지라 한번 따라하면서 정리할 겸 별도로 내 식대로 정리한다.

    아래 블로그의 내용 기반으로 살을 붙혀 정리했다.

     

    http://cwondev.tistory.com/15

     

     

     

     

     


    #서론

    Array나 List, Map 등과 같은 Collection 객체를 사용하다 보면 정렬을 해야할 때가 있다.

    Collections 클래스의 sort()를 이용하면 기본 정렬이 가능하다.

    이 sort()는 Comparable 구현에 의해 정렬된 것인데, 이 Comparable과 Comparator에 대해 결과를 보며 설명을 하려한다

     

     

     

     


    #본론

    1. Comparable - 인터페이스

     

    배열 , ArrayList 모두 활용해서 예제를 통해 결과를 보자.

     

    1.1 배열의 정렬

    package compare;
    import java.util.Arrays;
    public class PlantList {
    	public static void main(String args[]) {
    		String[] plantEng = new String[] {
    			"Rose","Primrose","Daisy","Tulip","Canation","Bluebell","Cactus","Thistle","Ivy"
    		}
    		;
    		String[] plantKor = new String[] {
    			"고사리","잔디","버섯","카네이션","물망초","백합","해바라기","장미","수련"
    		}
    		;
    		Arrays.sort(plantEng);
    		Arrays.sort(plantKor);
    		for (int i=0; i<plantEng.length; i++) {
    			System.out.print(plantEng[i]+" / ");
    		}
    		System.out.println();
    		for (int i=0; i<plantKor.length; i++) {
    			System.out.print(plantKor[i]+" / ");
    		}
    	}
    }

     

     

     

    Arrays.sort()에 배열을 넣으면 위 결과와 같이 영어는 ABC... 한글은 가나다... 순으로 정렬된다.

     

    구체적으로 살펴보면 StringIntegerDateFile 등과 같이 동일한 타입의 인스턴스끼리 비교할 수 있는 클래스들은

    Comparable 인터페이스를 가지고있다.

     Comparable 인터페이스를 가지고 있는 객체의 배열은 Arrays.sort(배열) 를 통해 정렬할 수 있다.

     

    그리고 위에서 언급한 같은타입의 인스턴스끼리 비교할 수 있는 클래스들(String, Integer, Date, File...)들은

    기본정렬기준이 작은 값에서 큰 값의 순서... 즉 오름차순으로 정렬되도록 정의되어 있다.

     

    sort()에 대해서 조금 더 정확한 개념 및 원리를 알고싶으면 아래 오라클 공식문서에서 Arrays 클래스의 sort() 매개변수와 설명을 참고하면 된다.

    https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html

     

    Arrays (Java Platform SE 7 )

    Returns a string representation of the "deep contents" of the specified array. If the array contains other arrays as elements, the string representation contains their contents and so on. This method is designed for converting multidimensional arrays to st

    docs.oracle.com

     

     

    1.2 ArrayList의 정렬

     

    package compare;
    import java.util.ArrayList;
    import java.util.Collections;
    public class PlantList2 {
    	public static void main(String args[]) {
    		ArrayList<String> plantList = new ArrayList<String>();
    		plantList.add("고사리");
    		plantList.add("잔디");
    		plantList.add("버섯");
    		plantList.add("카네이션");
    		plantList.add("물망초");
    		plantList.add("백합");
    		plantList.add("해바라기");
    		plantList.add("장미");
    		plantList.add("수련");
    		Collections.sort(plantList);
    		for (int i=0; i<plantList.size(); i++) {
    			System.out.print(plantList.get(i)+" / ");
    		}
    	}
    }

     

     

     

    ArrayList의 경우, Arrays.sort()가 아닌 Collections.sort() 로 정렬한다.

    이 역시 가나다... 순 즉, 오름차순으로 정렬되었다.

     

    마찬가지, 구체적으로 살펴보면 Collections 클래스 안에 정의된 sort()의 매개변수로

    List<T> 를 받아오는데, 이 List 의 클래스(String,int...)의 Comparable 에 따라 위에 언급한 기본정렬기준으로 정렬된다.

     

    위 예시의 경우 ArrayList가 List<String>의 구현체기 때문에 String의 기본기준으로 정렬된다.

     

    Comparable 인터페이스의 공식문서를 보면 아래의 클래스들은 Comparable 인터페이스를 구현하고 있다고 나와있다.

    잘 찾아보면 우리가 잘 아는  String, Integer, Date.... 등이 포함 있다.

     

    ======================

    AbstractRegionPainter.PaintContext.CacheModeAccessModeAclEntryFlagAclEntryPermissionAclEntryTypeAddressingFeature.ResponsesAuthenticator.RequestorTypeBigDecimalBigIntegerBooleanByteByteBufferCalendarCertPathValidatorException.BasicReasonCharacterCharacter.UnicodeScriptCharBufferCharsetClientInfoStatusCollationKeyComponent.BaselineResizeBehaviorCompositeNameCompoundNameCRLReasonCryptoPrimitiveDateDateDesktop.ActionDiagnostic.KindDialog.ModalExclusionTypeDialog.ModalityTypeDoubleDoubleBufferDropModeElementKindElementTypeEnumFileFileTimeFileVisitOptionFileVisitResultFloatFloatBufferFormatter.BigDecimalLayoutFormFormSubmitEvent.MethodTypeGraphicsDevice.WindowTranslucencyGregorianCalendarGroupLayout.AlignmentIntBufferIntegerJavaFileObject.KindJTable.PrintModeKeyRep.TypeLayoutStyle.ComponentPlacementLdapNameLinkOptionLocale.CategoryLongLongBufferMappedByteBufferMemoryTypeMessageContext.ScopeModifierMultipleGradientPaint.ColorSpaceTypeMultipleGradientPaint.CycleMethodNestingKindNormalizer.FormNumericShaper.RangeObjectNameObjectStreamFieldPKIXReasonPosixFilePermissionProcessBuilder.Redirect.TypeProxy.TypePseudoColumnUsageRdnResource.AuthenticationTypeRetentionPolicyRoundingModeRowFilter.ComparisonTypeRowIdLifetimeRowSorterEvent.TypeService.ModeShortShortBufferSOAPBinding.ParameterStyleSOAPBinding.StyleSOAPBinding.UseSortOrderSourceVersionSSLEngineResult.HandshakeStatusSSLEngineResult.StatusStandardCopyOptionStandardLocationStandardOpenOptionStandardProtocolFamilyStringSwingWorker.StateValueThread.StateTimeTimestampTimeUnitTrayIcon.MessageTypeTypeKindURIUUIDWebParam.ModeWindow.TypeXmlAccessOrderXmlAccessTypeXmlNsForm

    ==============================

     

     

     

    1.3 사용자클래스의 정렬기준 정의

     

    지금부터는 기본정렬기준이 아닌 다른 정렬기준으로 시도해보자.

    누구나 한번 쯤 해본 국민게임 메이플스토리 유저 정보를 담는 클래스를 정의하고, 메이플유저의 객체배열을 이용해 정렬을 해보자.

     

    package compare;
    public class MapleStoryUser {
    	private String nickName;
    	// 닉네임
    	private String guildName;
    	// 길드명
    	private int level;
    	//레벨
    	private int money;
    	//보유 메소
    	//생성자
    	public MapleStoryUser(String nickName, String guildName, int level, int money) {
    		this.nickName = nickName;
    		this.guildName = guildName;
    		this.level = level;
    		this.money = money;
    	}
    	//Getter Setter
    	public String getNickName() {
    		return nickName;
    	}
    	public void setNickName(String nickName) {
    		this.nickName = nickName;
    	}
    	public String getGuildName() {
    		return guildName;
    	}
    	public void setGuildName(String guildName) {
    		this.guildName = guildName;
    	}
    	public int getLevel() {
    		return level;
    	}
    	public void setLevel(int level) {
    		this.level = level;
    	}
    	public int getMoney() {
    		return money;
    	}
    	public void setMoney(int money) {
    		this.money = money;
    	}
    }

     

    메이플 유저 클래스는 닉네임, 길드명, 레벨, 보유메소 속성을 가지고 있다.

    여기서 만약 MapleStoryUser 객체를 생성하고, 위와 동일하게

    "배열 : Arrays.sort()" 또는 "ArrayList : Collections.sort()" 를 시도하는 경우, 오류가 발생한다.

    이유는 Comparable 인터페이스를 구현하지 않았기 때문이다.

     

     

    그럼 Conparable 인터페이스를 가져와보자.

     

     

     

    위와 같이 빨간줄이 생기는데 이유는... compareTo() 라는 추상메서드가 존재한다고 한다.

    즉..  Comparable 인터페이스를 사용하기 위해서는 compareTo() 를 구현해줘야 한다.

    감이 오겠지만 위에 이미 Comparable을 구현하고 있는 Java기본 제공클래스인 String, Integer, Date... 는

    저런 compareTo가 이미 정의되어 있는것이다.

     

     

    따라서 우리가 임의로 만든 클래스에서도 Comparable 인터페이스를 구현하기 위해서는 compareTo()를 재정의해줘야 한다.

    닉네임순으로 정렬하고 싶다면 아래와 같이 nickname 으로 compareTo를 재정의한다.

     

    package compare;
    public class MapleStoryUser implements Comparable<MapleStoryUser> {
    	private String nickName;
    	// 닉네임
    	private String guildName;
    	// 길드명
    	private int level;
    	//레벨
    	private int money;
    	// 보유 메소
    	//생성자
    	public MapleStoryUser(String nickName, String guildName, int level, int money) {
    		this.nickName = nickName;
    		this.guildName = guildName;
    		this.level = level;
    		this.money = money;
    	}
    	//Getter Setter
    	public String getNickName() {
    		return nickName;
    	}
    	public void setNickName(String nickName) {
    		this.nickName = nickName;
    	}
    	public String getGuildName() {
    		return guildName;
    	}
    	public void setGuildName(String guildName) {
    		this.guildName = guildName;
    	}
    	public int getLevel() {
    		return level;
    	}
    	public void setLevel(int level) {
    		this.level = level;
    	}
    	public int getMoney() {
    		return money;
    	}
    	public void setMoney(int money) {
    		this.money = money;
    	}
    	@Override
    	    public int compareTo(MapleStoryUser user) {
    		return this.nickName.compareTo(user.getNickName());
    	}
    }

     

     

    compareTo는 비교하는 메서드로써, 좌측에 있는 값과 매개변수로 들어오는 값을 비교하여

    동일하면 0, 기존값이 크면 1, 매개변수값이 크면 -1을 반환한다.

    위에 다루었던 Arrays.sort() 나 Collections.sort()가 이 compareTo()의 리턴값을 이용하여 순서를 바꾸는 것이다.

     

    어찌됐든 메이플유저들을 정렬할때 이름순으로 정렬하고 싶다면 위와같이 name으로 compareTo() 를 재정의해주면 된다.

    만약 하파타카... 와 같이 역순으로 정렬해주고 싶다면 this.nicnName 과 user.getNickName() 의 위치를 바꿔주면 되겠다.

     

     

     

     

    자.. 이제 메이플 유저들을 잔뜩 만들어 정렬을 한번 해보자.

     

    package compare;
    import java.util.ArrayList;
    import java.util.Collections;
    public class MapleStoryUserList {
    	public static void main(String args[]) {
    		ArrayList<MapleStoryUser> userList = new ArrayList<MapleStoryUser>();
    		MapleStoryUser user1 = new MapleStoryUser("메이플운영자","운영진길드" ,250 , 9999999);
    		MapleStoryUser user2 = new MapleStoryUser("헤네시스소음","모바일길드" ,173 , 980000);
    		MapleStoryUser user3 = new MapleStoryUser("커닝미세먼지","모바일길드" ,199 , 1300000);
    		MapleStoryUser user4 = new MapleStoryUser("엘리니아벌레","" ,83 , 3200000);
    		MapleStoryUser user5 = new MapleStoryUser("페리온치약","파이썬길드" ,125 , 2600000);
    		MapleStoryUser user6 = new MapleStoryUser("썬콜의파워","자바길드" ,143 , 1900000);
    		MapleStoryUser user7 = new MapleStoryUser("표도손목스냅","" ,230 , 1100000);
    		MapleStoryUser user8 = new MapleStoryUser("전사의삼두근","인바디클럽" ,207 , 2500000);
    		MapleStoryUser user9 = new MapleStoryUser("궁수의복싱","복싱길드" ,221 , 600000);
    		MapleStoryUser user10 = new MapleStoryUser("드디어마지막","힘들다길드" ,225 , 1620000);
    		userList.add(user1);
    		userList.add(user2);
    		userList.add(user3);
    		userList.add(user4);
    		userList.add(user5);
    		userList.add(user6);
    		userList.add(user7);
    		userList.add(user8);
    		userList.add(user9);
    		userList.add(user10);
    		Collections.sort(userList);
    		for (int i=0; i<userList.size(); i++) {
    			System.out.print(userList.get(i).getNickName() + " / ");
    		}
    	}
    }

     

     

     

     

    이름을 작명하다 어느순간 너무 귀찮았는데 어찌어찌 10개의 유저를 만들고 ArrayList에 추가했고,

    정렬을 시도하니 MapleStoryUser 클래스에 재정의된 compareTo() 메서드에 따라 이름이 오름차순으로 정렬되었다.

     

    이와 같이 본인이 필요한 기준을 정해 compareTo() 메서드에 정의해주면 된다.

     

     

    추가적으로, 위에 설명된 Comparable을 구현하는 클래스의 리스트를 보면.. int는 없다.

    즉.. 누가 제일 그지인지 알고싶다고 money에다가 compareTo()를 사용해보면 오류가 난다.

    int는 아래와 같이 Integer로 형변환을 해주어야 한다.

     

    @Override
        public int compareTo(MapleStoryUser user) {
    	return ((Integer)money).compareTo((Integer​)(user.getMoney()));
    }

     

     

    그럼 위와같이 그지들의 돈을 순서대로 정렬해준다.

     

     

     

    지금까지 ArrayList를 이용하여 실험을 했는데, 동일한 개념으로 배열을 만들어 테스트해도 위의 개념이 그대로 적용된다.

    스냅샷을 만들기 조금 귀찮아졌으니 간단하게 결과사진으로 대체한다.

     

     

     

     

     

     

     

    2. Comparator - 클래스

     

    지금까지 Comparable 인터페이스를 구현해서 정렬기준을 만들어주었는데,

    Comparable 인터페이스를 구현하지 않고도 특정 변수를 기준으로 정렬하는 방법이 있다.

     

    이때 사용하는게 Comparator 이라는 클래스이다.

    Comparator을 이용하여 본인이 원하는대로 정렬할 수 있다.

     

    추가적으로 간단하게 설명하자면,

    위에서 사용했던 sort()의 클래스들의 공식 레퍼런스에 보면, 아래와 같이 적혀있다.

     

     

     

    ======================

    Arrays 클래스 :

    static <T> void

    sort(T[] a, Comparator<? super T> c)

    Sorts the specified array of objects according to the order induced by the specified comparator.

     

    Collections 클래스 :

    static <T> void

    sort(List<T> list, Comparator<? super T> c)

    Sorts the specified list according to the order induced by the specified comparator.

    =======================

     


    위 레퍼런스의 일부에 기재된바와 같이,  배열이나 리스트가 매개변수로 들어오더라도, 추가 매개변수로 Comparator을 넣어주면 
    해당 Comparator에서 정의된 정렬순서에 따라 배열이나 리스트를 정렬해주겠다고 한다.

     

     

     

    우리는 지금, 이 Comparator을 무명클래스로 만들어주려고 한다.

    위에 메이플스토리 유저클래스에다가 살을 붙혀보자.

     

    먼저 MapleStoryUser 클래스에 추가된  implements Comparable<MapleStoryUser>  를 날린다.

    그리고 아까 재정의했던 compareTo() 메서드를 통째로 주석처리 해버리자.

    그 후 아래와 같이 sort() 메서드에 Comparator를 무명클래스로 정의한걸 통째로 인자로 넣어주자.

     

    package compare;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    public class MapleStoryUserList {
    	public static void main(String args[]) {
    		ArrayList<MapleStoryUser> userList = new ArrayList<MapleStoryUser>();
    		MapleStoryUser user1 = new MapleStoryUser("메이플운영자","운영진길드" ,250 , 9999999);
    		MapleStoryUser user2 = new MapleStoryUser("헤네시스소음","모바일길드" ,173 , 980000);
    		MapleStoryUser user3 = new MapleStoryUser("커닝미세먼지","모바일길드" ,199 , 1300000);
    		MapleStoryUser user4 = new MapleStoryUser("엘리니아벌레","" ,83 , 3200000);
    		MapleStoryUser user5 = new MapleStoryUser("페리온치약","파이썬길드" ,125 , 2600000);
    		MapleStoryUser user6 = new MapleStoryUser("썬콜의파워","자바길드" ,143 , 1900000);
    		MapleStoryUser user7 = new MapleStoryUser("표도손목스냅","" ,230 , 1100000);
    		MapleStoryUser user8 = new MapleStoryUser("전사의삼두근","인바디클럽" ,207 , 2500000);
    		MapleStoryUser user9 = new MapleStoryUser("궁수의복싱","복싱길드" ,221 , 600000);
    		MapleStoryUser user10 = new MapleStoryUser("드디어마지막","힘들다길드" ,225 , 1620000);
    		userList.add(user1);
    		userList.add(user2);
    		userList.add(user3);
    		userList.add(user4);
    		userList.add(user5);
    		userList.add(user6);
    		userList.add(user7);
    		userList.add(user8);
    		userList.add(user9);
    		userList.add(user10);
    		Collections.sort(userList, new Comparator<MapleStoryUser>() {
    			@Override
    			            public int compare(MapleStoryUser user1, MapleStoryUser user2) {
    				if(user1.getLevel() > user2.getLevel()) {
    					return 1;
    				} else if(user1.getLevel() < user2.getLevel()) {
    					return -1;
    				} else {
    					return 0;
    				}
    			}
    		}
    		);
    		for (int i=0; i<userList.size(); i++) {
    			System.out.print(userList.get(i).getLevel() + " / ");
    		}
    	}
    }

     

     

     

    원하는 대로 결과가 나왔다.

    Comparable 인터페이스를 구현하는것과 비슷하게,

    여기도 compare() 메소드를 재정의 해줘야 하며 어떤 데이터를 기준으로 정렬할건지 정의해주면 된다.

     

    보면 알겠지만 눈에 보이는 장점은 Comparable을 구현한 객체로 정렬하게 되면, 한번 정해져버리면 재컴파일 전에는 바꿀수가 없다.

    그러나 Comparator으로 구현한 경우, 어떤 데이터에 따라 정렬할 것인지 유동적으로 결과를 바꿀 수 있다.

     

     

    역시나 배열로 정의된경우도 위와같은 방법으로 정렬이 가능하다.

    스냅샷이 귀찮으니 결과캡쳐본으로 대체.

     

     

     

     

     

     

     

     


    #요약

    1. 배열 : Arrays.sort()  // ArrayList : Collections.sort() 을 이용해 정렬이 가능하다.

    2. Comparable에 있는 compareTo() 기준으로 sort()를 처리한다.

    3. String, Integer, date... 등의 Java 기본클래스들은 모두 Comparable이 구현되어있고, 오름차순 정렬로 compareTo가 정의되어있다.

    4. 사용자가 만든 클래스는 Comparable을 구현시켜 주거나, Comparator을 sort의 매개변수로 넣어준다.

    5. Comparable 인터페이스를 구현하는 경우 compareTo() 메서드를 오버라이딩 해줘야 한다.

    6. Comparator 클래스를 정의하는 경우 compare() 메서드를 오버라이딩 해줘야한다.

          -> compareTo() , compare() 에서 정의된 데이터에 따라 sort()를 만나면 정렬된다.

     

     

     

     

     

    =========끗==========

    댓글

다치지 말고 운동하자.