TIL 18 [ 키오스크 과제 (도전🔥 2단계) + Java ( 컬렉션, 스트림 ) ]

2025. 3. 13. 21:55·[내일배움캠프-Sparta]/Spring 6기
반응형
import java.util.HashSet;
import java.util.Set;

public class SetExample {
    public static void main(String[] args) {
        Set<String> fruits = new HashSet<>();
        fruits.add("Apple");
        fruits.add("Banana");
        fruits.add("Orange");

        // 중복 데이터 추가 시, 무시됨
        fruits.add("Apple");

        System.out.println(fruits);
        // 출력 예시: [Orange, Banana, Apple] (순서는 랜덤)

        // 포함 여부 확인
        System.out.println("Banana가 포함되어 있나요? " + fruits.contains("Banana"));
		// contains 메소드 포함 여부 있으면 T , else F
        // 반복문으로 순회
        for (String fruit : fruits) {
            System.out.print(fruit + " "); 
        }  // Orange Banana Apple (순서는 무관)
    }	
}

1. 학습내용 : 키오스크 과제 도전1단계, 알고리즘 Daily 풀기

★ 키오스크 도전 2단계 ★

(Challenge) Lv 2. Enum, 람다 & 스트림을 활용한 주문 및 장바구니 관리

Kiosk, Main, Menu, MenuItem 클래스는 하나의 수정도 없이 동일함.

이번 단계 에서는 Enum을 활용하여 주문을 할때 할인율을 적용할 것인지 선택하게 구현 하고, 람다 & 스트림을 통해서 보다 간편하게 코드량을 줄여서 주문 및 장바구니 관리를 할 것이다. ( 해보는데까지 ! ! )

package com.example.kiosk;

public enum User {
    // Enum을 이용해 사용자의 할인율을 관리함.
    SOLDIER("군인 : 10%"){
        @Override
        public double discount() {
            System.out.println("군인 할인율을 적용합니다.");
            return 0.1;
        }
    },
    EMPLOYEE("직원 : 7%"){
        @Override
        public double discount() {
            System.out.println("직원 할인율을 적용합니다.");
            return 0.07;
        }
    },
    STUDENT("학생 : 3%"){
        @Override
        public double discount() {
            System.out.println("학생 할인율을 적용합니다.");
            return 0.03;
        }
    },
    General("일반 : 0%") {
        @Override
        public double discount() {
            System.out.println("일반인은 할인율이 없습니다.");
            return 1;
        }
    };

    public abstract double discount();
    final String user;

    User(String user) {
        this.user = user;
    }

    public String getUser() {
        return user;
    }
}

User ( Enum 타입 )

package com.example.kiosk;

import java.util.*;

public class Cart {
    // 장바구니 클래스를 담당한다.
    private final List<MenuItem> menuItems = new ArrayList<>();
    Scanner sc = new Scanner(System.in);
    private Integer sum = 0; // 총 가격

    // 장바구니 출력 및 금액 계산
    boolean showInfo() {
        if (!menuItems.isEmpty()) { // 장바구니가 비어 있지 않으면, 정보 출력
            System.out.println("아래와 같이 주문 하시겠습니까?");
            System.out.println("[ Orders ]");

            for(int i=0; i<menuItems.size(); i++) {
                MenuItem menuItem = menuItems.get(i); // menuItem 객체 접근
                System.out.print((i+1)+ ". ");
                menuItem.showInfo();
            }
            // 스트림화 ( 현재 코드에선 for문이 훨씬더 깔끔함. )
//            IntStream.range(0, menuItems.size())
//                     .forEach(i -> {
//                         MenuItem menuItem = menuItems.get(i);
//                         System.out.print((i+1) + ". ");
//                         menuItem.showInfo();
//                     });
            System.out.println("\n[ Total ]");
//            sum = 0;  // 총합 계산;
//            for (MenuItem menuItem : menuItems) {
//                sum += menuItem.getPrice();
//            }
            // 스트림화
            sum = menuItems.stream().mapToInt(MenuItem::getPrice).sum();

            System.out.println(sum + "원\n");
            System.out.println("1. 주문       2. 메뉴판");
            discountShow();
            System.out.println("주문 하시겠습니까?(1 주문): ");
            int choose = sc.nextInt();
            if (choose == 1 && sum > 0) { // 주문시, 합계금액이 0원을 넘어야 주문가능
                System.out.println("\n주문이 완료 되었습니다. 금액은 " + sum + "원 입니다.\n");
                return true; // 튜터님 도움받아서 void 말고 boolean 으로 반환.
            }
        }
        System.out.println("\n주문한 상품이 없습니다.\n");
        return false;
    }

