자바에서 정렬에 관련된 작업을 할 경우 떠올릴 수 있는 대표적인 인터페이스가 두가지 있다.

Comparator와 Comparable이다.

두 가지 인터페이스의 차이점과 사용법에 대해 알아보자.

 

두 인터페이스가 사용되는 이유는 공통적이다.

Primitive 타입의 숫자나 문자를 비교할 경우 명확한 기본 비교로직이 존재한다. 오름차순이라면 '1'보단 '2'가 크고, '가' 보다는 '나'가 크다. 하지만 비교하는 대상이 객체라면 기본 비교로직으로는 비교가 불가능하다.

예를들어 Game 클래스로 만든 객체들을 정렬한다고 가정한다면 여러가지 정렬을 위한 기준이 있을 수 있다. (발매년도, 타이틀명 등등) 이 때, 정렬 기준에 대한 명확한 로직을 정의할 수 있도록 도와주는 것이 Comparator와 Comparable 인터페이스이다. 

 

하나씩 살펴보자.

 

Comparator

'비교기'라는 이름에서 알 수 있듯이 비교를 위한 보조로직을 따로 작성하여 정렬시 인자로 건네준다.

보조로직 작성은 Comparator의 int compare(obejct args1, object args2) 메서드에 작성한다.

리스트의 정렬은 Collections.sort() 메서드를 사용하는데, 여기의 인자로 전달한다.

public class GameComparatorTest {
    @Test
    public void test1(){
        Comparator<Game> gameComparator = new Comparator<Game>() {
            // 비교를 위한 로직을 작성
            public int compare(Game game1, Game game2) {
                // 발매년도는 내림차순으로 하면서, 같은 발매년도에서는 타이틀명을 오름차순으로 하고싶다.
                if(game1.year != game2.year){
                    return Integer.compare(game1.year, game2.year) * (-1);
                }else {
                    return game1.title.compareTo(game2.title);
                }
            }
        };
 
        List<Game> games = new ArrayList<>();
        games.add(new Game(2000"Assassin Creed"));
        games.add(new Game(1990"Overwatch"));
        games.add(new Game(2000"League Of Legends"));
        games.add(new Game(1990"A Power"));
 
        Collections.sort(games, gameComparator);
 
        System.out.println(games);
    }
}
 
class Game {
    int year;
    String title;
 
    public Game(int year, String title) {
        this.year = year;
        this.title = title;
    }
 
    @Override
    public String toString() {
        return "Game{" +
                "year=" + year +
                ", title='" + title + '\'' +
                '}';
    }
}

// 출력결과

[Game{year=2000, title='Assassin Creed'}, Game{year=2000, title='League Of Legends'}, Game{year=1990, title='A Power'}, Game{year=1990, title='Overwatch'}]

발매년도로 내림차순 되었고, 발매년도가 같은경우 타이틀명으로 오름차순 되었다. 

 

 

Comparable

Comparator와 유사하게 비교를 위한 보조로직을 정의하지만 작성하는 위치가 다르다. '비교할 수 있는'이라는 뜻과 같이 비교하고자 하는 클래스에 비교로직을 정의한다. 마찬가지로 Collections.sort() 메서드로 정렬할 수 있고 이 때는 두번째 인자가 필요하지 않는다.

public class GameComparableTest {
    @Test
    public void test1(){
        List<Game> games = new ArrayList<>();
        games.add(new Game(2000"Assassin Creed"));
        games.add(new Game(1990"Overwatch"));
        games.add(new Game(2000"League Of Legends"));
        games.add(new Game(1990"A Power"));
 
        Collections.sort(games);
 
        System.out.println(games);
    }
}
 
class Game implements Comparable<Game> {
    int year;
    String title;
 
    public Game(int year, String title) {
        this.year = year;
        this.title = title;
    }
 
    @Override
    public String toString() {
        return "Game{" +
                "year=" + year +
                ", title='" + title + '\'' +
                '}';
    }
 
