As-Is (데이터 컬렉션 반복을 명시적으로 관리하는 외부 반복을 수행)

List<Dish> vegetarianDishes = new ArrayList<>();  
for(Dish d : menu){  
  if(d.isVegetarianDishes()){
    vegetarianDishs.add(d);
  }
}

To-Be (filter와 collect 연산을 지원하는 스트림 API를 이용해서 데이터 켈렉션 반복을 내부적으로 처리)

List<Dish> vegetarianMenu = menu.stream()  
            .filter(Dish::isVegetarian)
            .collect(toList());
System.out.println(vegetarianMenu.toString());  

5.1 Filtering and Slicing

5.1.1 Filtering with a predicate

stream 인터페이스는 filter 기능을 지원한다. predicate 를 파라미터로 받아서 필터링된 요소들을 stream 형식으로 반환한다.

List<Dish> vegetarianMenu = menu.stream()  
            .filter(Dish::isVegetarian) // 채식에 가까운 Dish 인지 여부
            .collect(toList());
System.out.println(vegetarianMenu.toString());  

5.1.2 Filtering unique elements

고유한 요소를 반환하는 distinct 함수를 제공한다.

List<Integer> numbers = Arrays.asList(1,2,1,3,3,2,4);  
numbers.stream()  
      .filter(i -> i%2 == 0)
      .distinct()
      .forEach(System.out::println);

5.1.3 Truncating a stream

반환되는 요소의 갯수를 제한 할 수 있는 limit(n) 함수를 제공한다. 그 이외의 요소들은 처리되지 않는다.

List<Dish> dishes = menu.stream()  
             .filter(d -> d.getCalories() > 300)
             .limit(3)
              .collect(toList());
System.out.println(dishes.toString());  

5.1.4 Skipping elements

skip(n)은 n만큼의 요소를 제외한 나머지를 반환하는 함수이다. limit(n)과는 상호보완적인 관계이다.

List<Dish> dishes = menu.stream()  
            .filter(d -> d.getCalories() > 300)
            .skip(2)
            .collect(toList());
System.out.println(dishes.toString());  

Quiz 5.1 처음 2개의 고기요리를 고르시오.

List<Dish> dishes =  
            menu.stream()
            .filter(d -> d.getType() == Dish.Type.MEAT)
            .limit(2)
            .collect(toList());
System.out.println(dishes.toString());  

5.2 Mapping

stream은 SQL 의 select 문 처럼 stream에서 특정 컬럼을 추출하는 함수를 제공한다(map, flatMap).

5.2.1 map

map 는 함수를 인수로 받는 메소드이다. 인수로 받은 함수를 stream의 매 요소에 적용되어 새로는 stream을 생성한다.

List<String> dishNames =  
            menu.stream()
            .map(Dish::getName)
            .collect(toList());
System.out.println(dishNames);  

Dish.getName()의 리턴타입이 String 이기 때문에 map메소드의 리턴타입 역시 Stream\

아래의 경우에는 String.length() 의 리턴타입이 Integer이므로 map메소드의 리턴타입은 Stream\

List<String> words = Arrays.asList("Java8", "Lambdas", "In", "Action");  
List<Integer> wordLenghs = words.stream()  
            .map(String::length)
            .collect(toList());
System.out.println(wordLenghs);  

그러면, 메뉴의 각 요리이름을 구한 후 그 이름들의 길이를 구하고 싶다면 아래와 같이 할 수 있다.

List<Integer> dishNameLengths = menu.stream()  
            .map(Dish::getName)
            .map(String::length)
            .collect(toList());
System.out.println(dishNameLengths);  

만일 단어의 목록에서 유일한 문자들(unique characters)을 추출하려면 (예를 들어 ["Hello","World"] 라는 list 가 있다면,

["H", "e", "l", "o", "W", "r", "d"] 라는 리턴을 원한다면) 아래와 같이 코딩이 가능할 것 이다.

List<String> hello = Arrays.asList("Hello", "World");  
List<String[]> uniqueWord = hello.stream()  
            .map(word -> word.split(""))
            .distinct()
            .collect(toList());
System.out.println(uniqueWord.get(0)[0]);  

하지만 리턴값으로 원하는것이 Stream\<string[]> 이 아니고 Stream\

이 과정을 차례대로 따라가 보자.