    // 장바구니 담기
    void addCart(MenuItem menuItem) {
        menuItem.showInfo();
        System.out.println("위 메뉴를 장바구니에 추가하시겠습니까?");
        System.out.println("1. 확인        2. 취소");
        int choose = sc.nextInt();
        if (choose == 1) {
            menuItems.add(menuItem);
            System.out.println("\n" + menuItem.getName() +"이(가) 장바구니에 추가 되었습니다.\n");
        } else if (choose == 2) {
            System.out.println("\n취소 되었습니다.\n");
        } else {
            System.out.println("\n잘못된 입력 값입니다.\n");
        }
    }

    // 장바구니 초기화
    void cartReset() { // 튜터님 도움 받아서 clear 메소드를 알게됨.
        if(!menuItems.isEmpty()) { // 비어있지 않다면 초기화.
            menuItems.clear();
            System.out.println("\n진행중이던 주문을 초기화 하였습니다.\n");
        } else {
            System.out.println("\n주문한 상품이 없습니다.\n");
        }
    }

    // 할인표 보여주기
    void discountShow() {
        System.out.println("\n할인 정보를 입력해주세요");
        for(int i=0; i < User.values().length; i++) { // 튜터님 도움을통해 Enum 메소드 !
            System.out.println((i+1) + ". " + User.values()[i].getUser());
        }
        // 스트림화 ( for문이 훨씬 더 깔끔 튜터님 조언 )
//        IntStream.range(0, User.values().length)
//                 .forEach(i -> {
//                     System.out.println((i+1) + ". " + User.values()[i].getUser());
//                 });

        try {
            int choose = sc.nextInt();
            if (choose == 1) {
                sum = sum - (int) (sum * User.SOLDIER.discount());
            } else if (choose == 2) {
                sum = sum - (int) (sum * User.EMPLOYEE.discount());
            } else if (choose == 3) {
                sum = sum - (int) (sum * User.STUDENT.discount());
            } else if (choose == 4) {
                sum = sum - (int) (sum * User.General.discount());
            }
            else {
                throw new Exception(); // throw문으로 예외처리를 해결 (튜터님 도움)!!
            }
        } catch (InputMismatchException e) {
            System.out.println("\n해당 입력값은 유효하지 않습니다.\n");
        } catch (Exception e) { // catch문은 여러개 작성가능하다.
            System.out.println("\n유효 하지않은 번호입니다.\n");
        }
    }

    public List<MenuItem> getMenuItems() {
        return menuItems;
    }

}

Cart 클래스

Enum 타입을 이용해서 구매자가 주문시 할인율을 받을 것인지 물어보고 입력한 값에 따라 할인율을 선택하게 된다.

try-catch문을 통해 예외적인 입력 값이 입력될 가능성을 고려해서 예외 처리를 하였다.

되도록 스트림화는 for문 위주로 스트림화를 시도해보았는데 그렇게 많이 해보지는 않았다. 오히려 for문이 가독성이 더 좋은 것들이 많았고, stream에 대해 개념은 이해가 되지만 적용하는것이 너무 어려웠다.

 

[트러블 슈팅]

  • 배경 : Cart클래스에 스트림화 시켜서 주문 메뉴들을 이제 주문할때 가격을 합산 하려고 할때, 0원이 나오는 현상 발견.
  • 발단 : 스트림화를 잘못 했었는가? 등 의문이 들기 시작함.
  • 전개 : 스트림에 대해서는 잘 작동하지만 저장을 안시키고 있엇나? 확인함.
  • 위기 : 결국에는 sum 필드에 저장을 하지 않고, 프로그램이 스트림만 읽는 거라고 판단함.
  • 절정 : sum 에 직접 다시 그 스트림 코드를 대입 시켜주니까 해결됨
  • 결말 : 단순히 대입을 하지 않아서, 0원 현상이 생긴것임. 컴파일러는 그대로 코드를 읽고 있엇을 뿐.