    @Override
    public int compareTo(Game game1) {
        // 발매년도는 내림차순으로 하면서, 같은 발매년도에서는 타이틀명을 오름차순으로 하고싶다.
        if(this.year != game1.year){
            return Integer.compare(this.year, game1.year) * (-1);
        }else {
            return this.title.compareTo(game1.title);
        }
    }
}

출력결과는 Comparator의 출력결과와 동일하다.

 

즉, 두 가지 인터페이스 모두 객체 비교를 위한 보조로직을 정의하는데 사용되는 인터페이스이다. 상황에 맞게 선택하여 사용하자.

 

 

 

 

'개발 > JAVA' 카테고리의 다른 글

JVM, JRE, JDK 간단 개념  (0) 2020.12.07
ExecutorService 사용법  (0) 2020.12.01
LocalDateTime, ZonedDateTime  (0) 2019.10.11
Collections를 이용한 정렬(sort method) 활용  (0) 2017.05.09
BigDecimal & BigInteger  (0) 2017.04.18

목표

  • Maven에 대한 기본개념 이해
  • Maven의 의존성 관리 빌드 자동화 이해

Maven

의존성 관리와 빌드 자동화 기능을 제공하는 툴이며, 프로젝트 내에서 pom.xml을 주설정파일로 하여 동작한다.


의존성 관리

개발시 사용하는 라이브러리에 대해 pom.xml을 기준으로 메이븐 저장소로부터 다운받아 개발자의 프로젝트에 추가해 준다. (로컬저장소의 .m2 폴더에 다운로드 되며 가끔 의존성이 꼬일경우 .m2 폴더 내용을 날려주면 해결되는 경우가 있었다.)

라이브러리 추가를 위해서는 <dependencies/> 이하에 <dependency/> 태그에 작성하며 groupId, artifactId, version 세가지 정보가 필요하다. 보통 maven repository 웹사이트에서 라이브러리를 검색하여 등록한다,

dependency에 <scope>의 경우 compile, runtime, provided, test등이 올 수 있는데 해당 라이브러리가 언제 필요한지, 언제 제외되는지를 나타낸다.


빌드 자동화

메이븐은 자바 프로젝트의 빌드(Build)를 자동화해 주는 빌드 툴(Build Tool)이다. 즉, 자바 소스를 컴파일하고 패키징하여 배포까지 자동으로 해주는 도구이다.

컴파일->패키징->배포 과정(maven build)에 대한 세부 설정값들은 pom.xml에 작성한다.

 

maven build는 LifeCycle이 존재하며, 메이븐에 내장된 라이프사이클은 default, clean, site 3가지가 있다.

라이프사이클은 세부적으로 페이즈(phase)로 구성되어 있다. 예를 들어 default 라이프사이클은 다음과 같이 7개의 페이즈로 구성되며(전부는 아니고 큰 줄기로하면) 이들 사이에는 실행순서가 존재한다.

mvn install 명령어로 install 페이즈를 실행하면 1번 validate 페이즈부터 6번 install 페이즈까지 순차적으로 실행되는 구조다. (deploy는 실행되지 않는다.)

  1. validate
  2. compile
  3. test
  4. package
  5. verify
  6. install
  7. deploy

mvn 명령은 스페이스를 구분자로 여러동작을 한번에 지정할 수 있다.

mvn clean deploy : clean 페이즈 실행 후 1번 페이즈 validate부터 7번 페이즈 deploy까지 실행한다.

 

위의 Build Phase는 다시 Plugin Goal의 모음으로 이루어져 있다. Phase는 구체적인 작업을 나타내지는 않는다. 예를들어 package는 패키징된 결과물이 jar 파일이냐 war 파일이냐에 따라 다르게 수행되어야 하지만 Phase는 세부정보를 표시하지 않는 상위 개념이다.