Attempt using Arrays.stream and flatMap

먼저, 배열의 stream대신 문자들의 stream이 필요하다. Arrays.stream()은 배열을 받아 stream으로 처리한다.

String[] arrayOfWords = {"Goodbye", "World"};  
Stream<String> streamOfWords = Arrays.stream(arrayOfWords);  

flatMap은 Arrays.stream()을 통해 여러개로 나눠진 stream들을 하나로 모아 새로운 stream 을 생성한다.

방금 전 예제에 적용해보자

List<String> hello = Arrays.asList("Hello", "World");  
    List<String> words = hello.stream()
            .map(word -> word.split(""))
            .flatMap(Arrays::stream)
            .distinct()
            .collect(toList());
    System.out.println(words);

output :  
[H, e, l, o, W, r, d]

그림으로 비교해 보면 아래와 같다.

vs.

Quiz 5-2

1.Give a list of numbers, how would you return a list of the square of each number? For example, given [1,2,3,4,5] you should return [1,4,9,16,25].

   List<Integer> num = Arrays.asList(1,2,3,4,5);
   List<Integer> powNUm = num.stream()
            .map(n -> n*n)
            .collect(toList());
   System.out.println(powNUm);

   output :
   [1, 4, 9, 16, 25]

2.Given two lists of numers, how would you return all pairs of numbers? For example given a list [1,2,3] and a list[3,4] you should return [(1,3), (1,4), (2,3), (2,4), (3,3), (3,4)]. For simplicity, you can represent a pair as an array with elements.

   List<Integer> num1 = Arrays.asList(1,2,3);
   List<Integer> num2 = Arrays.asList(3,4);
   List<int[]> pairs = num1.stream()
               .flatMap(i -> num2.stream().map(j -> new int[]{i,j}))
               .collect(toList());
   pairs.forEach(pair -> System.out.println(pair[0]+","+pair[1]));

   output :
   1,3
   1,4
   2,3
   2,4
   3,3
   3,4

3.How would you extend the previous example to return only pairs whose sum is divisible by 3? For example,(2,4) and (3,3) are valid.

   List<Integer> num1 = Arrays.asList(1,2,3);
   List<Integer> num2 = Arrays.asList(3,4);
   List<int[]> pairs = num1.stream()
                           .flatMap(i -> num2.stream()
                           .filter(j -> (i+j)%3 == 0)
                           .map(j -> new int[]{i,j}))
               .collect(toList());
   pairs.forEach(pair -> System.out.println(pair[0]+","+pair[1]));

   output :
   2,4
   3,3

5.3 Finding and matching

stream의 요소들이 주어진 속성에 일치여부를 찾을 수 있는 기능을 제공한다.(allMatch, anyMatch, noneMatch, findFirst, findAny)

5.3.1 Checking to see if a predicate matches at least one element

anyMatch 는 stream의 요소중에 주어진 속성과 일치하는 것이 하나라도 있으면 true를 리턴한다. terminal operation이다.

if(menu.stream().anyMatch(Dish::isVegetarian)){  
  System.out.println("The menu is (somewhat) vegetarian friendly!!");
}

5.3.2. Checking to see if a predicate matches all elements

anyMatch 는 stream의 요소 모두가 주어진 속성과 일치하면 true를 리턴한다. terminal operation이다.

boolean isHealthy = menu.stream()  
                        .allMatch(d -> d.getCalories() < 1000);

allMatch의 반대가 nonMatch 이다. 모든요소가 주어진 속성과 일치하지 않으면 true를 리턴한다.

boolean isHealthy = menu.stream()  
                        .noneMatch(d -> d.getCalories() => 1000);

anyMatch, allMatch, noneMatch 이 3개의 기능을 short-circuiting 이라고 한다.

short-circuiting 이란

...

5.3.3. Finding an element

findAny 메소드는 임의의 요소를 반환한다. 이것은 다른 stream 기능과 결합하여 사용된다. filter 와 findAny를 통해서 채식요리를 찾을 수 있다.

Optional<Dish> dish =  
            menu.stream()
            .filter(Dish::isVegetarian)
            .findAny();
System.out.println(dish.toString());  

간단히 Optional 알아보기(chapter 10. p315)

Optional\

null이 반환될때 생길 수 있는 문제를 피하기 위하여 java8에서는 Optional이 소개되었고 자세한 설명은 chapter10에 있다.