{ 자바 문법 특강 }

[배열(Array)]

같은 타입의 데이터를 연속된 메모리 공간에 저장하는 자료 구조

 

특징 

  • 고정 크기 (Static Size)
    • 배열을 선언 할시 한번 크기를 정하면 다시는 되돌릴수 없음. ( 불변성 )
  • 동일한 타입
    • 배열은 하나의 타입으로만 데이터를 저장함.
String[] arr = {"apple", "lemon", "orange"};
  • 인덱스(Index) 접근
    • 인덱스는 0부터 시작, 배열길이 - 1 까지만 유효.
  • 간단한 구조 & 메모리 효율
    • 배열은 구조가 단순하고 연속된 메모리를 사용해 성능이 좋음.
    • 크기가 고정이라는 큰 단점, 중간 데이터 삽입 / 삭제 어려움. ( 유연성 ↓ )

[컬렉션] ★ ★

특징

  • 동적 크기
    • 필요에 따라 크기가 자동으로 조절됨 ( 유연성 ↑ )
      • add() 메소드로 추가
      • remove() 메소드로 삭제
      • clear() 전부 삭제
  • 유연한 데이터 타입
    • 제네릭을 사용해 다양한 타입 데이터를 저장
  • 다양한 자료구조
    • 순서가 있는 List,  순서가 없는 Set, 키-값 쌍으로 이루어진 Map등 다양한 구조 제공
  • 풍부한 메소드
    • 삽입, 삭제, 검색, 정렬 등의 기능을 손쉽게 수행할 수 있는 다양한 메소드를 제공

정리 하자면,

특징 배열 (Array) 컬렉션 (Collection)
크기 고정 (  유연성 ↓ ) 동적 ( 유연성 ↑ )
데이터 타입 동일한 타입만 제네릭으로 다양한 타입 가능
데이터 구조 단일 구조 다양한 구조 (List, Set, Map 등)
사용법 단순  다양한 메소드와 구조로 인해 상대적으로 복잡하지만 유연함.
성능 인덱스 접근이 빠름 데이터 구조마다 다름.
메모리 사용 효율 고정 크기로 효율적 ( 빠름 ) 필요에 따라 크기가 늘어나므로 비효율적 일수 있음.
기본 기능 제한적 풍부한 메소드 ( 삽입, 삭제, 검색, 정렬 )

 

※ 주요 인터페이스 : List, Set, Map ★ ★

인터페이스  대표 구현 클래스  특징
List ArrayList, LinkedList 순서가 있음, 중복 O
Set HashSet, TreeSet 순서가 없음, 중복 X
Map HashMap, TreeMap 키-값 쌍으로 이루어지며, 순서가 없음
중복은 키는 X ,값만 O

 

ArrayList

import java.util.ArrayList;
import java.util.List;

public class ListExample {
    public static void main(String[] args) {
        List<String> fruits = new ArrayList<>();
        fruits.add("Apple");
        fruits.add("Banana");
        fruits.add("Orange");

        // 중복 데이터도 추가 가능
        fruits.add("Apple");

        System.out.println(fruits);
        // 출력 예시: [Apple, Banana, Orange, Apple]

        // 인덱스로 값 가져오기
        System.out.println("인덱스 1의 값: " + fruits.get(1)); // Banana

        // 반복문으로 순회
        for (String fruit : fruits) {
            System.out.println(fruit);
        }  // Apple
    }	   // Banana
} 	   // Orange
	   // Apple

HashSet

import java.util.HashSet;
import java.util.Set;

