본문 바로가기

배열 기반 회원가입

용량과 저장 개수를 구분하고 합계·평균·최솟값·최댓값을 계산해 배열 기반 회원가입을 완성합니다.

배열을 저장소로 쓰려면 원소를 넣는 것만으로는 부족합니다.

비어 있는 칸과 실제 회원을 구분하고, 용량을 넘지 않게 추가하며, 저장된 범위만 조회하고 집계해야 합니다.

이 절에서는 상품 관리 문제에서 자주 쓰는 고정 용량 배열 패턴을 회원가입에 적용합니다.

용량 4, size 2의 병렬 배열에서 index 0과 1은 회원이고 index 2와 3은 기본값이 남은 미사용 영역이다. 같은 합계 90의 평균 분모는 용량 4가 아니라 회원 수 2다.

LengthInsteadOfSize의 동일한 입력 상태를 사용합니다. size가 회원 집합의 경계이고 null이나 0이라는 기본값은 회원 존재를 뜻하지 않습니다.

MEMBER · STATE CONTRACT

회원의 범위와 평균 분모는 size로 결정합니다

용량4의 배열에 두 회원만 있습니다. 미사용 기본값을 출력하지 않고, 실제 회원 나이 합계90을 회원 수2로 나누면 평균45.0입니다.

length = 4 · size = 2 사용 [0, 2) / 미사용 [2, 4)

LengthInsteadOfSize · 같은 index의 email과 age가 한 회원
indexemails[index]age[index]회원 여부
0[email protected]40사용 · 실제 회원
1[email protected]50사용 · 실제 회원
2 · sizenull0미사용 · size 경계
3null0미사용 · 기본값
회원의 범위와 평균 분모는 size로 결정합니다 length4 안의 size2 사용/2빈칸 분리;같은사용40+50=90→용량분모int22/실제회원double45.0 비교;0회원정책 전체 length4 size2 사용 미사용 기본값 index 0 [email protected] / 40 index 1 [email protected] / 50 index 2 null / 0 index 3 null / 0 사용 값의 합계 40+50=90 같은 회원 집합 잘못된 용량 분모 90/4 = 22 정수몫 · 평균 아님 실제 회원 수 분모 (double)90/2 = 45.0 size2로 나눔 size0이면 나누기 전 분기 0.0은 회원 없음 표시
회원의 범위와 평균 분모는 size로 결정합니다 length4 안의 size2 사용/2빈칸 분리;같은사용40+50=90→용량분모int22/실제회원double45.0 비교;0회원정책 length=4 · 전체 용량 size=2 · 사용 [0,2) 미사용 [2,4) index 0 [email protected] age=40 index 1 [email protected] age=50 index 2 null age=0 index 3 null age=0 사용한 두 값만 합산 40+50=90 미사용 두 칸 제외 잘못된 용량 분모 90/4 = 22 정수몫 · 회원 평균 아님 실제 회원 수 분모 (double)90/2 45.0 size=0은 나누기 전 분기 0.0은 회원 없음 표시 정책
index 0 · 사용 · 실제 회원
[email protected] / age 40
index 1 · 사용 · 실제 회원
[email protected] / age 50
index 2 · 미사용 · size 경계
null / age 0
index 3 · 미사용 · 기본값
null / age 0
잘못된 분모 · 용량 4
미사용 두 칸까지 출력하면 null=0이 두 줄 나타납니다. 정수 나눗셈 90 / 4 = 22는 회원 평균이 아닙니다.
같은 회원 집합 · 개수 2
index < size만 읽으면 합계는 40 + 50 = 90, 평균은 (double) 90 / 2 = 45.0입니다.

0 ≤ size ≤ length. size == 0은 나눗셈 전에 분기하며 예제의 0.0은 회원 없음의 표시 정책이지 나이 0인 회원이 아닙니다.


length와 size 구분