여기서는 제공되는 메소드에 대한 간략한 설명을 한다.

  • isPresent() : Optional이 값을 포함하고 있다면 true, 아니면 false를 리턴한다.
  • ifPresnet(Consumer\
  • T get() : 값이 있으면 값을 리턴하고 없으면 NoSuchElement-Exception을 발생시킨다.
  • T orElse(T other) : 값이 있으면 값을 리턴하고 없으면 기본값을 리턴한다.

5.3.4. Finding the first element

findFirst 는 findAny와 비슷하지만, steam의 요소의 순서를 보장한다는 차이점이 있다.

List에서 3으로 나눠지는 제곱값을 찾는 경우

List<Integer> someNumbers = Arrays.asList(1,2,3,4,5);  
Optional<Integer> firstSquareDivisibleByThree =  
            someNumbers.stream()
            .map(x -> x*x)
            .filter(x -> x%3 == 0)
            .findFirst();
System.out.println(firstSquareDivisibleByThree);  

findAny 와 findFirst를 구분한 이유는 findAny는 병렬처리시 순서를 보장할 수 없기 때문이다.

5.4. Reducing

메뉴의 모든 요리의 칼로리를 더한 값은?

가장 칼로리가 높은 음식은?

reducton operations은 stream을 Integer같은 하나의 값으로 합칠 수 있는 메소드를 제공한다.

5.4.1 Summing the elements

// 모든 요소를 더한 값 
int sum = numbers.stream().reduce(0, (a,b) -> a+b);  
2개의 인수  
- 초기값 . 예) 0
- 2개의 요소를 합치고 계산하는 구문 예) (a,b) -> a+b.

// 모든 요소를 곱한 값
int product = numbers.stream().reduce(1, (a,b) -> a*b);  

그림으로 표현하면 위와 같다.

아래와 같이 좀 더 간소하게 표현할 수 있음.

List<Integer> numbers = Arrays.asList(1,2,3,4,5);  
int sum1 = numbers.stream().reduce(0, Integer::sum);  
System.out.println(sum1);

//Optional<Integer> sum2 = numbers.stream().reduce((a,b) -> (a+b));
Optional<Integer> sum2 = numbers.stream().reduce(Integer::sum);  
System.out.println(sum2);  

5.4.2. Maximum and minimum

stream에서 최대값과 최소값을 구할수 있다.

Optional<Integer> max = numbers.stream().reduce(Integer::max);  
Optional<Integer> min = numbers.stream().reduce(Integer::min); // (x,y) -> x<y?x:y  

Quiz 5-3 : reducing

How would you count the number of dishes in a stream using the map and reduce method?

int count = menu.stream()  
            .map(d-> 1)
            .reduce(0, (a,b) -> a+b);
System.out.println(count);  

or

long count = menu.stream().count();  

Benefit of the reduce method and parallelism

reduce를 사용하면 내부반복이 추상화되면서 내부 구현에서 병렬로 reduce를 실행 할 수 있음.

반면에 반복적인 합계(iteractive summation)에서는 sum 변수를 공유해야 하므로 병렬로 구현하는게 힘들고, 강제적으로 동기화 하더라도 스레드간의 경쟁으로 이득이 감소하게 된다.

7장에서는 fork/join 프레임워크를 이용하는 방법을 보게 될거지만 중요한 사실은 가변 누적자 패턴(mutable accumulator pattern)은 병렬화의 막다른 골목이다는 것이다. 그러므로 reduce 라는 새로운 패턴이 필요하게 되었다.

7장에서 stream의 모든 요소를 병렬로 더하는 코드를 방법을 배우게 된다.

int sum = numbers.parallelStream().reduce(0, Integer::sum);  

Stream operations : stateless vs. stateful

map, filter 등 : 입력 스트림에서 각 요소를 받아 0 또는 결과를 출력 스트림으로 보내기 때문에 내부 상태를 갖지 않는 연산(stateless operation)

reduce , sum, max 등 : 결과를 누적할 내부 상태가 필요하지만 스트림에서 처리하는 요소 수와 관계없이 내부 상태의 크기는 한정(bounded) 되어 있음.

