Functional Programming in Java 8 책의 6장에 Lambda를 이용하여 Lazy Initialization을 하는 방법이 나온다.

이 예제는 Heavy 객체의 생성을 Supplier 인터페이스를 이용해서 사용할 시점까지 지연시키는 예제다.

하지만 이 예제는 Heavy 클래스가 아닌 또 다른 heavy한 객체를 생성해야 할 때 매번 해당 클래스에 대한 HeavyInstanceHolder 클래스를 만들어 주어햐 하는 단점이 있다.

Jake라는 분이 이를 해결한 방법 Blog로 정리해 놓았다. 요점은 Heavy 클래스를 Lazy로 생성하는 부분을 T 형 타입으로 받아서 처리하면 된다.
그런데 synchronized 키워드를 붙여 thread-safe 하게 처리한 부분이 좀 이상해서 github을 따라 갔더니 코드가 조금 변경되어 있다.

해당 github 소스:

- test용 main 클래스
- Lazy Initialization 구현 클래스


하지만 여전히 문제가 있다. main 클래스에서 Heavy 객체를 생성하는 게 잘 표현되어 있지 않다!! Venkat(책 저자)의 의도가 드러나게 다시 바꿔보자.


먼저 초기화에 시간이 오래 걸리는 Heavy 클래스가 있다.


이 놈을 여러번 생성하는 테스트 클래스다. 2개의 쓰레드에서 Heavy 객체를 동시에 생성해 보자. 그리고 생성 후 Heavy 객체의 메소드를 호출해서 2번째 쓰레드가 같은 객체를 또 생성하지는 않는지, 지연 생성이 완료될 때까지 기다리고 있는 지도 확인 해 보자.


LazilyInstantiate 클래스다. Supplier를 이용해서 Heavy 생성 시점을 지연시키고, 한 번 생성된 객체는 재활용한다.


Main을 실행한 결과

Main start
Thread1 run
Main end
Thread2 run
Heavy Initiation started from Thread1
Heavy Initiation ended from Thread1
1
2


이제 Main 함수에서 Heavy 객체 외에 다른 객체(Heavy2)의 생성을 지연시키고 싶다면, 이렇게만 하면 된다.


저작자 표시 비영리 변경 금지
신고

Files 클래스에는 Java 8에서 추가된 find라는 메소드가 있다.

Funtional Programmin in Java 8 책의 3장에 ListSubDir.java 예제가 있는데,  해당 디렉토리의 하부 디렉토리까지만 가져오므로 완전한 예가 아니다.

find를 이용해서 어떤 디렉토리에 존재하는 모든 파일을 가져오도록 Stream API를 이용하여 작성해 보자.

Path start = Paths.get(".");

int maxDepth = 5; // Integer.MAX_VALUE로 하면 모든 파일을 가져올 수 있다

Files.find(start, maxDepth, (path, attr) -> true)

        .forEach(System.out::println);

만약 어떤 특정 파일 속성을 가지는 파일만 가져오고 싶다면 find의 3번째 인자를 수정하면 된다.

예를 들어 .java파일만 찾고 싶다면

(path, attr) -> path.toString().endsWith(".java")

이렇게 바꿔주면 된다.

저작자 표시 비영리 동일 조건 변경 허락
신고


저작자 표시 비영리 동일 조건 변경 허락
신고


문자열 내에서 숫자만 분리하기

String 클래스의 split 함수와 정규표현식을 이용해서 문자열 내에 들어 있는 숫자들만 추려보자. 

자바 소스를 보면 String.split() 메쏘드는 내부적으로 Pattern클래스의 split 메쏘드를 사용한다. 즉, 사용자가 Pattern 클래스를 이용하지 않고 사용하기 편하게 만들어 놓았을 뿐.
    public String[] split(String regex, int limit) {
return Pattern.compile(regex).split(this, limit);
    }

    public String[] split(String regex) {
        return split(regex, 0);
    }

예제 코드를 보자.
    @Test
    public void split() {
        String text = "blah 11 per day 22 abc 33";
        String[] split = text.split("\\D+");
        assertEquals("", split[0]);    // 주의!
        assertEquals("11", split[1]);
        assertEquals("22", split[2]);
        assertEquals("33", split[3]);
    }

정규표현식 \D 는 숫자가 아닌 문자(A non-digit: [^0-9])를 뜻한다. 

(http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html 참고)

따라서 \D+는 숫자가 아닌 문자열들을 토큰으로 스트링을 잘라내게 된다.


주의) split 함수가 리턴하는 문자열 배열의 첫번째 원소의 값은 공백문자("")가 된다. "11"을 만나기 전까지의 문자 "blah "문자를 잘라내고 남는 문자를 배열 요소에 담기 때문.