lab/LengthInsteadOfSize.java
public final class LengthInsteadOfSize {
    public static void main(String[] args) {
        String[] emails = new String[4];
        int[] age = new int[4];
        int size = 0;

        emails[size] = "[email protected]";
        age[size] = 40;
        size++;

        emails[size] = "[email protected]";
        age[size] = 50;
        size++;

        int total = 0;
        for (int index = 0; index < age.length; index++) {
            total += age[index];
            System.out.println(emails[index] + "=" + age[index]);
        }
        System.out.println("average=" + total / age.length);
    }
}
잘못된 결과
[email protected]=40
[email protected]=50
null=0
null=0
average=22

예외는 발생하지 않지만 결과는 틀렸습니다.

배열의 기본값 null과 0이 존재하지 않는 두 회원처럼 출력되고, 평균의 분모도 용량 4가 되어 45가 아니라 22가 됩니다.

반복 범위와 평균 분모에는 size를 사용해야 합니다.

length는 추가 가능 여부를 판단하는 용량입니다.

if (size >= emails.length) {
    System.out.println("저장 공간이 가득 찼습니다.");
} else {
    emails[size] = "[email protected]";
    age[size] = 35;
    size++;
}

두 병렬 배열에 값을 모두 쓴 다음 마지막에 size를 증가시킵니다.

중간에 검증이 실패했는데 size부터 증가시키면 빈 회원이 생깁니다.


저장 범위 집계

src/SummaryWithSize.java
public final class SummaryWithSize {
    public static void main(String[] args) {
        int[] age = new int[5];
        int size = 3;
        age[0] = 40;
        age[1] = 50;
        age[2] = 30;

        int total = 0;
        for (int index = 0; index < size; index++) {
            total += age[index];
        }

        double average = size == 0 ? 0.0 : (double) total / size;
        System.out.println("total=" + total);
        System.out.println("average=" + average);
    }
}
total=120
average=40.0

정수끼리 나누면 소수 부분이 먼저 버려집니다.

(double) total / size처럼 피연산자 하나를 실수로 변환해야 실수 나눗셈이 됩니다.

size가 0이면 회원의 평균을 정의할 수 없으므로 표시 정책을 정해야 합니다. 자바의 실수 나눗셈에서 0.0 / 0.0은 NaN이 되며, 정수 나눗셈의 0 나누기 예외와는 다릅니다.

여기서는 회원 없음의 평균을 0.0으로 표시했지만, 화면에 회원 없음을 출력하거나 계산을 건너뛰는 편이 더 정확할 수도 있습니다.

합계가 커질 수 있는 영역에서는 int total 대신 long total을 고려합니다.

회원 나이 몇 건에서는 int가 충분하지만, 단위를 밀리초로 바꾸거나 장기간 데이터를 합치면 범위를 검토해야 합니다.

자료형 선택도 요구사항의 일부입니다.


최솟값과 최댓값 초기화

최솟값을 0으로 초기화하면 모든 회원 나이가 양수일 때 최솟값이 계속 0으로 남습니다.

0은 저장된 회원이 아니라 개발자가 만든 가짜 값입니다.

src/MemberExtremes.java
public final class MemberExtremes {
    public static void main(String[] args) {
        String[] emails = {"[email protected]", "[email protected]", "[email protected]", null};
        int[] age = {40, 55, 25, 0};
        int size = 3;

        if (size == 0) {
            System.out.println("회원 없음");
            return;
        }

        int minIndex = 0;
        int maxIndex = 0;
        for (int index = 1; index < size; index++) {
            if (age[index] < age[minIndex]) {
                minIndex = index;
            }
            if (age[index] > age[maxIndex]) {
                maxIndex = index;
            }
        }

        System.out.println("min=" + emails[minIndex] + ":" + age[minIndex]);
        System.out.println("max=" + emails[maxIndex] + ":" + age[maxIndex]);
    }
}

값만 기억하지 않고 인덱스를 기억하면 같은 위치의 email도 함께 찾을 수 있습니다.

같은 최댓값이 여러 개일 때 >는 먼저 나온 회원을 유지하고 >=는 나중 회원 정보로 교체합니다.