반면 sorted, distinct 등 : filter 나 map 처럼 스트림을 입력으로 받아 다른 스트림을 출력하는 것처럼 보이지만, 다르다. 스트림의 요소를 정렬하거나 중복을 제거하려면 과거의 이력을 알고 있어야 한다. 예를 들어 어떤 요소를 출력 스트림으로 추가하려면 모든 요소가 버퍼에 추가되어 있어야 한다. 연산을 수행하는 데 필요한 저장소 크기는 정해져있지 않으므로 데이터 스트림의 크기가 무한이라면 문제가 생각 수 있다. 따라서 이러한 연산은 내부 상태를 갖는 연산(stateful operation)으로 간주.

연산형식반환 형식사용된 함수명함수 디스크립터
filter중간 연산Stream<t>< td="">Predicate<t>< td="">T -> boolean
distinct중간 연산 (상태 있는 언바운드)Stream<t>< td="">
skip중간 연산 (상태 있는 언바운드)Stream<t>< td="">Long
limit중간 연산 (상태 있는 언바운드)Stream<t>< td="">Long
map중간 연산Stream<r>< td="">Function<t, r><="" td="">T -> R
flatMap중간 연산Stream<r>< td="">Function<t, stream<r>><="" td="">T -> Stream<r>< td="">
sorted중간 연산 (상태 있는 언바운드)Stream<t>< td="">Comparator<t>< td="">(T, T) -> int
anyMatch최종 연산booleanPredicate<t>< td="">T -> boolean
noneMatch최종 연산booleanPredicate<t>< td="">T -> boolean
allMatch최종 연산booleanPredicate<t>< td="">T -> boolean
findAny최종 연산Optional<t>< td="">
findFirst최종 연산Optional<t>< td="">
forEach최종 연산voidConsumer<t>< td="">T -> void
collect최종 연산RCollector<t, a,="" r><="" td="">
reduce최종 연산 (상태 있는 언바운드)Optional<t>< td="">BinaryOperator<t>< td="">(T, T) -> T
count최종 연산long

5.5. 실전 연습

public class Trader {  
  private final String name;
  private final String city;

  public Trader(String n, String c){
    this.name = n;
    this.city = c;
  }

  public String getName() {
    return name;
  }

  public String getCity() {
    return city;
  }

  @Override
  public String toString() {
    return "practice.Trader:" + this.name + " in " + this.city;
  }
}

public class Transaction {  
  private final Trader trader;
  private final int year;
  private final int value;

  public Transaction(Trader trader, int year, int value) {
    this.trader = trader;
    this.year = year;
    this.value = value;
  }

  public Trader getTrader() {
    return trader;
  }

  public int getYear() {
    return year;
  }

  public int getValue() {
    return value;
  }

  @Override
  public String toString() {
    return "{" + this.trader + ", " +
            "year: " + year + ", " +
            "value: " + value + "}";
  }
}


Trader raoul = new Trader("Raoul", "Cambridge");  
Trader mario = new Trader("Mario", "Milan");  
Trader alan = new Trader("Alan", "Cambridge");  
Trader brian = new Trader("Brian", "Cambridge");

List<Transaction> transactions = Arrays.asList(  
          new Transaction(brian, 2011, 300),
          new Transaction(raoul, 2012, 1000),
          new Transaction(raoul, 2011, 400),
          new Transaction(mario, 2012, 710),
          new Transaction(mario, 2012, 700),
          new Transaction(alan, 2012, 950)
);

1.2011년에 일어난 모든 트랜잭션을 찾아 값을 오름차순으로 정리하시오.

   List<Transaction> ordredTran2011 =
               transactions.stream()
               .filter(d -> d.getYear() == 2011)
               .sorted(Comparator.comparing(Transaction::getYear))
               .collect(toList());
   System.out.println(ordredTran2011);

2.거래자가 근무하는 모든 도시를 중복 없이 나열하시오.

3.케임브리지에서 근무하는 모든 거래자를 찾아서 이름순으로 정렬하시오.

4.모든 거래자의 이름을 알파벳순으로 정렬해서 반환하시오.

5.밀리노에 거래자가 있는가?

6.케임브리지에 거주하는 거래자의 모든 트랜잭션값을 출력하시오.

7.전체 트랜잭션 중 최댓값은 얼마인가?

8.전체 트랜잭션 중 최솟값은 얼마인가?

5.6. 숫자형 스트림

