반응형

 

이번 문제는 1개의 tree를 인자로 받아 대칭인지 판단하는 문제입니다.

 

대칭트리의 경우에는, 트리의 왼쪽부분은 왼쪽 우선으로, 오른쪽 부분은 오른쪽 우선으로 탐색 시 만나는 값의 순서가 같습니다.

 

그렇기 때문에, 저는 아래와 같이 풀이했습니다.

 

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

class Solution:

    def isSymmetric(self, root: TreeNode) -> bool:
        if root == None: return True
        left_list = [root.val]
        right_list = [root.val]

        self.searchPriorLeft(root.left, left_list)
        self.searchPriorRight(root.right, right_list)
        
        if len(right_list) != len(left_list): return False

        for i in range(len(right_list)):
            if right_list[i] != left_list[i]: return False
        return True

    def searchPriorLeft(self, root: TreeNode, list):
        if root == None:
            list.append(None)
            return True
        list.append(root.val)
        if root.left == None and root.right == None: return True
        self.searchPriorLeft(root.left, list)
        self.searchPriorLeft(root.right, list)

    def searchPriorRight(self, root: TreeNode, list):
        if root == None:
            list.append(None)
            return True
        list.append(root.val)
        if root.left == None and root.right == None: return True
        self.searchPriorRight(root.right, list)
        self.searchPriorRight(root.left, list)

 

감사합니다.

반응형
반응형

1. 서론

이번 포스팅에서는 Chapter2의 동작 파라미터화 코드 전달하기 에 대해 진행하도록 하겠습니다.

 

진행하기에 앞서 동작 파라미터화의 의미를 설명하자면 아래와 같습니다.

 

아직은 어떻게 실행할것인지 결정하지 않은 코드 블록

 

2. 변화하는 요구사항에 대응하기

책에서는 사과 농부의 요구사항을 예로 들고 있습니다.

 

요구사항으로는 녹색인 사과 혹은 특정 무게 이상인 사과와 같이 filter 류의 요구사항들입니다.

 

1) 첫 번째 시도 : 녹색 사과 필터링

 

private enum Color {
    GREEN,
    RED
}

@Getter
@RequiredArgsConstructor
private static class Apple {
    private final Color color;
    private final int weight;
}

private static List<Apple> filterGreenApples(List<Apple> apples) {
    List<Apple> result = new ArrayList<>();
    for(Apple apple: apples) {
        if(Color.GREEN.equals(apple.getColor())) {
            result.add(apple);
        }
    }
    return result;
}

 

아직, 색이 다양해짐에 따라 메소드들이 늘어나야 하는 불편함이 존재합니다.

아래는 이를 해결하기 위해 값을 파라미터화한 케이스입니다.

 

 

2) 두 번째 시도 : 색을 파라미터화

 

private enum Color {
    GREEN,
    RED
}

@Getter
@RequiredArgsConstructor
private static class Apple {
    private final Color color;
    private final int weight;
}

private static List<Apple> filterApplesByColor(List<Apple> apples, Color color) {
    List<Apple> result = new ArrayList<>();
    for(Apple apple: apples) {
        if(color.equals(apple.getColor())) {
            result.add(apple);
        }
    }
    return result;
}

private static List<Apple> filterApplesByWeight(List<Apple> apples, int weight) {
    List<Apple> result = new ArrayList<>();
    for(Apple apple: apples) {
        if(apple.getWeight() > weight) {
            result.add(apple);
        }
    }
    return result;
}

 

 

값을 파라미터화한 이 코드가 나쁘진 않습니다. 다만, 중복되는 코드가 존재하는것을 볼 수 있습니다.

아래는 그것을 해결하기 위해 flag 값을 두어 필터링하는 케이스입니다.

 

 

3) 세 번째 시도 : 가능한 모든 속성으로 필터링

 

private static List<Apple> filterApples(List<Apple> apples, Color color, int weight, boolean flag) {
    List<Apple> result = new ArrayList<>();
    for(Apple apple: apples) {
        if((flag && apple.getColor().equals(color)) 
        || (!flag && apple.getWeight() > weight)) {
            result.add(apple);   
        }
    }
    return result;
}