어느 쪽이 맞는지는 동점 정책에 따라 정합니다.

빈 배열과 회원이 0개인 배열은 다릅니다.

new int[5]는 원소 다섯 개가 있지만 size가 0이면 저장된 회원은 없습니다.

최솟값·최댓값 계산 전에 size를 검사해야 index 0 접근도 피할 수 있습니다.


회원 관리 패턴

상품 관리 예제는 보통 상품명과 가격을 병렬 배열에 넣고, 추가와 목록 출력을 반복합니다.

회원가입도 이메일과 나이를 병렬 배열로 관리하므로 구조가 같습니다.

상품 관리회원가입공통 불변식
상품명 배열이메일 배열같은 index의 문자열이 이름 역할
가격 배열회원 나이 배열같은 index의 숫자가 속성 역할
상품 수회원 수0 <= size <= length
재고 초과 방지회원 용량 초과 방지추가 전에 capacity 검사

도메인 단어는 다르지만 저장 알고리즘은 재사용할 수 있습니다.

다만 상품 가격에는 0이 허용될 수 있고 회원 나이에는 양수만 허용한다는 식으로 검증 규칙은 도메인에 맞게 달라집니다.


배열 기반 명령 흐름

다음 예제는 입력을 문자열로 고정해 추가, 목록, 요약 흐름을 재현합니다.

입력 원천을 실제 콘솔로 바꾸려면 new Scanner(System.in)을 사용합니다. 다만 이 예제는 add 뒤에 이메일과 정수 나이 두 줄이 모두 온다고 가정합니다. 입력 원천을 바꾸는 것만으로 누락된 줄이나 forty 같은 숫자 변환 실패를 복구하지는 않습니다. 직접 입력할 때도 이 형식을 지키고, 실패 후 재입력까지 지원하려면 앞의 입력 검증과 반복에서 배운 존재 여부 검사와 try/catch를 적용해야 합니다.

src/ArrayMemberRegistry.java
import java.util.Scanner;

public final class ArrayMemberRegistry {
    public static void main(String[] args) {
        Scanner scanner = new Scanner("add\n[email protected]\n40\nadd\n[email protected]\n50\nlist\nsummary\nquit\n");
        String[] emails = new String[3];
        int[] age = new int[3];
        int size = 0;
        boolean running = true;

        while (running && scanner.hasNextLine()) {
            String command = scanner.nextLine().trim();

            switch (command) {
                case "add" -> {
                    if (size >= emails.length) {
                        System.out.println("full");
                        break;
                    }

                    String email = scanner.nextLine().trim();
                    int value = Integer.parseInt(scanner.nextLine().trim());
                    if (email.isEmpty() || value < 14 || value > 120) {
                        System.out.println("invalid");
                        break;
                    }

                    emails[size] = email;
                    age[size] = value;
                    size++;
                    System.out.println("added");
                }
                case "list" -> {
                    for (int index = 0; index < size; index++) {
                        System.out.println((index + 1) + ". " + emails[index] + "=" + age[index]);
                    }
                }
                case "summary" -> {
                    int total = 0;
                    for (int index = 0; index < size; index++) {
                        total += age[index];
                    }
                    double average = size == 0 ? 0.0 : (double) total / size;
                    System.out.println("total=" + total + ", average=" + average);
                }
                case "quit" -> running = false;
                default -> System.out.println("unknown");
            }
        }
    }
}
added
added
1. [email protected]=40
2. [email protected]=50
total=90, average=45.0

용량 분기를 직접 관찰하려면 두 배열의 길이를 함께 3에서 1로 바꾸고 같은 입력을 실행해 보세요. 첫 회원은 저장되지만 둘째 add는 full을 출력합니다. 이때 이메일과 나이는 읽기 전이므로 입력에 남아 있는 [email protected]과 50이 다음 회차의 명령으로 읽혀 unknown을 각각 출력합니다. 이후 목록에는 첫 회원만 나오며 합계는 40, 평균은 40.0입니다.

