공공부하자개발 · 영어 학습 노트
자바
초급자바의 뼈대0/8 완료
  • 00개발 환경과 첫 프로그램
  • 01변수, 데이터 타입, 형변환
  • 02연산자와 제어문
  • 031차원 · 2차원 배열
  • 04클래스, 객체, 생성자, 오버로딩
  • 05접근 제어자와 캡슐화
  • 06메서드 활용 패턴 (초급)
  • 07enum, 패키지와 import, static 과 final
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › 초급 › 03 / 8

1차원 · 2차원 배열

섹션 7진행 0 / 8
1왜 배우는가2핵심 원리3코드 예제4응용 변형 예제5자주 하는 실수 (Tip)6연습 문제7정리‹ 이전다음 ›

4. 응용 변형 예제

배열 통계를 메서드로 나누고, 같은 일을 스트림으로 써 보고, 문자열 배열과 빈 배열을 다루며, 배열을 늘리는 두 전략의 비용을 숫자로 비교합니다.

변형 1: 통계 계산을 메서드로 분리하기

예제 1은 main 안에서 합계·최댓값·평균을 한꺼번에 구했습니다. 배열을 매개변수로 받는 작은 메서드로 나누면 다른 배열에도 그대로 쓸 수 있고, 빈 배열 처리(평균의 0 나누기)를 한 곳에서만 하면 됩니다.

java
public class ArrayStats {
    static int sum(int[] a) {
        int s = 0;
        for (int v : a) s += v;
        return s;
    }

    static int max(int[] a) {
        int m = a[0];
        for (int v : a) if (v > m) m = v;
        return m;
    }

    static double average(int[] a) {
        return a.length == 0 ? 0 : (double) sum(a) / a.length;   // 빈 배열이면 0
    }

    static int countAbove(int[] a, int threshold) {
        int n = 0;
        for (int v : a) if (v >= threshold) n++;
        return n;
    }

    public static void main(String[] args) {
        int[] scores = {78, 92, 65, 88, 95};
        int[] retake = {40, 55};
        System.out.println(sum(scores) + " " + max(scores) + " " + average(scores));   // 출력: 418 95 83.6
        System.out.println(sum(retake) + " " + max(retake) + " " + average(retake));   // 출력: 95 55 47.5
        System.out.println(countAbove(scores, 80) + " " + countAbove(retake, 80));     // 출력: 3 0
        System.out.println(average(new int[0]));                                         // 출력: 0.0
    }
}

변형 2: 반복문 → Arrays.stream 맛보기

같은 통계를 스트림으로 씁니다. 반복문보다 "무엇을 구하는지"가 코드에 그대로 드러나고, 예제 1에서 두 번 순회하던 "조건에 맞는 것만 새 배열로"가 한 줄이 됩니다. 인덱스가 필요하면 IntStream.range를 씁니다. 자세한 내용은 고급 04 레슨에서 다루므로 지금은 형태만 익히세요.

java
import java.util.Arrays;
import java.util.stream.IntStream;

public class StreamTaste {
    public static void main(String[] args) {
        int[] scores = {78, 92, 65, 88, 95};

        System.out.println(Arrays.stream(scores).sum());                          // 출력: 418
        System.out.println(Arrays.stream(scores).max().getAsInt());               // 출력: 95
        System.out.println(Arrays.stream(scores).average().getAsDouble());        // 출력: 83.6
        System.out.println(Arrays.stream(scores).filter(s -> s >= 80).count());   // 출력: 3

        int[] passed = Arrays.stream(scores).filter(s -> s >= 80).toArray();     // 조건에 맞는 것만 새 배열로
        System.out.println(Arrays.toString(passed));                              // 출력: [92, 88, 95]

        String[] names = {"김민수", "이서연", "박지훈", "최유진", "정우진"};
        IntStream.range(0, scores.length)                                         // 인덱스가 필요할 때
                 .filter(i -> scores[i] >= 90)
                 .forEach(i -> System.out.println(names[i] + " " + scores[i]));
        // 출력: 이서연 92
        // 출력: 정우진 95

        int[] sorted = Arrays.stream(scores).sorted().toArray();                  // 정렬된 복사본
        System.out.println(Arrays.toString(sorted) + " 원본 " + Arrays.toString(scores));
        // 출력: [65, 78, 88, 92, 95] 원본 [78, 92, 65, 88, 95]
    }
}

변형 3: 파일명 배열 필터링 — 실무 데이터

문자열 배열에서 조건에 맞는 것만 골라 새 배열을 만듭니다. 결과 개수를 미리 알 수 없으므로 최대 크기로 잡았다가 Arrays.copyOf로 실제 개수만큼 잘라 반환하는 것이 배열만으로 하는 표준 기법입니다. 문자열 배열도 Arrays.sort로 사전순 정렬됩니다.

java
import java.util.Arrays;

public class LogFiles {
    static String[] endingWith(String[] files, String suffix) {
        String[] tmp = new String[files.length];     // 최대 크기로 잡고
        int n = 0;
        for (String f : files) {
            if (f.endsWith(suffix)) tmp[n++] = f;
        }
        return Arrays.copyOf(tmp, n);                // 실제 개수만큼 잘라 반환
    }

