반응형

실무에서 아직 jdk1.6을 사용하지만 최신 자바에 대해 공부하는 중 가장 컸던 변화라 생각한 Stream에 대해 정리하고자 한다.

 

Stream

Stream은 자바8부터 추가된 기능으로 컬렉션, 배열과 같은 자료구조를 함수형 인터페이스(람다)를 적용하며 처리하는 기능입니다. 즉, 데이터를 담고 있는 자료구조(컬렉션)이 아닙니다.

스트림은 '데이터의 흐름'이며 적절한 operation을 통해 데이터를 가공해 원하는 타입으로 제공합니다.

 

자바8 이전에는 배열이나 컬렉션을 다루기 위해 for 또는 foreach를 통해 하나씩 꺼내서 다뤘습니다. 간단한 경우는 큰 문제가 없었지만 로직이 복잡해지면 코드의 양은 많아지고 향후 유지보수에도 어려움이 발생합니다.

 

아래 코드는 기존 for문을 사용한 방법과 stream을 사용한 방법의 예시입니다.

import java.util.*;
public class StreamExample{

     public static void main(String []args){
        List<String> names = new ArrayList<>();
        names.add("seungwoo");
        names.add("sw");
        names.add("jsw");
        names.add("java8");
        
        // 자바 8 이전 for문을 사용한 방법
        for (int i=0; i<names.size(); i++) {
            if (names.get(i).startsWith("s")) {
                System.out.println(names.get(i).toUpperCase());
            }
        }

        // stream을 사용한 방법
        names.stream().filter(name -> name.startsWith("s"))
                .map(name -> name.toUpperCase())
                .forEach(System.out::println);
     }
}

위 코드에서 stream을 이용한 방법을보면 메서드 체이닝을 통해 연산을 합니다. 이러한 오퍼레이션은 중개 오퍼레이션, 종료 오퍼레이션 2가지가 존재합니다.

 

중개 오퍼레이션

 

  • Stream을 리턴함
  • Lazy하다
  • filter, map, limit, skip, sorted, flatmap...

여기서 lazy하다는 말은 종료 오퍼레이션이 나오기 전까지는 실행하지 않는 것을 뜻합니다. 즉 결과가 필요하기 전까지 연산의 시점을 최대한 늦추는것을 뜻합니다.

List<String> names = new ArrayList<>();
        names.add("seungwoo");
        names.add("sw");
        names.add("jsw");
        names.add("java8");
        
// list의 내용을 바꾸지 않고 단지 stream으로 가지고 있음
// 중계 operation, 종료 operation이 있음
// 중계 operation는 기본적으로 lazy한데 중계 operation은 stream을 리턴하며 종료 operation은 stream이 아닌 다른타입을 리턴함.
Stream<String> stream = names.stream()
	.map(String::toUpperCase);

이처럼 종료 오퍼레이션이 없는경우 연산을 하지 않습니다.

 

종료 오퍼레이션

  • Stream을 리턴하지 않음
  • collect, allMatch, count, forEach, min, max...
List<String> names = new ArrayList<>();
        names.add("seungwoo");
        names.add("sw");
        names.add("jsw");
        names.add("java8");

long count = names.stream()
		.filter(name -> name.startsWith("s"))
		.count();
System.out.println(count);

종료 오퍼레이션이 있는경우 연산이 실행됩니다.

 

 

반응형
반응형

 최근 코딩테스트를 진행하면서 정규표현식과 관련된 문제가 나와 다시 한번 정리해 보기로 결심했다. 

정규표현식이란 특정 규칙을 가진 문자열을 표현하기 위해 쓰이는 식입니다. 개발을 진행하면 보통 주민등록번호, 휴대폰 번호, 이메일주소와 같이 정해져 있는 규칙이 있고 사용자가 형식에 맞게 입력했는지 검증할때 주로 사용합니다.

사실 기존에 정규표현식을 사용할 일이 있으면 구글링을 통해 정규표현식을 확인하고 사용해서 규칙을 정확히 숙지하고 있지 않았습니다. 근데 코딩테스트에서 검색없이 진행하려니 많은 어려움이 있었습니다.

 