용량 오류의 break는 현재 switch만 끝내며 바깥 while은 계속합니다. 따라서 고정된 입력 묶음에서 실패 뒤 남은 줄을 어떻게 처리할지와, 대화형 프로그램에서 공간을 확인한 뒤 입력을 요청할지는 서로 연결된 설계 문제입니다.

이 프로그램이 지켜야 할 핵심 불변식은 네 가지입니다.

  1. size는 0보다 작지 않고 배열 길이보다 크지 않습니다.
  2. 0부터 size - 1까지는 두 배열 모두 유효한 값이 있습니다.
  3. size부터 length - 1까지는 사용하지 않는 영역입니다.
  4. 검증을 통과한 회원만 저장하고 그때만 size를 증가시킵니다.

현재는 모든 로직이 main 안에 있어 길고, 같은 집계가 다른 메뉴에 추가되면 중복될 수 있습니다.

다음 절에서 추가·출력·집계를 메서드로 분리해 변경 이유가 다른 코드를 떼어 냅니다.


용량 초과 처리

용량이 2인 배열에 검사 없이 세 번째 회원을 넣으면 인덱스 2에서 실패합니다.

lab/CapacityOverflow.java
public final class CapacityOverflow {
    public static void main(String[] args) {
        String[] emails = new String[2];
        int size = 0;

        emails[size++] = "[email protected]";
        emails[size++] = "[email protected]";
        emails[size++] = "[email protected]";
    }
}
실패 관찰
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 2 out of bounds for length 2

예외 메시지는 문제가 생긴 인덱스 2와 배열 길이 2를 함께 보여 줍니다.

그러나 사용자 입력 때문에 프로그램이 예외로 종료되게 두기보다, 추가 전에 공간을 확인해 full이라는 도메인 결과로 바꾸는 편이 낫습니다.

추가 실패는 저장 범위를 보존하고, 두 배열 쓰기 뒤 size를 확정한다

용량 확인 전에는 상세 입력을 읽지 않고 검증 후 두 배열의 같은 칸을 쓴 다음 size를 올립니다. 저장 확정 시점과 거절 시 미소비 입력을 실제 순서로 보여 줄 수 있습니다.

ArrayMemberRegistry의 add 분기. 공간과 값을 검사한 뒤에만 emails와 age를 순차 기록하고 마지막으로 size를 늘린다. full과 invalid는 쓰기 없이 switch를 빠져나가 다음 명령 조건으로 돌아간다.

전제는 같은 용량의 두 배열, 유효한 size, 완전한 정수 입력입니다. 두 배열의 쓰기는 순차 실행이며 트랜잭션이나 예외 롤백 보장을 뜻하지 않습니다.

MEMBER · STATE CONTRACT

검사를 통과한 뒤 두 값을 쓰고 size를 늘립니다

full과 invalid는 배열을 바꾸지 않고 switch를 끝냅니다. 성공은 같은 index에 이메일·나이를 순서대로 기록한 뒤 사용 영역을 확장합니다.

ArrayMemberRegistry의 add 명령 · 같은 배열 용량 · 0 ≤ size ≤ capacity · 완전한 정수 입력 전제