5.6.3. 숫자 스트림 활용: 피타고라스 수

Stream<double[]> pythagoreanTriples2 =  
            IntStream.rangeClosed(1,100).boxed()
            .flatMap(a ->
            IntStream.rangeClosed(a,100)
            .mapToObj(
                    b -> new double[]{a, b, Math.sqrt(a*a+b*b)})
                    .filter(t -> t[2]%1 == 0));
    pythagoreanTriples2.limit(5)
            .forEach(t ->
            System.out.println(t[0] + ", " + t[1] + ", " + t[2]));

output :  
3.0, 4.0, 5.0  
5.0, 12.0, 13.0  
6.0, 8.0, 10.0  
7.0, 24.0, 25.0  
8.0, 15.0, 17.0  

5.7. 스트림 만들기

5.7.1. 값으로 스트림 만들기

Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");  
stream.map(String::toUpperCase).forEach(System.out::println);

output :  
JAVA 8  
LAMBDAS  
IN  
ACTION  

5.7.2. 배열로 스트림 만들기

int[] numbers = {2, 3, 5, 7, 11, 13};  
int sum = Arrays.stream(numbers).sum();  
System.out.println(sum);

output :  
41  

5.7.3. 파일에서 스트림 만들기

long uniqueWords = 0;

try(Stream<String> lines =  
    Files.lines(Paths.get("/Users/red/pro/java8/out/production/ch5/data.txt"), Charset.defaultCharset())){
    uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
              .distinct()
              .count();
}
catch(IOException e){  
  e.printStackTrace();
}
System.out.println(uniqueWords);  

Quiz. Fibonacci Tuples

Stream.iterate(new int[]{0,1}, t -> new int[]{t[1], t[0]+t[1]})  
            .limit(20)
            .forEach(t -> System.out.println("("+ t[0] + ","+ t[1] + ")"));


Posted by 빨강토끼
,

chapter 4. Introducing streams

4.1 What are Streams

  • 마치 SQL 쿼리 같이 데이터를 처리
  • 많은 요소를 포함하는 커다란 컬렉션을 단순히 처리

Before(Java 7)

List<Dish> lowCaloricDishes = new ArrayList<>();  
for(Dish d: menu){  
  if(d.getCalories() < 400){
    lowCaloricDishes.add(d);
  }
}

Collections.sort(lowCaloricDishes, new Comparator<Dish>() {  
  @Override
  public int compare(Dish d1, Dish d2) {
    return Integer.compare(d1.getCalories(), d2.getCalories());
  }
});

List<String> lowCaloricDishesName = new ArrayList<>();  
for(Dish d: lowCaloricDishes){  
  lowCaloricDishesName.add(d.getName());
}

자바8 코드

List<String> lowCaloricDishesName =  
  menu.stream()
  //menu.parallelStream()
  .filter(d -> d.getCalories() < 400)
  .sorted(Comparator.comparing(Dish::getCalories))
  .map(Dish::getName)
  .collect(toList());

4.2 Getting started with streams

  • Sequence of elements

    • Collections are about data

    • streams are about computations

  • Source
    • 정렬된 컬렉션으로 스트림을 생성하면 정렬이 그대로 유지된다. 즉 리스트로 스트림을 만들면 스크림의 요소는 리스트의 요소와 같은 순서를 유지한다.
  • Data processing operations
    • 함수형 프로그래밍 언어에서 일반적으로 지원하는 연산과 데이터베이스와 비슷한 연산을 지원
    • 예) filter, map, reduce, find, match, sort 등
    • 스트림연산은 순차적으로 또는 병렬로 실행될 수 있다.

In addition

  • Pipelining
    • 스트림 연산 끼리 연결하여 커다란 파이프라인을 구성 할 수 있음.
    • 게으름(laziness), 쇼트서킷(sort-cicuiting) 같은 최적화를 얻을 수 있음.
    • 데이터베이스의 SQL 쿼리 와 비슷
  • Internal iteration
    • 반복자를 이용해서 명시적으로 반복하는 컬렉션과 달리 스트림은 내부 반복을 지원