public class SetExample {
    public static void main(String[] args) {
        Set<String> fruits = new HashSet<>();
        fruits.add("Apple");
        fruits.add("Banana");
        fruits.add("Orange");

        // 중복 데이터 추가 시, 무시됨
        fruits.add("Apple");

        System.out.println(fruits);
        // 출력 예시: [Orange, Banana, Apple] (순서는 랜덤)

        // 포함 여부 확인
        System.out.println("Banana가 포함되어 있나요? " + fruits.contains("Banana"));
		// contains(값) 해당 값이 포함되어 있으면 T , 없으면 F
        // 반복문으로 순회
        for (String fruit : fruits) {
            System.out.print(fruit);
        } // Orange Banana Apple ( 순서는 무관 , 랜덤 )
    }
}

컬렉션의 구조

※ 컬렉션에서 사용하는 변수 타입 ( 기본 타입 & 래퍼타입 ) ★ ★

기본 타입 

  • 정수형 : byte, short, int, long
  • 실수형 : float, double
  • 문자형 : char
  • 논리형 : boolean

래퍼 타입 

  • 정수형 : Byte, Short, Integer, Long
  • 실수형 : Float
  • 문자형 : Character
  • 논리형 : Boolean

컬렉션에서 래퍼 클래스를 사용하는 이유 

1. 객체 지향 설계

  • 자바의 컬렉션은 객체를 다루도록 설계됨. 원시타입은 객체가 아니므로, 컬렉션은 래퍼클래스 사용 권장

2. 기능 확장

  • 래퍼 클래스는 원시 타입에 없는 여러 메소드를 제공함. ex) Integer.parseInt()

3. 제네릭 사용

  • 자바의 제네릭은 객체 타입을 요구합니다. 원시 타입은 제네릭 타입 파라미터로 사용할수 없으므로 래퍼클래스 사용 권장함.

4. 값의 불변성

  • 래퍼 클래스 불변 객체로, 멀티스레드 환경에서 안전하게 사용할 수 있습니다.

[스트림] ★ ★

배열이나 컬렉션의 데이터를 함수형(람다)로 다루기 위한 추상화된 데이터 처리 방식

반복문으로 하나씩 꺼내서 처리하던 방식을 벗어나, 중간 연산과 최종 연산을 체이닝 하여 선언적으로 데이터 처리.

※ 스트림과 I/O 스트림과는 다른 개념임 ! !

특징

  • 함수형 스타일 : for문 대신 함수형 메소드로 연쇄적으로 적용해 가독성을 높임.
  • 데이터 변경 X : 스트림은 원본 데이터를 변경하지 않음 ( 불변성 )
  • 지연 연산 : 중간 연산을 결과를 바로 내지않고, 최종 연산이 실행될 때 한 번에 처리.
  • 병렬 처리 : 스트림을 병렬로 처리해 멀티코어 환경에서 성능을 높일수 있음.★ 대표적인 스트림 쓰는이유.

스트림의 처리 단계

  1. 스트림 생성 (Create): 배열, 컬렉션, 혹은 기타 소스에서 스트림 객체를 생성
  2. 중간 연산 (Intermediate Operations): 맵핑, 필터링 등으로 데이터를 가공
  3. 최종 연산 (Terminal Operation): 결과를 집계하거나, 리스트로 다시 모으거나, 화면에 출력

이 과정을 체이닝(Chaining) 으로 연결하여 선언적으로 작성할 수 있습니다.

List<String> fruits = Arrays.asList("apple", "banana", "avocado", "grape");
fruits.stream() // 스트림 생성
        .filter(s-> s.equals("apple")) // 중간연산 1 ( "apple"과 같으면 요소 조건)
        .map(String::toUpperCase) // 중간 연산 2 ( 필터링된 요소 String타입에 , 모두다 대문자로 변환)
        .forEach(System.out::println); // 최종연산

스트림 생성 방법

1. 컬렉션(Collection)에서 생성

List<String> list = Arrays.asList("a", "b", "c", "d");
Stream<String> stream = list.stream();

2. 배열에서 생성

String[] arr = {"a", "b", "c", "d"};
Stream<String> streamFromArray = Arrays.stream(arr);

3. Stream.of() 메서드 사용

Stream<String> streamOf = Stream.of("a", "b", "c", "d");

4. IntStream, LongStream, DoubleStream (기본형 특화 스트림)

 

  • DoubleStream 과 Stream<Double>은 다름 ! !