검사를 통과한 뒤 두 값을 쓰고 size를 늘립니다 full은상세입력미읽기/invalid는쓰기없음,성공emails→age→size++순차;모두switch끝낸뒤while조건,동시원자성아님 size >= capacity? 공간 부족? 참 full · switch 종료 상세 입력 미읽기 거짓 이메일·정수 입력 trim / parseInt 이메일 있고 나이14–120? 거짓 invalid · switch 종료 쓰기 없이 복귀 참 emails[size] = email 첫 번째 쓰기 age[size] = value 두 번째 쓰기 size++ 사용 영역 확장 added 출력 다음 while 조건 running 및 hasNextLine()
검사를 통과한 뒤 두 값을 쓰고 size를 늘립니다 full은상세입력미읽기/invalid는쓰기없음,성공emails→age→size++순차;모두switch끝낸뒤while조건,동시원자성아님 size >= capacity? 공간이 없는가? 참 full · switch 종료 상세 입력도 읽지 않음 거짓 이메일·나이 입력 trim / parseInt 완전한 정수 입력 전제 이메일 있고 14–120 나이인가? 참 emails[size] = email age[size] = value size++ 사용 영역 확장 added 출력 거짓 invalid · switch 종료 배열·size 쓰기 없음 다음 while 조건 running 및 hasNextLine()
  1. 공간을 먼저 검사

    size >= emails.length이면 full을 출력하고 break로 switch를 끝냅니다. 상세 입력을 읽거나 배열에 쓰지 않고 4단계로 갑니다.

    공간이 남았을 때만 다음 검사를 진행합니다.

  2. 이메일·정수 나이를 읽고 값 검사

    trim()으로 이메일을 정리하고 Integer.parseInt로 나이를 변환합니다. 이메일이 비었거나 나이가14–120 밖이면 invalid 출력 후 switch를 끝내고 4단계로 갑니다.

    입력이 완전하고 나이 문자열이 정수라는 예제의 전제 아래, 통과한 값만 기록합니다.

  3. 같은 위치에 순차 기록 → 마지막에 개수 증가

    ① 이메일

    emails[size] = email;

    ② 나이

    age[size] = value;

    ③ 사용 영역 확장

    size++;

    added 출력

  4. 다음 명령의 while 조건으로

    거절과 성공 모두 running && scanner.hasNextLine()을 다시 검사합니다. 참이면 다음 명령을 읽고, 거짓이면 반복이 끝납니다.

    1·2단계의 break는 switch를 끝냅니다. 바깥 while을 직접 끝내지 않습니다.

숫자 파싱 실패와 입력 조기 종료의 복구는 이 흐름에 없습니다. 두 쓰기는 원자적 동시 작업이나 예외 롤백이 아닌 순차 실행입니다.

이 예의 조건

  • 순차 기록이며 동시 원자성을 보장하는 예제가 아닙니다.
  • 상세 입력은 모두 존재하고 나이 입력은 parseInt가 변환할 수 있는 정수 문자열이라는 전제입니다.
  • NumberFormatException과 조기 EOF를 invalid로 복구하는 코드가 없습니다. invalid는 변환 뒤 이메일·나이 값 조건 실패입니다.