아래는 정규표현식의 기본 작성법입니다.

자바에서는 이러한 정규표현식을 사용하기 위해 Pattern과 Matcher클래스를 사용합니다. 간단한 예제를 통해 살펴보겠습니다.

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {
    public static void main(String[] args) {

        // test를 위한 임시 String 배열
        String[] names = {"seungwoo", "sw", "jsw"};

        // 소문자 sw로 시작하는 단어
        Pattern pattern = Pattern.compile("sw[a-z]*");

        for (String name : names) {

            // 정규식으로 비교할 대상을 매개변수로 Pattern 클래스의 matcher 메서드를 호출해 인스턴스를 얻음
            Matcher matcher = pattern.matcher(name);

            // 정규식에 부합한지 체크
            if (matcher.matches()) {
                System.out.println(name);
            }
        }
    }
}

 

결과

 

Pattern클래스의 compile메서드를 통해 정규표현식으로부터 패턴을 만듭니다. 이후 패턴에 맞는 Matcher 객체를 pattern.matcher 메서드를 통해 반환하고 정규식에 부합한지 체크하는 과정입니다.

 

Matcher클래스의 matches 메서드는 정규식에 부합하면 true, 부합하지 않으면 false를 반환합니다.

Pattern과 Matcher 클래스의 더 상세한 메서드는 API를 통해 쉽게 확인할 수 있습니다.

 

정규표현식의 사용법은 간단하나 정규표현식 기본규칙을 암기하고 있지 않으면 이러한 문제를 만났을때 당황할 것 같습니다.

사실 모든걸 외울 수는 없으나 기본적인 정규표현식 규칙은 암기하고 있어야 할 것 같습니다.

 

반응형
반응형

Jsoup을 이용한 웹 크롤링 간단 예제 입니다.

Jsoup을 사용하기 위해서는 의존성을 받아야 하는데 maven repository에서 받으면 됩니다.

 

https://mvnrepository.com/

 

Maven Repository: Search/Browse/Explore

Infinispan Hot Rod Client Last Release on Jun 15, 2020

mvnrepository.com

저는 네이버 증권 페이지에서 진행하겠습니다. 네이버 증권 페이지를 들어가고 개발자 도구에 들어가서 코스피 지수를 확인합니다.

 

코스피 지수의 태그를 선택하고 개발자 도구에서 ...을 클릭하면 그림과 같이 css selector를 클립보드로 복사 할 수 있습니다.

여기서 저는 코스피 지수와 변동지수만 가져오도록 하겠습니다.

 

KospiCrawer

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;

public class KospiCrawer {
    private static final String CONNECT_URL ="https://finance.naver.com/";
    
    // 현재 코스피지수
    public String getKospi() throws Exception {
        Document document = Jsoup.connect(CONNECT_URL).get();
        Elements kospi = document.select("#content > div.article > div.section2 > div.section_stock_market > div.section_stock > div.kospi_area.group_quot.quot_opn > div.heading_area > a > span > span.num");
        return kospi.text();
    }

	// 코스피 변동지수
    public String getKospiChangeRate() throws Exception {
        Document document = Jsoup.connect(CONNECT_URL).get();
        Elements kospiChangedRate = document.select("#content > div.article > div.section2 > div.section_stock_market > div.section_stock > div.kospi_area.group_quot.quot_opn > div.heading_area > a > span > span.num2");
        return kospiChangedRate.text();
    }
}

 

copy selector로 복사한 것을 select메서드를 활용해 가져왔습니다.

 

Main

public class CrawlingApplication {

    public static void main(String[] args) throws Exception {
        KospiCrawer kospiCrawer = new KospiCrawer();
        String currentKospi = kospiCrawer.getKospi();
        String changeKospiRate = kospiCrawer.getKospiChangeRate();
        System.out.println("현재 코스피 : " + currentKospi + " 변동율 : " + changeKospiRate);
    }

}

 

결과

이처럼 Jsoup을 사용하면 간단하게 크롤링을 할 수 있다. 하지만 크롤링을 이용해 상업적으로 이용한다면 문제가 있을 수 있으니 주의하도록 하자.

반응형

+ Recent posts