    public static void main(String[] args) {
        String[] files = {"app.log", "app.log.1", "error.log", "notes.txt", "access.log"};

        String[] logs = endingWith(files, ".log");
        System.out.println(Arrays.toString(logs));                       // 출력: [app.log, error.log, access.log]
        Arrays.sort(logs);
        System.out.println(Arrays.toString(logs));                       // 출력: [access.log, app.log, error.log]
        System.out.println(Arrays.toString(endingWith(files, ".csv")));  // 출력: []

        int[] lengths = new int[files.length];                           // 배열 → 다른 타입 배열
        for (int i = 0; i < files.length; i++) lengths[i] = files[i].length();
        System.out.println(Arrays.toString(lengths));                    // 출력: [7, 9, 9, 9, 10]
    }
}

변형 4: 엣지 케이스 — null, 빈 배열, 길이 1, 범위 끝

배열을 받는 메서드는 null과 길이 0을 먼저 걸러야 합니다. 길이 1인 배열은 반복이 한 번만 돌아도 정답이 나와야 하고, copyOfRange가 범위를 넘으면 기본값으로 채워진다는 점, Arrays.equals는 null을 안전하게 처리한다는 점도 확인합니다.

java
import java.util.Arrays;

public class ArrayEdges {
    static int safeMax(int[] a) {
        if (a == null) throw new IllegalArgumentException("배열이 null");
        if (a.length == 0) throw new IllegalArgumentException("빈 배열");
        int m = a[0];
        for (int i = 1; i < a.length; i++) if (a[i] > m) m = a[i];
        return m;
    }

    public static void main(String[] args) {
        System.out.println(safeMax(new int[]{7}));               // 출력: 7
        System.out.println(safeMax(new int[]{-3, -1, -2}));      // 출력: -1
        for (int[] bad : new int[][]{null, {}}) {
            try {
                safeMax(bad);
            } catch (IllegalArgumentException e) {
                System.out.println("거부: " + e.getMessage());
            }
        }
        // 출력: 거부: 배열이 null
        // 출력: 거부: 빈 배열

        int[] a = {1, 2, 3};
        System.out.println(Arrays.toString(Arrays.copyOfRange(a, 1, 3)));   // 출력: [2, 3]
        System.out.println(Arrays.toString(Arrays.copyOfRange(a, 2, 5)));   // 출력: [3, 0, 0]  (넘치는 만큼 기본값)
        System.out.println(Arrays.toString(Arrays.copyOf(a, 0)));           // 출력: []
        System.out.println(Arrays.equals(new int[0], new int[0]));          // 출력: true
        System.out.println(Arrays.equals(null, new int[0]));                // 출력: false
        try {
            System.out.println(a[a.length]);                                // 마지막 인덱스는 length - 1
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println(e.getMessage());                             // 출력: Index 3 out of bounds for length 3
        }
    }
}

변형 5: 배열 늘리기 전략 비교 — 1칸씩 vs 2배씩

배열은 크기가 고정이라 요소를 추가하려면 새 배열로 복사해야 합니다. 매번 정확히 1칸 늘리면 복사가 n번 일어나고 옮기는 요소 수는 n²에 비례합니다. 꽉 찼을 때만 2배로 늘리면 복사는 log₂n번, 옮기는 요소는 2n 이하입니다. 대신 빈 칸이 남습니다. ArrayList가 내부적으로 후자(1.5배)를 택하는 이유입니다.

java
import java.util.Arrays;

public class GrowStrategy {
    public static void main(String[] args) {
        int n = 100_000;

        // 전략 1: 매번 1칸씩 늘림
        int[] a = new int[0];
        int copies1 = 0;
        long moved1 = 0;
        for (int i = 0; i < n; i++) {
            moved1 += a.length;                    // 이번 복사에서 옮기는 요소 수
            a = Arrays.copyOf(a, a.length + 1);
            copies1++;
            a[i] = i;
        }

        // 전략 2: 꽉 차면 2배로 늘림
        int[] b = new int[1];
        int size = 0, copies2 = 0;
        long moved2 = 0;
        for (int i = 0; i < n; i++) {
            if (size == b.length) {
                moved2 += b.length;
                b = Arrays.copyOf(b, b.length * 2);
                copies2++;
            }
            b[size++] = i;
        }

        System.out.println("1칸씩: 복사 " + copies1 + "회, 옮긴 요소 " + moved1 + "개");
        System.out.println("2배씩: 복사 " + copies2 + "회, 옮긴 요소 " + moved2 + "개, 남는 칸 " + (b.length - size));
        // 출력: 1칸씩: 복사 100000회, 옮긴 요소 4999950000개
        // 출력: 2배씩: 복사 17회, 옮긴 요소 131071개, 남는 칸 31072
    }
}
응용 변형 예제
  • 변형 1: 통계 계산을 메서드로 분리하기
  • 변형 2: 반복문 → Arrays.stream 맛보기
  • 변형 3: 파일명 배열 필터링 — 실무 데이터
  • 변형 4: 엣지 케이스 — null, 빈 배열, 길이 1, 범위 끝
  • 변형 5: 배열 늘리기 전략 비교 — 1칸씩 vs 2배씩
이전 섹션3 코드 예제4 / 7다음 섹션5 자주 하는 실수 (Tip)