List<String> threeHightCaloricDishNames =  
            menu.stream()
            .filter(d -> d.getCalories() > 300)
            .map(Dish::getName)
            .limit(3)
            .collect(toList());
  • filter

    • 람다를 인수로 받아 스트림에서 특정 요소를 제외

    filter(d -> d.getCalories() > 300)

  • map

    • 람다를 이용해서 한 요소를 다른 요소로 변환하거나 정보를 추출

    map(Dish::getName)

  • limit

    • 정해진 개수 이상을 추출 못하게 한계를 지정

    limit(3)

  • collect

    • 스트림을 다른 형식으로 변환

    collect(toList());

4.3 Streams vs. Collections

  • DVD vs. Internet stream
  • 모든 값을 메모리에 저장하여 계산 vs. 필요할때만 요소를 계산
  • 브라우저 인터넷 검색

  • Traversavle only once

  List<String> title = Arrays.asList("java8", "In", "Action");
      Stream<String> s = title.stream();
      s.forEach(System.out::println);
      s.forEach(System.out::println);

  output :
  java8
  Exception in thread "main" In
  Action
  java.lang.IllegalStateException: stream has already been operated upon or closed
      at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:274)
      at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
      at Solution.IllegalStateException(Solution.java:77)
      at Solution.main(Solution.java:102)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:497)
      at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
  • External vs. internal iteration

    for-each loop

  List<String> names = new ArrayList<>();
  for(Dish d: menu){
    names.add(d.getName());
  }

iterator

  List<String> names = new ArrayList<>();
  Iterator<String> iterator = menu.iterator();
  while(iterator.hasNext()){
    Dish d = iterator.next();
    names.add(d.getName());
  }

stream: internal iteration

  List<String> names = menu.stream()
                           .map(Dish::getName)
                           .collct(toList());

4.4 Stream operations

  • intermediate operations

  • terminal operations

  • Intermediate operations

    • 다른 intermediate operations 와의 상호연동을 통하여 pipe line 을 형성한다.
    • intermediate operations의 동작들이 하나의 terminal operations으로 모이기 때문에 terminal operations이 호출되어서 파이프라인이 시작하기전 까지의 흐름이 동작하지 않는다( lazy)
    • 파이프라인이 어떻게 동작되고 있는지 확인하기 위하여 각각의 람다함수에 print 문을 삽입한다.(이러한 코딩은 흔히 사용되지 않지만 배우는 과정이기 때문에 특별한 경우이다.)
  List<String> names =
              menu.stream()
              .filter(d -> {
                System.out.println("filtering " + d.getName());
                return d.getCalories() > 300;
              })
              .map(d -> {
                System.out.println("mapping " + d.getName());
                return d.getName();
              })
              .limit(3)
              .collect(toList());
      System.out.println(names);


  filtering pork
  mapping pork
  filtering beef
  mapping beef
  filtering chicken
  mapping chicken
  [pork, beef, chicken]
  1. 칼로리가 300보다 큰 dish 중에 오직 3개만 추출하고 동작이 끝난다.(Short-circuitinge)
  2. filter 와 map 의 명령이 하나의 경로로 합쳐져서 동작한다.(loop fusion)

    • Terminal operations
  3. Stream pipe 로의 결과를 처리한다.

  4. List 나 Integer, 혹은 void 등의 스트림이 아닌 다양한 포맷으로 데이터를 변환하여 리턴한다.

    menu.stream().forEach(System.out::println);

    • Working with streams
  5. Builder pattern 과 비슷

  6. intermediate operations
    • filter
    • map
    • limit
    • sorted
    • distinct
  7. Terminal operations
    • forEach
    • count
    • collect

Summary

  • stream은 데이터처리동작을 지원하는 source에서 연속된 요소이다.
  • Stream은 내부반복을 사용한다.
  • intermediate 와 terminal operations 이 있다.
  • intermediate operations 은 filter와 map 등 stream을 반환하고 다른 stream와 연동된다. 파이프라인이 설정될뿐 어떤 result 동작을 하지 않는다.
  • forEach 와 같은 terminal operations 은 스트림이 아닌 결과를 리턴한다.
  • 스트림의 요소는 요청시에 계산된다.


Posted by 빨강토끼
,

기존의 Maven 으로 작성된 프로젝트를 Gradle 로 변경하는 작업을 하였다.

자신있게 gradle build 를 했다.

그런데 Maven 으로 잘되던 것이 갑자기 test step 에서 error 가 나는것이다.

이유는 
import static org.hamcrest.CoreMatchers.containsString; 
에서 containsString 을 못찾는것이다.

