[Java] Lambda&stream
Why?
지금까지 회사에서 for문을 이용해서 작성했던 코드를 코드 리팩토링을 하면서 알게된 람다를 써보게 되었다. 굉장히 코드가 깔끔해지면서 가독성도 좋아져서 어떤 이점이 있는지도 한번 파헤쳐보고 싶어 이 글을 작성하게 되었다.
Lambda?
람다는 최초로 자바 1.8에서부터 지원을 하기 시작했다. 따라서 람다를 사용하고 싶다면, 자바 1.8 이상이 되어야만 한다.
-
java 1.8 에서부터 지원한다.
-
람다 표현식(lambda expression)이란 간단히 말해 메소드를 하나의 식으로 표현한 것이다.
-
코드를 간결하게 만들 수 있다.
-
람다는 익명 함수이다.
-
익명함수란, 이름이 없는 함수를 의미한다. 예시 : (매개변수) -> {실행코드}
-
// ex1) list의 sum구하기 List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); Integer result = list.stream().reduce(0, (a, b) -> { System.out.println(a + b); return a + b; }); // reduce 뒤에 나오는 것을 익명함수라고 한다.
-
stream
-
stream은 자바 8부터 추가된 컬렉션(배열 포함)의 저장 요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 해주는 반복자이다.
-
stream과 Iterator반복자와 비교
-
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); // Iterator 사용 Iterator<Integer> itor = list.iterator(); while(itor.hasNext()) { int nReuslt = itor.next(); System.out.println(nReuslt); } System.out.println(); // Stream 사용 list.stream().forEach(num->System.out.println(num));
-
-
stream과 for문 비교
-
stream은 for문과 달리 메모리를 절약할 수 있다.
-
// ex1) list에서 짝수를 필터링 하기 List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); List<Integer> result = new ArrayList<>(); for (int i = 0; i < list.size(); i++) { if (0 == (i % 2)) { result.add(i); } } // list를 옮기기 위해 새로운 메모리(변수 result)가 필요함
-
// ex2) lambda를 이용한 list 필터링 하기 List<Integer> list = Arrays.asList(1, 2, 3, 4, 5) .stream() .filter(num->(num%2==0)) .collect(Collectors.toList()); // 추가적인 메모리가 필요없음
-
-
위예 예시에서 볼 수 있듯이 코드가 간결하고 직관적이게 된다.
-
또한 변수 타입을 따로 선언하지 않는다.
- 이것은 런타임 시에 대입되는 값에 따라 자동으로 인식 될 수 있기때문에 람다식에서는 변수의 타입을 일반적으로 업급하지 않는다.
-
- 스트림이 짧고 간결하다고하여 무조건 좋다고 할수는 없다!
- 다만, 그 차이가 굉장히 미비하다고 생각하기 때문에 가독성 측면에서는 stream을 추천한다. (코딩 컨벤션으로 막혀있다면 어쩔수 없다.)
Refactoring
- 간단한 예시를 들면서 글을 마치도록 하겠습니다.
public class Main {
static List<Product> productList = Arrays.asList(
new Product("아이폰", 739123,480000, "Y", "애플"),
new Product("후드티", 132455,53000, "Y", "폴로"),
new Product("기프트카드", 43212,50000, "N", "CJ"),
new Product("맥북", 65456123,2700000, "Y", "애플"),
new Product("마스크", 32132,10000, "N", "코려")
);
public static void main(String[] args) {
// "가격이 50만원 이하 & 재고가 있는거 & 브랜드가 애플인거"
List<Product> resultList = new ArrayList<>();
for (int i = 0; i < productList.size(); i++) {
Product product = productList.get(i); // 지역변수 필요
if (500000 >= product.getPrice() && product.getIsExistStock().equals("Y") && product.getBrandName().equals("애플")) {
resultList.add(product);
}
}
}
}
class Product {
private String productName; // 상품 이름
private int productCode; // 상품 코드
private int price; // 상품 가격
private String isExistStock; // 재고 여부
private String brandName; // 브랜드 이름
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public int getProductCode() {
return productCode;
}
public void setProductCode(int productCode) {
this.productCode = productCode;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getIsExistStock() {
return isExistStock;
}
public void setIsExistStock(String isExistStock) {
this.isExistStock = isExistStock;
}
public String getBrandName() {
return brandName;
}
public void setBrandName(String brandName) {
this.brandName = brandName;
}
public Product(String productName, int productCode, int price, String isExistStock, String brandName) {
this.productName = productName;
this.productCode = productCode;
this.price = price;
this.isExistStock = isExistStock;
this.brandName = brandName;
}
}
- 위 코드에서 main로직 부분을 stream으로 전환하는 예시다.
- 코드가 간결해지면서 가독성이 좋아진 모습을 볼 수 있다.
List<Product> usedStream = productList.stream()
.filter(product -> product.getPrice() < 500000)
.filter(product -> product.getIsExistStock().equals("Y"))
.filter(product -> product.getBrandName().equals("애플")).collect(Collectors.toList());
글을 마치며..
위 예제들은 가벼운 예제들이라 for문과 stream의 차이가 크게 없다고 생각할 수 있지만 실무에서 가독성이란 것은 굉장히 큰 무기가 될 수 있다. 특히 이 예제에서는 필터링하는 부분만 사용했는데, stream에서 사용할 수 있는 함수는 굉장히 많다. stream을 적재적소에 잘 사용해 보자!