pom.xml의 설정에 따라서 Phase마다 수행되는 세부작업을 Goal이라 부르고, Goal들의 집합을 Plugin이라 한다. 각 Phase마다 수행되어야할 Goal들이 맵핑되어 있고, Phase를 실행하면 설정에 맞는 Goal이 수행되는 구조다.

Goal은 하나 이상의 Phase에 맵핑되어 있거나, 맵핑이 되어 있지 않은 것도 있는데 맵핑된 Phase가 없다면 해당 Goal을 직접적으로 호출하여 실행할 수 있다.

 

mvn clean dependency:copy-dependencies package

  1. clean phase 실행(라이프사이클 순서에 따라 순차적으로)
  2. dependency:copy-dependencies : dependency plugin의 copy-dependencies goal 실행
  3. package phase 실행(라이프사이클 순서에 따라 순차적으로)

 

메이븐의 빌드자동화를 정리하면,

  • LifeCycle : 순서를 가지는 Phase의 집합
  • Phase : LifeCycle의 구성요소로 Phase간에는 정해진 순서가 있다.
  • Goal : 메이븐이 수행할 구체적인 작업으로 하나의 페이즈는 하나의 골과 맵핑된다.
  • Plugin : Goal의 집합

라이프사이클에 따른 페이즈 구성이나 설정에 따라 수행되는 골의 종류 등은 메이븐의 공식 사이트를 참고하고 대략적인 개념정리는 여기서 마무리 하도록 한다.


참고

메이븐 웹 - http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference

메이븐파헤치기(김우용) - https://www.slideshare.net/ssuser5445b7/ss-56566336?qid=927855f5-7c8a-4f88-a834-d31292324fd2&v=&b=&from_search=4

 

 


 

 

'개발 > 기타' 카테고리의 다른 글

패스워드 암호화에 대해서  (0) 2020.12.06
LastModified 헤더를 이용한 파일변경 체크  (0) 2020.12.06
UTC, GMT, Epoch Time  (0) 2019.10.11
Ajax에 관하여  (0) 2018.12.13
META-INF 디렉터리에 대하여  (1) 2018.04.12

목표 

  • Java8부터 추가된 LocalDateTime, ZonedDateTime에 대한 기본이해 및 간단한 활용

날짜와 시간을 나타내는 자바 API는 여러가지가 있었지만 기능의 부족함으로 사용이 권장되지는 않았다.

(Calendar, Date 등)

하지만 자바8부터 등장한 java.time API는 이전 API의 결함을 상당부분 해결했고, 앞으로 제 역할을 할 것이라 기대된다. 

 

Instant Class

Instant 클래스는 타임라인의 한 점을 나타낸다.

Immutable, thread-safe 하며 now()로 생성된 인스턴스를 리턴받을 수 있다.

equals(), compareTo() 처럼 비교를 위한 함수를 제공하므로 인스턴스끼리 시간비교가 가능하다.

 
    @Test
    public void Instant_Duration_기본() throws InterruptedException {
        Instant start = Instant.now();
        System.out.println("현재시각(인스턴스) : " + start); // 현재시각(인스턴스) : 2019-10-11T04:50:51.759Z
 
        someComplicatedJob(); // 시간텀을 두기 위해서 임의의 JOB을 수행
 
        // Instant는 한 점을 나타내므로 비교가 가능하다.
        Instant end = Instant.now();
        System.out.println("Instant 사이의 비교 - equals() : " + start.equals(end));
        System.out.println("Instant 사이의 비교 - compareTo() : " + start.compareTo(end));
 
        // 비교를 위해서 Duration 클래스를 사용할 수 있다.
        // Duration : 두 Instant 사이에 있는 시간의 양, 불변 클래스
        Duration timeElapsed = Duration.between(start, end);
        System.out.println("JOB 수행에 걸린시간(밀리초) : " + timeElapsed.toMillis());
        System.out.println("JOB 수행에 걸린시간(초) : " + timeElapsed.getSeconds());
        System.out.println("JOB 수행시간이 마이너스인지 ? " + timeElapsed.isNegative());
    }
    
    
 