이유를 찾아보니 
JUnit 에 종속적인 hamcrest 의 org.hamcrest 와 
org.mockito에 있는 org.hamcrest 가 충돌나는 것이었다.

정확한 해결책인지는 아직도 확신은 안들지만 
아래같은 순서로 [프로젝트네임].gradle 파일의 내용을 수정하니 잘되었다.

dependencies {  
  testCompile group: 'org.hamcrest', name: 'hamcrest-core', version: '1.3'
  testCompile group: 'org.mockito', name: 'mockito-all', version:'1.10.19'
  testCompile group: 'junit', name: 'junit', version:'4.12'
}

아마도 dependencies 하는 순서대로 호출시 참조하는 것 같다.

Posted by 빨강토끼
,

나는 java 에서 배열을 shift 하는 방법이 가끔 헛갈릴때가 있다.

그중 rotation left 하는 방법을 정리하였다.

그런데 이 방법은 메모리를 주어진 배열의 2배를 사용한다. 주어진 배열만큼의 배열을 하나 더 생성하여서 옮겨진(rotation left) 위치에 copy를 하는 방식이다.

좀 더 효율을 높히기 위하여 생각할수있는 방법은 크게 2가지이다.

  1. 메모리 사용을 줄이기 위하여 직접 in-place 방법으로 처리하는 방식
  2. 속도를 높히기 위해서 하나하나를 copy하는 방식이 아닌 block단위로 copy하는 방식

1번 소스

2번 소스

Posted by 빨강토끼
,

SpringBoot를 사용하는데 요청하는 기존 레거시 시스템이 한글 인코딩을 EUC-KR로 보내고 있다.

한글처리가 잘안되서 검색해보니 http://theeye.pe.kr/archives/2206 
라는 글을 발견하였다.

그런데도 잘 안되었다.

그런데 이것 저것 조금더 수정해보니 된다. ㅎ

주요한것은 
public Filter characterEncodingFilter() -> public CharacterEncodingFilter characterEncodingFilter(
로 바꾸었다.

참조 http://emflant.tistory.com/97

Posted by 빨강토끼
,

학습비용이 높은 편이라 팀에 하이버네이트 전문가가 없다면, 초기 도입비용이 높을수 있다.

  • 쿼리 힌트나 DB에 종속적인 쿼리에 대한 의존도가 높은 경우
  • 네이티브 쿼리(하이버네이트가 생성해주는 쿼리가 아니라 데이터베이스 벤더에 종속적으로 작성한 SQL을 사용하는) 비율이 높은 경우
  • 애플리케이션 개발이 객체지향적이지 않은 경우

Write Behind, Dirty Checking, Lazy Fetching 등의 장점을 살리기 어렵다.

참고로.

Write behind

Write-behind caching. 하이버네이트의 Session에서 기본으로 제공해주는 기능으로서, 정말로 쿼리를 실행해야 하는 순간까지 최대한 쓰기 작업을 미루는 기능.

Dirty Checking

하이버네이트를 사용해서 읽어온 객체는 항상 그 상패의 변화를 주시하고 있다. 그러다가 만약 트랜잭션이 커밋되면 , 하이버네이트가 알아서 테이터베이스에 해당 객체의 상태 변화를 적용시킨다.

Lazy Fetching

객체를 조회시 실제로는 비어있는 가짜객체(Proxy 객체)를 만들어 가짜객체의 데이터에 접근하면 하이버네이트가 그제서야 해당 객체를 채우는데 필요한 데이터를 가져다 준다.

Posted by 빨강토끼
,

- 기본 프로젝트 생성성

mvn -B archetype:generate -DgroupId=com.example -DartifactId=hajiboot -Dversion=1.0.0-SNAPSHOT -DarchetypeArtifactId=maven-archetype-quickstart


- 프로젝트에 사용하는 라이브러리의 종속성 확인

mvn dependency:tree

Posted by 빨강토끼
,

an error has occurred see the log file.

configuration 폴더안의 [숫자].log 를 보라는 메시지가 뜨면서 실행이 안된다.


log 파일을 열어보면 맨 아래쯤에 아래와 같은 내용이 보인다.

!MESSAGE Application error

!STACK 1

java.lang.IllegalStateException: Unable to acquire application service. Ensure that the org.eclipse.core.runtime bundle is resolved and started (see config.ini).

at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:78)