관계도의 보충 설명
원 도표의 전체 관계·라벨·조건
구분내용
2full은 상세 입력 미읽기/invalid는 쓰기 없음, 성공은 emails→age→size++의 순차 실행; 모두 switch를 끝낸 뒤 while 조건으로 돌아가며 동시 원자성이 아님
10trim
11parseInt
13나이 14–120?
원 도표의 설명과 주석
구분내용
설명full은 상세 입력 미읽기/invalid는 쓰기 없음, 성공은 emails→age→size++의 순차 실행; 모두 switch를 끝낸 뒤 while 조건으로 돌아가며 동시 원자성이 아님
전체 읽기 정보추가 실패는 저장 범위를 보존하고, 두 배열 쓰기 뒤 size를 확정한다 ArrayMemberRegistry의 add 분기. 공간과 값을 검사한 뒤에만 emails와 age를 순차 기록하고 마지막으로 size를 늘린다. full과 invalid는 쓰기 없이 switch를 빠져나가 다음 명령 조건으로 돌아간다. 전제는 같은 용량의 두 배열, 유효한 size, 완전한 정수 입력입니다. 두 배열의 쓰기는 순차 실행이며 트랜잭션이나 예외 롤백 보장을 뜻하지 않습니다. MEMBER · STATE CONTRACT 검사를 통과한 뒤 두 값을 쓰고 size를 늘립니다 full과 invalid는 배열을 바꾸지 않고 switch를 끝냅니다. 성공은 같은 index에 이메일·나이를 순서대로 기록한 뒤 사용 영역을 확장합니다. ArrayMemberRegistry 의 add 명령 · 같은 배열 용량 · 0 ≤ size ≤ capacity · 완전한 정수 입력 전제 공간을 먼저 검사 size >= emails.length 이면 full 을 출력하고 break 로 switch를 끝냅니다. 상세 입력을 읽거나 배열에 쓰지 않고 4단계로 갑니다. 공간이 남았을 때만 다음 검사를 진행합니다. 이메일·정수 나이를 읽고 값 검사 trim() 으로 이메일을 정리하고 Integer.parseInt 로 나이를 변환합니다. 이메일이 비었거나 나이가 14–120 밖이면 invalid 출력 후 switch를 끝내고 4단계로 갑니다. 입력이 완전하고 나이 문자열이 정수라는 예제의 전제 아래, 통과한 값만 기록합니다. 같은 위치에 순차 기록 → 마지막에 개수 증가 ① 이메일 emails[size] = email; ② 나이 age[size] = value; ③ 사용 영역 확장 size++; added 출력 다음 명령의 while 조건으로 거절과 성공 모두 running && scanner.hasNextLine() 을 다시 검사합니다. 참이면 다음 명령을 읽고, 거짓이면 반복이 끝납니다. 1·2단계의 break는 switch를 끝냅니다. 바깥 while을 직접 끝내지 않습니다. 숫자 파싱 실패와 입력 조기 종료의 복구는 이 흐름에 없습니다. 두 쓰기는 원자적 동시 작업이나 예외 롤백이 아닌 순차 실행입니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
공간 가드

size≥capacity이면 상세 입력 없이 full입니다.

  • full: switch 종료
  • 공간 있음: email·age 읽기
  • 사용 영역 끝에 쓸 공간이 있어야 입력을 받습니다.
입력 검증

완전한 정수 입력을 trim과 parseInt로 읽은 뒤 이메일·14–120 값 조건을 검사합니다.

  • 값 조건 실패: invalid, 쓰기 없음
  • 통과: index=size 사용
  • 이메일·나이 값 조건 실패는 쓰기 없이 invalid로 복귀하며 사용 영역을 늘리지 않습니다.
기록 후 size 증가

emails[size],age[size]를 차례로 쓴 뒤 size++합니다.

  • 순서: email→age→size
  • 다음: added 후 while 조건
  • 두 칸 기록 뒤 사용 범위를 확장합니다.

추가 실패는 저장 범위를 보존하고, 두 배열 쓰기 뒤 size를 확정한다

용량 확인 전에는 상세 입력을 읽지 않고 검증 후 두 배열의 같은 칸을 쓴 다음 size를 올립니다. 저장 확정 시점과 거절 시 미소비 입력을 실제 순서로 보여 줄 수 있습니다.

ArrayMemberRegistry의 add 분기. 공간과 값을 검사한 뒤에만 emails와 age를 순차 기록하고 마지막으로 size를 늘린다. full과 invalid는 쓰기 없이 switch를 빠져나가 다음 명령 조건으로 돌아간다.

전제는 같은 용량의 두 배열, 유효한 size, 완전한 정수 입력입니다. 두 배열의 쓰기는 순차 실행이며 트랜잭션이나 예외 롤백 보장을 뜻하지 않습니다.

MEMBER · STATE CONTRACT

검사를 통과한 뒤 두 값을 쓰고 size를 늘립니다

full과 invalid는 배열을 바꾸지 않고 switch를 끝냅니다. 성공은 같은 index에 이메일·나이를 순서대로 기록한 뒤 사용 영역을 확장합니다.

ArrayMemberRegistry의 add 명령 · 같은 배열 용량 · 0 ≤ size ≤ capacity · 완전한 정수 입력 전제