추가) 마찬가지로 만약 숫자를 제외한 문자들만 뽑아내고 싶다면 \d+ 를 쓰면 된다.


저작자 표시 비영리 변경 금지
신고


정규 표현식을 사용하면 코드 양을 획기적으로 줄일 수 있다. 이번 기회에 Java에서 지원하는 정규표현식을 정리해 본다.


일단 Java 정규표현식 문서는 아래 링크를 참조. (JAVA SE7기준)

http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html


문서에 사용예제가 있다.

Pattern p = Pattern.compile("a*b"); Matcher m = p.matcher("aaaaab"); boolean b = m.matches();


정규표현식의 기본 형식에 대해 테스트 코드를 만들어 보았으니, 첨부소스를 참고하자.


regularExpression.zip


(첨부 소스에 빠진 예제가 많은데, 혹시 예제로 표현해 주실 분은 댓글로 남겨주시면 감사하겠습니다.)


정규표현식 검사기도 있다고 하니 JAVA가 아닌 다른 언어로 정규식을 검사할 때 유용하겠다.


아래는 개발하면서 유용하게 쓰일 정규 표현식들. (유첨 소스의 UsefulExpressionsTest.java에 포함)


IP주소

((([0-9])|([1-9]\d{1})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}(([0-9])|([1-9]\d{1})|(1\d{2})|(2[0-4]\d)|(25[0-5]))


URL

^(https?):\/\/([^:\/\s]+)(:([^\/]*))?((\/[^\s/\/]+)*)?\/?([^#\s\?]*)(\?([^#\s]*))?(#(\w*))?$


Email주소

(\w+\.)*\w+@(\w+\.)+[A-Za-z]+


HTML 주석

<!-{2}(.|\n)*?-{2}>


HTML 태그

^<([a-z]+)([^<]+)*(?:>(.*)<\/\1>|\s+\/>)$


주민등록번호

- 주민번호 체계 공식에 대입하기 전에 간단히 자리수 체크만 할 때 쓴다.

// \d{2}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])-[1-4]\d{6}


한글

[가-힣]+


3자~16자의 사용자 id (영문/숫자/_/- 만 가능)

^[a-z0-9_-]{3,16}$


16진수값

^#?([a-f0-9]{6}|[a-f0-9]{3})$



참고 사이트

1.http://blog.naver.com/PostView.nhn?blogId=fightingdk&logNo=60124162230&redirect=Dlog&widgetTypeCall=true

2. http://goodidea.tistory.com/86

3. http://blog.outsider.ne.kr/360


저작자 표시 비영리 동일 조건 변경 허락
신고


JAVA에서 URL 정규표현식을 알아보자. (발췌: http://goodidea.tistory.com/86)


원문에서는 마지막에 http://www.daum.net 과  같이 URL 마지막에 슬래쉬(/)가 없는 예에서는 에러가 난다.

이를 수정하면 아래와 같이 된다.


^(https?):\/\/([^:\/\s]+)(:([^\/]*))?((\/[^\s/\/]+)*)?\/?([^#\s\?]*)(\?([^#\s]*))?(#(\w*))?$


JAVA 테스트 코드로 바꾸면 다음과 같다.


    @Test

    public void url() {

        String regex = "^(https?):\\/\\/([^:\\/\\s]+)(:([^\\/]*))?((\\/[^\\s/\\/]+)*)?\\/?([^#\\s\\?]*)(\\?([^#\\s]*))?(#(\\w*))?$";

        Pattern p = Pattern.compile(regex);


        assertTrue(p.matcher("https://goodidea.tistory.com:8888/qr/aaa/ddd.html?abc=def&ddd=fgf#sharp").matches());

        assertTrue(p.matcher("http://dextto.tistory.com").matches());

        assertTrue(p.matcher("http://blog.daum.net/dexter").matches());

        assertTrue(p.matcher("http://www.daum.net:80/index.cfm").matches());

        assertTrue(p.matcher("http://xxx:password@www.daum.net").matches());

        assertTrue(p.matcher("http://localhost/index.php?ab=1&c=2").matches());

        assertTrue(p.matcher("http://localhost:8080").matches());

        assertTrue(p.matcher("http://dextto.tistory.com/admin/entry/post/?id=150&returnURL=/entry/JAVA-Regular-Expression-%EC%A0%95%EA%B7%9C-%ED%91%9C%ED%98%84%EC%8B%9D-%EC%98%88%EC%A0%9C").matches());

    }

저작자 표시 비영리 동일 조건 변경 허락
신고

티스토리 툴바