IntStream intStream = IntStream.range(1, 5); // 1,2,3,4
Stream<int> X

중간 연산

중간 연산은 스트림을 변환하거나 필터링하여, 또 다른 스트림을 반환함.

중간 연산은 실제로 실행 되지 않고 최종 연산이 호출될 때 한꺼번에 실행됨.

 

필터링 - filter() ★

  • 스트림 내 요소들을 조건에 따라 걸러냄.
  • Predicate<T>는 boolean을 반환하는 람다식을 의미함.
List<String> list = Arrays.asList("apple", "banana", "avocado", "grape");

List<String> filteredList = list.stream() // Stream<String> , 최초 연산 ( 스트림 )
				.filter(s -> s.startsWith("a")) // Stream<String> , 중간연산 (1)
				.collect(Collectors.toList()); // List<String> , 최종연산 (리스트로 출력)
// 결과 : ["apple", "avocado"]

  - map()  ★

  • 스트림 내 각 요소를 다른 형태로 변환합니다.
  • Function<T, R>를 사용하여 요소를 매핑합니다.
  •  
List<String> list = Arrays.asList("apple", "banana", "orange");

List<String> mappedList = list.stream()
                              .map(String::toUpperCase) // 중간연산 
                              .collect(Collectors.toList()); // 리스트로 반환. (최종연산)

// 결과: ["APPLE", "BANANA", "ORANGE"]

정렬 - sorted() ★

  • 스트림 요소들을 정렬함.
  • 기본적으로 오름차순으로 정렬하며, 커스텀 Comparator로 정렬 방식을 변경할 수 있음.
List<Integer> numbers = Arrays.asList(3, 1, 4, 2);

List<Integer> sortedList = numbers.stream()
                                  .sorted() // 정렬  ( 중간연산 )
                                  .collect(Collectors.toList()); 리스트로 반환 ( 최종연산 )
// 결과: [1, 2, 3, 4]

중복 제거 - distinct()

List<Integer> numlist = Arrays.asList(1, 2, 2, 3, 3, 3);

List<Integer> distinctList = numlist.Stream()
				    .distinct() // 중복 제거 (중간 연산)
                    .collect(Collectors.toList()); // 리스트로 반환 (최종 연산)
// 결과 : [1, 2, 3]

 

제한/건너뛰기  

limit(n) - 스트림에서 최대 n개의 요소 까지만 추출

skip(p) - 스트림에서 처음 n개의 요소를 건너 뛰고 이후 요소만 가져옴.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

List<Integer> limited = numbers.stream()
                               .limit(3)
                               .collect(Collectors.toList());
// [1, 2, 3]

List<Integer> skipped = numbers.stream()
                               .skip(2)
                               .collect(Collectors.toList());
// [3, 4, 5]

최종 연산

최종 연산이 호출되면 스트림은 소모되며, 더 이상 재사용이 불가능함.

 

forEach()

  • 스트림의 각 요소를 소비하며, 주어진 동작을 수행함.
list.stream()
    .forEach(System.out::println);

collect() ★

  • 스트림의 요소를 List, Set, Map 등으로 모아서 반환함.
List<String> collectedList = list.stream().collect(Collectors.toList());

reduce() ★

  • 스트림의 요소를 하나씩 줄여가면서(계산) 하나의 결과를 생성.

그 외 최종연산 메소드

  • count(): 스트림의 요소 개수 반환
  • max(), min(): 최대값, 최소값 반환 (Comparator 필요)
  • anyMatch(), allMatch(), noneMatch(): 특정 조건에 부합하는지 검사 후 boolean 결과 반환
  • findFirst(), findAny(): 스트림의 첫 번째 혹은 임의 요소 반환 (Optional)

스트림의 지연 연산(Lazy Evaluation) 예시

중간 연산은 최종 연산이 일어나기 전까지 실제로 실행되지 않습니다. 이를 Lazy Evaluation(지연 연산) 이라고 함.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class LazyEvaluationExample {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("apple", "banana", "orange", "grape", "melon");

        // 중간 연산만 정의
        Stream<String> stream = list.stream()
            .filter(s -> {
                System.out.println("filtering: " + s);
                return s.startsWith("a");
            })
            .map(s -> {
                System.out.println("mapping: " + s);
                return s.toUpperCase();
            });

        System.out.println("스트림이 정의되었지만 아직 처리되지 않았습니다.");

        // 최종 연산을 호출하는 순간, 그제야 모든 중간 연산이 실행됨
        List<String> result = stream.collect(Collectors.toList());

        System.out.println("결과: " + result);
    }
}