cs

 

Duration Class

시간차에 대한 작업을 위해 제공되는 클래스이다.

@Test
public void Duration_활용(){
    Duration duration1 = Duration.ofHours(1); // 1시간 차이
    Duration duration3 = Duration.ofHours(3); // 3시간 차이
 
    // 두 Duration이 2배 이상 차이가 나는지 체크하는 코드
    boolean overDoubleFaster1 = duration1.multipliedBy(2).minus(duration3).isNegative();
    System.out.println("duration3이 duration1 보다 2배 더 빠른가 ? " + overDoubleFaster1);
 
    boolean overDoubleFaster2 = duration1.toNanos() * 2 < duration3.toNanos();
    System.out.println("duration3이 duration1 보다 2배 더 빠른가 ? " + overDoubleFaster2);
}
cs

 

LocalDateTime

실제 사람이 사용하는 시간을 나타내는 API이다. Local(지역)이 접두어로 붙은 것에서 유추할 수 있듯이 여러 지역의 시간대(Time Zone) 정보는 포함하지 않는다.

KST나 UTC처럼 시간대를 지정하는 것이 정확한 표현이지만, 실제 활용에서 이러한 시간대는 잘 사용되지 않고 오히려 방해가 될 수 있으므로 만일 시간대를 필요로 하지 않는 작업이라면 LocalDateTime 사용을 고려한다.

 

LocalDate, LocalTime처럼 날짜, 시간에 특화된 API도 있지만 이 둘을 합쳐서 표현할 수 있는 LocalDateTime이 주로 사용된다.

@Test
public void LocalDate_LocalTime_LocalDateTime_기본(){
    // 구역(Zone) 구분이 없는 지역날짜로 now()나 of()로 생성한다.
    LocalDate nowDate = LocalDate.now();
    LocalDate myBirthDay = LocalDate.of(1988, Month.MAY, 26); // '월'을 enum으로 줄 수도 있다.
 
    System.out.println("현재날짜 : " + nowDate); // 2019-10-11
    System.out.println("현재날짜 + 7일 : " + nowDate.plusDays(7L)); // 2019-10-18
    System.out.println("현재날짜 + 7일 : " + nowDate.plusWeeks(1L)); // 2019-10-18
    System.out.println("현재날짜 - 7일 : " + nowDate.minus(Period.ofDays(7))); // 2019-10-04
 
    LocalTime nowtime = LocalTime.now();
    System.out.println("현재시각 : " + nowtime); // 15:16:22.909
    System.out.println("현재시각 + 1시간 : " + nowtime.plusHours(1L)); // 16:16:22.909
    System.out.println("현재시각에서 시간만 3으로 변경 : " + nowtime.withHour(3)); // 03:16:22.909
 
 
    LocalDateTime nowDateTime = LocalDateTime.now();
    System.out.println("현재시각 : " + nowDateTime); // 2019-10-11T15:16:22.909
    System.out.println("현재시각 + 1시간 : " + nowDateTime.plusHours(1L)); // 2019-10-11T16:16:22.909
    System.out.println("현재시각에서 시간만 3 : " + nowDateTime.withHour(3)); // 2019-10-11T03:16:22.909
}
cs

 

ZonedDateTime

LocalDateTime에서 시간대(Time Zone) 개념이 추가된 클래스로 기본적인 사용방법은 비슷하다.

다음과 같이 사용가능한 TimeZoneId를 출력해보면 많은 수가 있는것을 알 수 있다.

@Test
public void ZoneId(){
    // 각 시간대는 America/New_York, Europe/Berlin 등 ID가 존재한다.
    Set<String> availableZoneIds = ZoneId.getAvailableZoneIds(); // 이용할 수 있는 모든 시간대를 얻는다.
    for(String str : availableZoneIds){
        System.out.println(str); // Asia/Aden, America/Chicago, Europe/Luxembourg ...
    }
}
cs