public static void main(String[] args) {
    List<Apple> appleList = Arrays.asList(new Apple(Color.RED, 200), new Apple(Color.GREEN, 30), new Apple(Color.GREEN, 300));
    final List<Apple> greenAppleList = filterApples(appleList, Color.GREEN, 0, true);
    final List<Apple> heavyAppleList = filterApples(appleList, null, 150, false);
}

 

한개의 메서드로 줄어지긴 했지만 코드가 너무 지저분하며 이해하기가 어렵습니다.

이를 해결하기 위해, 이제 동작 파라미터화를 진행해보겠습니다.

 

 

 

 

 

반응형

 

 

 

 

3. 동작 파라미터화

private interface ApplePredicate {
    boolean test (Apple apple);
} 

private static class AppleHeavyWeightPredicate implements ApplePredicate {
    public boolean test(Apple apple) {
        return apple.getWeight() > 150;
    }
}

private static class AppleGreenColorPredicate implements ApplePredicate {
    public boolean test(Apple apple) {
        return Color.GREEN.equals(apple.getColor());
    }
}

 

위와 같이 인터페이스를 하나 두어 각 필터링을 담당하는 클래스들을 선언합니다.

 

1) 네 번째 시도 : 추상적 조건으로 필터링

 

private static List<Apple> filterApples(List<Apple> apples, ApplePredicate p) {
    List<Apple> result = new ArrayList<>();

    for(Apple apple: apples) {
        if(p.test(apple)) {
            result.add(apple);
        }
    }
    return result;
}

 

동작 파라미터화로 지저분한 코드를 깔끔하게 만들었으며 이해하기도 쉬워졌습니다.

다만, 요구사항이 늘어남에 따라 클래스를 생성해야 하니 코드수가 많이 늘게 됩니다.

또한, 한번만 사용하는 소스도 굳이 클래스를 생성하여 써야하는 불편함이 생겼습니다.

이를 해결하기 위해 아래와 같이 간소화 작업을 진행하겠습니다.

 

 

4. 복잡한 과정 간소화

1) 다섯 번째 시도 : 익명 클래스 사용

 

private static List<Apple> filterApples(List<Apple> apples, ApplePredicate p) {
    List<Apple> result = new ArrayList<>();

    for(Apple apple: apples) {
        if(p.test(apple)) {
            result.add(apple);
        }
    }
	return result;
}

final List<Apple> redAppleList = filterApples(appleList, new ApplePredicate() {
    public boolean test(Apple apple) {
        return Color.RED.equals(apple.getColor());
    }
});

 

익명클래스를 통해 클래스 선언을 제거했습니다.

하지만, 위 두번째 시도인 색을 파라미터화와 같이 중복되는 코드들이 많게됩니다.

 

2) 여섯 번째 시도 : 람다 표현식 사용

 

final List<Apple> redAppleList = filterApples(appleList, apple -> Color.RED.equals(apple.getColor()));

 

자바 8의 기능인 람다를 사용하여 중복되는 코드를 줄였으며 이해하기도 편하게 되었습니다.

 

5. 마무리

이번 포스팅에서는 동작 파라미터화 코드 전달하기에 대해 간단한 예제를 진행해보았습니다.

다음에는 람다 표현식에 대해 포스팅하겠습니다.

반응형

'Programming > ModernJavaInAction' 카테고리의 다른 글

(6) 스트림으로 데이터 수집  (0) 2020.04.04
(5) 스트림 활용  (0) 2020.04.04
(4) 스트림 소개  (0) 2020.03.28
(3) 람다 표현식  (0) 2020.03.28
(1) 자바 8, 9, 10, 11 : 무슨 일이 일어나고 있는가?  (0) 2020.03.21
반응형

1. 서론

 

이번 포스팅에서는 Modern Java In Action 의 1장인 자바 8, 9, 10, 11 : 무슨 일이 일어나고 있는가? 에 대해 알아보도록 하겠습니다.

 

2. 왜 아직도 자바는 변화하는가?