검사를 통과한 뒤 두 값을 쓰고 size를 늘립니다 full은상세입력미읽기/invalid는쓰기없음,성공emails→age→size++순차;모두switch끝낸뒤while조건,동시원자성아님 size >= capacity? 공간 부족? 참 full · switch 종료 상세 입력 미읽기 거짓 이메일·정수 입력 trim / parseInt 이메일 있고 나이14–120? 거짓 invalid · switch 종료 쓰기 없이 복귀 참 emails[size] = email 첫 번째 쓰기 age[size] = value 두 번째 쓰기 size++ 사용 영역 확장 added 출력 다음 while 조건 running 및 hasNextLine()
검사를 통과한 뒤 두 값을 쓰고 size를 늘립니다 full은상세입력미읽기/invalid는쓰기없음,성공emails→age→size++순차;모두switch끝낸뒤while조건,동시원자성아님 size >= capacity? 공간이 없는가? 참 full · switch 종료 상세 입력도 읽지 않음 거짓 이메일·나이 입력 trim / parseInt 완전한 정수 입력 전제 이메일 있고 14–120 나이인가? 참 emails[size] = email age[size] = value size++ 사용 영역 확장 added 출력 거짓 invalid · switch 종료 배열·size 쓰기 없음 다음 while 조건 running 및 hasNextLine()
  1. 공간을 먼저 검사

    size >= emails.length이면 full을 출력하고 break로 switch를 끝냅니다. 상세 입력을 읽거나 배열에 쓰지 않고 4단계로 갑니다.

    공간이 남았을 때만 다음 검사를 진행합니다.

  2. 이메일·정수 나이를 읽고 값 검사

    trim()으로 이메일을 정리하고 Integer.parseInt로 나이를 변환합니다. 이메일이 비었거나 나이가14–120 밖이면 invalid 출력 후 switch를 끝내고 4단계로 갑니다.

    입력이 완전하고 나이 문자열이 정수라는 예제의 전제 아래, 통과한 값만 기록합니다.

  3. 같은 위치에 순차 기록 → 마지막에 개수 증가

    ① 이메일

    emails[size] = email;

    ② 나이

    age[size] = value;

    ③ 사용 영역 확장

    size++;

    added 출력

  4. 다음 명령의 while 조건으로

    거절과 성공 모두 running && scanner.hasNextLine()을 다시 검사합니다. 참이면 다음 명령을 읽고, 거짓이면 반복이 끝납니다.

    1·2단계의 break는 switch를 끝냅니다. 바깥 while을 직접 끝내지 않습니다.

숫자 파싱 실패와 입력 조기 종료의 복구는 이 흐름에 없습니다. 두 쓰기는 원자적 동시 작업이나 예외 롤백이 아닌 순차 실행입니다.

이 예의 조건

  • 순차 기록이며 동시 원자성을 보장하는 예제가 아닙니다.
  • 상세 입력은 모두 존재하고 나이 입력은 parseInt가 변환할 수 있는 정수 문자열이라는 전제입니다.
  • NumberFormatException과 조기 EOF를 invalid로 복구하는 코드가 없습니다. invalid는 변환 뒤 이메일·나이 값 조건 실패입니다.

관계도의 보충 설명