at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:380)

at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:235)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

at java.lang.reflect.Method.invoke(Method.java:597)

at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:669)

at org.eclipse.equinox.launcher.Main.basicRun(Main.java:608)

at org.eclipse.equinox.launcher.Main.run(Main.java:1515)


이유는 간단하다.


혹시 여러분이 JDK6이하를 사용하고 있는 경우일 수 있다.


STS 3.7.0 부터는 JDK7 이상을 사용한다.


JDK7 이나 그 이상의 JDK 를 설치하고 다시 실행해 보기 바란다. 



Posted by 빨강토끼
,

Jenkins 을 스케줄링용도로 사용시 장점

  • Job 추가/삭제/목록 등의 UI를 통해서 쉽게 관리 가능
  • Job 순서에 대한 개런티를 보장함
    (즉 선행된 동일 Job이 끝날때 까지 대기)
  • 다양한 플러기인 제공
  • 클러스터링을 통한 중앙 집중 관리
  • 심플한 Job Flow를 제공
  • 실행 Job 앞뒤로 before, after 처리 가능
  • Java 외에 모든 언어 및 shell를 지원
  • Job 실패 와 성공 유뮤를 UI를 통해서 확인 가능
  • 실행 중인 Job을 취소 할 수 있음.
  • Job 시작/종료 시간을 알 수 있음.


Jenkins 을 스케줄링용도로 사용시 단점

  • 매번 Job를 hard하게  run (java -jar ….)하기 때문에세밀한 스케줄러 타이밍 어려움 (스프링으로 된 Job일 경우 10 ~ 15초 정도 delay)
  • 초 단위 Batch를 정확하게 스케줄링 하기 어려움
  • 비즈니스 로직에 의한 스케줄러 실행 유무가 어려움

하지만 개인적인 생각으로는 특별한 경우를 제외하고는 Quartz 기반으로 다른 기능들을 조합해서 

구현 및 운영하는 것이 좋다고 생각한다.


Jenkins 는 지속적인 통합빌트(C.I) 를 위한 툴의 성격이 강한 이유이다.


Jenkins의 장점으로 거론되는 기능들은 Spring Batch 나 DB 를 이용하여 Job을 관리하는 JDBCJobStore 를 사용하면 구현 가능하다.  


오히려 일반적인 경우 단점이 너무 크다.


Posted by 빨강토끼
,

spring 을 사용하여 quartz 를 사용하려면 아래와 같은 구조를 흔히 사용하게 된다.




Hello : 개발자가 직접 작성할 비즈니스 컴포넌트.


JobDetailBean : Job과 Job 속성 및 파라미터 정보를 모아놓은 컴포넌트.


TriggerBean : Job 실행 시작, 주기등을 설정하는 컴포넌트.


SchedulerBean : 모든 Job 설정 정보를 갖고 Manager Thread를 생성해서 해당 시간에 Job을 실행하는 컴포넌트.


그리고 각 bean 들을 FactoryBean 을 사용하여 생성한다.


일단 먼저 spring framework 를 설정해야된다.


중요 모듈은 아래와 같다.


모듈 : 설명 

spring-core.jar : 스프링코어 

spring-context-support.jar : Quartz 지원


Maven 으로 설정하기(pom.xml)

<dependency>

  <groupId>org.springframework</groupId>

  <artifactId>spring-core</artifactId>

  <version>4.1.1.RELEASE</version>

</dependency>

<dependency>

  <groupId>org.springframework</groupId>

  <artifactId>spring-context-support</artifactId>

  <version>4.1.1.RELEASE</version>

</dependency>



그리고 당연히 quartz 도 설정해야된다.

http://www.quartz-scheduler.org/downloads


모듈 : 설명

quartz-2.2.1.jar : Quartz 코어 

quartz-jobs-2.2.1.jar : Quartz Job 지원


Maven

<dependency> 

  <groupId>org.quartz-scheduler</groupId>

  <artifactId>quartz</artifactId> 

  <version>2.2.1</version> 

</dependency> 

<dependency> 

  <groupId>org.quartz-scheduler</groupId> 

  <artifactId>quartz-jobs</artifactId> 

  <version>2.2.1</version> 

</dependency>




output



Posted by 빨강토끼
,