프로그래밍 언어 생태계는 끊임없이 변화하고 있습니다.

이러한 생태계에서 살아남기위해 프로그래밍 언어들은 계속 발전을 해왔고, 자바도 그 중 하나입니다.

 

자바는 살아남기 위해 무엇을 발전했는지 아래 설명하도록 하겠습니다.

 

 

1) 스트림 처리

 

자바8에서 java.util.stream 패키지가 추가되었습니다.

 

이 패키지에 있는 기능들은 리눅스의 파이프라인과 동일하다고 생각하시면 편합니다.

 

아래는 리눅스의 파일을 읽어 소문자로 변경하고 정렬하여 마지막 3줄만을 출력하는 command입니다.

 

cat file1 | tr "[A-Z]" "[a-z]" | sort | tail -3

 

각 파일읽기, 소문자 치환, 정렬 같은 일련의 작업을 ' | ' 사용하여 연결하는 것을 볼 수 있습니다.

 

이와 같이 자바 8에서도 Stream 패키지를 통해 각각의 작업을 하나의 일련의 작업으로 만들 수 있게 제공하고 있습니다.

 

 

2) 동작 파라미터화로 메서드에 코드 전달하기

 

자바 8에서는 메서드를 다른 메서드로 전달할 방법이 없었습니다.

 

하지만 자바 8에서 이를 지원하도록 개선되었고 이를 통해 복잡한 코드구조가 사라지게 되었습니다.

 

아래는 대표적인 메서드에 코드를 전달할 수 있는 함수형 인터페이스입니다.

패키지는 java.util.function 입니다.

 

  • Predicate
  • Consumer
  • Supplier

 

3) 병렬성과 공유 가변 데이터

 

자바 8에서는 스트림API를 지원하면서 간단히 병렬성을 가져갈수 있도록 제공하고 있습니다.

하지만, 이를 위해서는 멀티 쓰레드 환경에서도 서로 코드를 동시에 수행하더라도 안전하게 바꿔야하는 번거로움이 존재합니다.

 

스트림 함수 사용 시 외부접근 데이터를 Atomic한 객체로 만들어 사용해야 합니다.
이는, 위의 안전한 코드여야 하기 때문입니다.

 

 

 

 

 

반응형

 

 

 

 

 

3. 자바 함수

 

프로그래밍 언어에서 함수라는 용어는 메서드와 같은 의미로 사용됩니다.

자바 8에서는 이러한 메서드를 일급시민으로 올리도록 제공하고 있습니다.

 

일급시민이란, 각 구조체의 값을 전달할 수 있는 것을 의미합니다.

그러므로, 위의 메서드를 일급시민으로 올린다는 의미는 메서드라는 구조체의 값을 다른 구조체에게 전달할 수 있다는 의미입니다.

 

1) 메서드와 람다를 일급시민으로

 

1 - 메서드 참조

 

자바8 에서는 메서드참조를 제공합니다.

 

아래는 숨겨진 파일들만을 filter하여 얻어오는 코드입니다.

File[] hiddenFiles = new File(".").listFiles(new FileFilter() {
	@Override
	public boolean accept(File pathname) {
		return pathname.isHidden();
	}
});

 

위의 코드는 자바 8이 나오기전의 코드입니다.

 

자바 8에서는 람다와 메서드 참조를 제공하였고 그 결과 아래와 같이 더욱 readable 있는 코드로 리팩토링이 가능해졌습니다.

 

File[] hiddenFiles = new File(".").listFiles(File::isHidden);

 

람다를 통해 익명클래스를 간단하게 바꾸었으며, ::isHidden 인 메서드 참조를 통해 한결 간단해진 코드가 되었습니다.

 

 

2- 코드 넘겨주기

 

 

아래는 사과 리스트에서 초록색이며, 무게가 200이 넘는 사과만 분류하는 코드입니다. - 자바 8 이전

 

private enum Color {
    GREEN,
    RED
}