많은 ZoneId 중에서도 아마 많이 사용할 시간대는 Asia/Seoul 과 UTC 일 것이다.

해당 타임존을 인자로하여 ZonedDateTime을 생성 후 출력해보면 인자로 준 시간대 정보가 같이 출력된다. 

UTC의 경우 표준이니 별다른 내용이 없지만, Asia/Seoul의 경우는 표준 UTC 06시23분에 9시간이 더해져서 15시23분임을 알 수 있다.

LocalDateTime에 타임존을 추가하거나 ZonedDateTime에 타임존을 제거하면서 상호변환이 가능하다.

@Test
public void ZonedDateTime(){
    ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Asia/Seoul"));
    System.out.println(now); // 2019-10-11T15:23:08.605+09:00[Asia/Seoul]
 
    ZonedDateTime now2 = ZonedDateTime.now(ZoneId.of("UTC"));
    System.out.println(now2); // 2019-10-11T06:23:08.605Z[UTC]
 
}
cs
@Test
public void ZonedDateTime_to_LocalDateTime(){
    ZonedDateTime nowUTC = ZonedDateTime.now(ZoneId.of("UTC"));
    System.out.println(nowUTC);
 
    LocalDateTime nowSeoul = nowUTC.withZoneSameInstant(ZoneId.of("Asia/Seoul")).toLocalDateTime();
    System.out.println(nowSeoul);
 
    ZonedDateTime nowSeoulZonedTime = nowSeoul.atZone(ZoneId.of("Asia/Seoul"));
    System.out.println(nowSeoulZonedTime);
}
cs

ZonedDateTime 인스턴스를 문자열(String)로 변경하거나 반대의 경우도 가능하다. 

변환시 DateTimeFormatter를 인자로하여 원하는 패턴으로 변환한다.

@Test
public void formatting(){
    ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Asia/Seoul"));
 
    String nowStr1 = now.format(DateTimeFormatter.ISO_DATE_TIME); // 2019-10-11T15:48:07.039+09:00[Asia/Seoul]
    String nowStr2 = now.format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss z")); // 2019/10/11 15:48:07 KST
    String nowStr3 = now.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL)); // 2019년 10월 11일 금요일 오후 3시 48분 07초 KST
    String nowStr4 = now.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL).withLocale(Locale.US)); // Friday, October 11, 2019 3:48:07 PM KST
 
    ZonedDateTime now1 = ZonedDateTime.parse(nowStr1);
    ZonedDateTime now2 = ZonedDateTime.parse(nowStr2, DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss z"));
    ZonedDateTime now3 = ZonedDateTime.parse(nowStr3, DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL));
    ZonedDateTime now4 = ZonedDateTime.parse(nowStr4, DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL).withLocale(Locale.US));
 
    System.out.println(now1);
    System.out.println(now2);
    System.out.println(now3);
    System.out.println(now4);
}
cs

 

EpochTime -> LocalDateTime 변환

시간을 표현하는 Long값인 EpochTime을 LocalDateTime으로 변환할 수 있다.

@Test
public void Epoch_to_LocalDateTime(){
    // Epoch Time을 LocalDateTime으로 변경한다.
    LocalDateTime now3 = LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis()), ZoneId.of("Asia/Seoul"));
    System.out.println(now3);
 
    LocalDateTime now4 = LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis()), ZoneId.of("UTC"));
    System.out.println(now4);
}
cs

 


읽어주셔서 감사합니다. 도움이 되셨다면 광고 클릭 부탁드립니다.

모두 힘내세요! : )

 

 

 

'개발 > JAVA' 카테고리의 다른 글

ExecutorService 사용법  (0) 2020.12.01
Comparator, Comparable 어떻게 사용할까?  (0) 2020.11.29
Collections를 이용한 정렬(sort method) 활용  (0) 2017.05.09
BigDecimal & BigInteger  (0) 2017.04.18
Array 와 ArrayList 사이의 변환  (0) 2017.03.28

+ Recent posts