실행 흐름

  • filter()와 map()은 중간 연산으로, 즉시 실행되지 않음
  • collect()(최종 연산)가 호출될 때 filter()와 map()이 실제 실행

 

 

※ IntStream.range() 와 IntStream.rangeClosed() 차이 ★\

System.out.println(IntStream.range(1,10).boxed().toList()); // [1, 2, 3, 4, 5, 6, 7, 8, 9]

System.out.println(IntStream.rangeClosed(1,10).boxed().toList()); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


// 인덱스 끝값이 포함되냐 안되냐의 차이임.

 

 

2. TMI - 오늘은 개인일정으로 인해 외출을 3~4시간정도 다녀와 복귀후 집중력이 좀 흐려져서 피로가 좀 쌓이고 피곤했다.

 

3. 내일 학습 할것 : 키오스크 과제 해설, 계산기 과제 피드백 자가점검 , 알고리즘

 

반응형

'[내일배움캠프-Sparta] > Spring 6기' 카테고리의 다른 글

TIL 20 [ Spring( 네트워크, 용어, HTTP, Rest API ) ]  (0) 2025.03.17
TIL 19 [ Java (스트림) , 미니세션 (클래스,객체) + 키오스크 과제 해설 ]  (0) 2025.03.14
TIL 17 [ 키오스크 과제 (도전🔥 1단계) + Intellij 깃 사용법 ]  (0) 2025.03.12
TIL 16 [ 키오스크 과제 5단계 , Java (Enum, 동일성 vs 동등성) ]  (0) 2025.03.11
TIL 15 [ 키오스크 과제(3~4단계) ]  (0) 2025.03.10
'[내일배움캠프-Sparta]/Spring 6기' 카테고리의 다른 글
  • TIL 20 [ Spring( 네트워크, 용어, HTTP, Rest API ) ]
  • TIL 19 [ Java (스트림) , 미니세션 (클래스,객체) + 키오스크 과제 해설 ]
  • TIL 17 [ 키오스크 과제 (도전🔥 1단계) + Intellij 깃 사용법 ]
  • TIL 16 [ 키오스크 과제 5단계 , Java (Enum, 동일성 vs 동등성) ]
dimenshun
dimenshun
한 소년의 개발 일기
    반응형
  • dimenshun
    Dev Life Notes
    dimenshun
  • 전체
    오늘
    어제
    • 분류 전체보기 (268)
      • CS (23)
        • 자료구조 (0)
        • 알고리즘 (0)
        • 컴퓨터 구조 (8)
        • 네트워크 (6)
        • 운영체제 (3)
        • DB ( + SQLD ) (5)
        • SW공학 (1)
      • 프로그래밍 (3)
        • Java (0)
        • Spring (0)
        • HTML,CSS (3)
        • JavaScript (0)
      • 개발 툴 (7)
        • Git(버전관리) (1)
        • Docker (3)
        • AWS (2)
        • JSP (1)
      • 코딩테스트(Algorithm) (125)
        • 백준 (6)
        • 프로그래머스 (119)
      • [내일배움캠프-Sparta] (110)
        • Spring 6기 (106)
        • KPT 회고 (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    docker
    네트워크
    Python
    Til
    KPT
    컴퓨터구조
    알고리즘
    배포
    메모리
    CPU
    개발자
    OS
    세션
    Testcode
    It
    SQL
    AWS
    network
    내일배움캠프
    spring
    코딩테스트
    트랜잭션
    백엔드
    cs
    Java
    web
    SQLD
    db
    운영체제
    웹
  • hELLO· Designed By정상우.v4.10.3
dimenshun
TIL 18 [ 키오스크 과제 (도전🔥 2단계) + Java ( 컬렉션, 스트림 ) ]
상단으로

티스토리툴바