원 도표의 전체 관계·라벨·조건
구분내용
2full은 상세 입력 미읽기/invalid는 쓰기 없음, 성공은 emails→age→size++의 순차 실행; 모두 switch를 끝낸 뒤 while 조건으로 돌아가며 동시 원자성이 아님
10trim
11parseInt
13나이 14–120?
원 도표의 설명과 주석
구분내용
설명full은 상세 입력 미읽기/invalid는 쓰기 없음, 성공은 emails→age→size++의 순차 실행; 모두 switch를 끝낸 뒤 while 조건으로 돌아가며 동시 원자성이 아님
전체 읽기 정보추가 실패는 저장 범위를 보존하고, 두 배열 쓰기 뒤 size를 확정한다 ArrayMemberRegistry의 add 분기. 공간과 값을 검사한 뒤에만 emails와 age를 순차 기록하고 마지막으로 size를 늘린다. full과 invalid는 쓰기 없이 switch를 빠져나가 다음 명령 조건으로 돌아간다. 전제는 같은 용량의 두 배열, 유효한 size, 완전한 정수 입력입니다. 두 배열의 쓰기는 순차 실행이며 트랜잭션이나 예외 롤백 보장을 뜻하지 않습니다. MEMBER · STATE CONTRACT 검사를 통과한 뒤 두 값을 쓰고 size를 늘립니다 full과 invalid는 배열을 바꾸지 않고 switch를 끝냅니다. 성공은 같은 index에 이메일·나이를 순서대로 기록한 뒤 사용 영역을 확장합니다. ArrayMemberRegistry 의 add 명령 · 같은 배열 용량 · 0 ≤ size ≤ capacity · 완전한 정수 입력 전제 공간을 먼저 검사 size >= emails.length 이면 full 을 출력하고 break 로 switch를 끝냅니다. 상세 입력을 읽거나 배열에 쓰지 않고 4단계로 갑니다. 공간이 남았을 때만 다음 검사를 진행합니다. 이메일·정수 나이를 읽고 값 검사 trim() 으로 이메일을 정리하고 Integer.parseInt 로 나이를 변환합니다. 이메일이 비었거나 나이가 14–120 밖이면 invalid 출력 후 switch를 끝내고 4단계로 갑니다. 입력이 완전하고 나이 문자열이 정수라는 예제의 전제 아래, 통과한 값만 기록합니다. 같은 위치에 순차 기록 → 마지막에 개수 증가 ① 이메일 emails[size] = email; ② 나이 age[size] = value; ③ 사용 영역 확장 size++; added 출력 다음 명령의 while 조건으로 거절과 성공 모두 running && scanner.hasNextLine() 을 다시 검사합니다. 참이면 다음 명령을 읽고, 거짓이면 반복이 끝납니다. 1·2단계의 break는 switch를 끝냅니다. 바깥 while을 직접 끝내지 않습니다. 숫자 파싱 실패와 입력 조기 종료의 복구는 이 흐름에 없습니다. 두 쓰기는 원자적 동시 작업이나 예외 롤백이 아닌 순차 실행입니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
공간 가드

size≥capacity이면 상세 입력 없이 full입니다.

  • full: switch 종료
  • 공간 있음: email·age 읽기
  • 사용 영역 끝에 쓸 공간이 있어야 입력을 받습니다.
입력 검증

완전한 정수 입력을 trim과 parseInt로 읽은 뒤 이메일·14–120 값 조건을 검사합니다.

  • 값 조건 실패: invalid, 쓰기 없음
  • 통과: index=size 사용
  • 이메일·나이 값 조건 실패는 쓰기 없이 invalid로 복귀하며 사용 영역을 늘리지 않습니다.
기록 후 size 증가

emails[size],age[size]를 차례로 쓴 뒤 size++합니다.

  • 순서: email→age→size
  • 다음: added 후 while 조건
  • 두 칸 기록 뒤 사용 범위를 확장합니다.

연습 문제

이메일과 나이 배열, size가 주어졌을 때 30세 이상인 회원의 개수, 합계, 평균을 출력하세요.

조건을 만족하는 회원이 하나도 없으면 평균 대신 none을 출력합니다.

해설 보기
src/QualifiedSummary.java
public final class QualifiedSummary {
    public static void main(String[] args) {
        int[] age = {20, 45, 30, 60, 0};
        int size = 4;
        int count = 0;
        int total = 0;

        for (int index = 0; index < size; index++) {
            if (age[index] >= 30) {
                count++;
                total += age[index];
            }
        }

        System.out.println("count=" + count);
        System.out.println("total=" + total);
        if (count == 0) {
            System.out.println("average=none");
        } else {
            System.out.println("average=" + (double) total / count);
        }
    }
}
count=3
total=135
average=45.0

평균의 분모는 전체 size가 아니라 조건을 통과한 count입니다.

필터링과 집계를 함께 할 때 어떤 집합의 평균인지 끝까지 일치시켜야 합니다.