public static void main(String[] args) {
    List<Apple> appleList = Arrays.asList(new Apple(Color.RED, 200), new Apple(Color.GREEN, 30), new Apple(Color.GREEN, 300));
    final List<Apple> filterdGreenAppleList = filterGreenApples(appleList);
    final List<Apple> filterdGreenWithHeavyAppleList = filterHeavyApples(filterdGreenAppleList, 200);
}

@Getter
@RequiredArgsConstructor
private static class Apple {
    private final Color color;
    private final int weight;
}

private static List<Apple> filterGreenApples(List<Apple> apples) {
    List<Apple> result = new ArrayList<>();
    for(Apple apple: apples) {
        if(Color.GREEN.equals(apple.getColor())) {
            result.add(apple);
        }
    }
    return result;
}

private static List<Apple> filterHeavyApples(List<Apple> apples, int weight) {
    List<Apple> result = new ArrayList<>();
    for(Apple apple: apples) {
        if(apple.getWeight() > weight) {
            result.add(apple);
        }
    }
    return result;
}

 

하지만, 자바 8의 스트림 함수와 람다를 사용하면 아래와 같이 간단하게 바뀔 수 있습니다.

 

public static void main(String[] args) {
    List<Apple> appleList = Arrays.asList(new Apple(Color.RED, 200), new Apple(Color.GREEN, 30), new Apple(Color.GREEN, 300));
    final List<Apple> filterdGreenWithHeavyAppleList = appleList
            .stream()
            .filter(apple -> Color.GREEN.equals(apple.getColor()))
            .filter(apple -> apple.getWeight() > 200)
            .collect(Collectors.toList());
}

 

단, filter 에 있는 조건들이 일회성이 아니라면 별도 함수로 추출해서 사용해야 합니다.

 

4. 스트림

이전 옛날 자바에서는 한개의 CPU만을 사용하는 단점이 있었습니다.

하지만 자바 8에서는 한개가 아닌 멀티 CPU를 점유해서 사용하도록 변경되었고 이는 성능의 극대화를 가져다 주었습니다

 

대표적으로 parallelStream 을 들 수 있습니다.

parallelStream은 멀티 CPU를 통해 분할로 처리할때 사용합니다.

 

단, Parallel Stream 작업이 독립적이면서 CPU사용이 높은 작업에 사용해야합니다.

 

5. 디폴트 메서드와 자바 모듈

자바 8에서는 인터페이스에 디폴트 메서드를 제공합니다.

 

디폴트 메서드는 기존의 코드를 수정하지 않고 확장하기 위해서 만들어진 것이며,

간단하게 인터페이스에도 기본적으로 정의된 구현 메서드가 있을 수 있다고 생각하면 됩니다.

 

아래는 List의 default sort 함수입니다.

 

default void sort(Comparator<? super E> c) {
    Object[] a = this.toArray();
    Arrays.sort(a, (Comparator) c);
    ListIterator<E> i = this.listIterator();
    for (Object e : a) {
        i.next();
        i.set((E) e);
    }
}

 

6. 함수형 프로그래밍에서 가져온 다른 유용한 아이디어

 

자바 8에서 제공되는 기능들은 대체로 함수형 프로그래밍 특성을 가져다 주는 것입니다.

이런 함수형 프로그래밍의 패러다임을 가져오면서 많은 프로그램에 도움이 되는 아이디어가 나왔습니다.

 

한가지 예로, null을 회피하는 방법이며 자바에서는 Optional<T> 라는 컨테이너 클래스를 통해 제공하고 있습니다.

 

7. 마무리

이번 포스팅에서는 자바 8부터의 변화에 대해 간단한 소개와 설명을 했습니다.

다음에는 동작 파라미터화 코드 전달에 대해 포스팅하겠습니다.

반응형

'Programming > ModernJavaInAction' 카테고리의 다른 글

(6) 스트림으로 데이터 수집  (0) 2020.04.04
(5) 스트림 활용  (0) 2020.04.04
(4) 스트림 소개  (0) 2020.03.28
(3) 람다 표현식  (0) 2020.03.28
(2) 동작 파라미터화 코드 전달하기  (0) 2020.03.21

+ Recent posts