1. 개요

모든 Java 개발자는 배열 작업으로 작업 할 때 깨끗하고 효율적인 솔루션을 생성하는 것이 항상 쉬운 것은 아니라는 것을 알고 있습니다. 그럼에도 불구하고 이들은 자바 생태계의 중심 부분이며, 우리는 여러 번 처리해야 할 것입니다.

이러한 이유로 퍼즐을 신속하게 해결하는 데 도움이되는 가장 일반적인 절차의 요약 인 '치트 시트'를 갖는 것이 좋습니다. 이 예제은 이러한 상황에서 유용합니다.

2. 배열과 도우미 클래스

계속하기 전에 Java에서 배열이 무엇인지, 그리고 사용 방법을 이해하는 것이 유용합니다. Java로 처음 작업하는 경우  모든 기본 개념을 다룬 이 이전 게시물을 살펴 보는 것이 좋습니다  .

어레이가 지원하는 기본 작업은 특정 방식으로 제한됩니다. 배열과 관련하여 비교적 간단한 작업을 실행하기 위해 복잡한 알고리즘을 보는 것은 드문 일이 아닙니다.

이러한 이유로 대부분의 작업에 대해 도우미 클래스와 메서드를 사용하여 Java에서 제공 하는 Arrays 클래스와 Apache의 ArrayUtils 클래스 를 지원합니다   .

프로젝트에 후자를 포함하려면 Apache Commons 의존성 을 추가해야합니다  .

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.11</version>
</dependency>

Maven Central 에서이 아티팩트의 최신 버전을 확인할 수 있습니다 .

3. 배열의 첫 번째 요소와 마지막 요소 가져 오기

이는 배열의 인덱스 별 액세스 특성 덕분에 가장 일반적이고 간단한 작업 중 하나입니다.

모든 예제에서 사용할 int 배열을 선언하고 초기화하는 것으로 시작하겠습니다 (달리 지정하지 않는 한) :

int[] array = new int[] { 3, 5, 2, 5, 14, 4 };

배열의 첫 번째 항목이 인덱스 값 0과 연관되어 있고 사용할 수 있는 길이 속성이 있다는 것을 알면 다음 두 요소를 얻을 수있는 방법을 쉽게 알아낼 수 있습니다.

int firstItem = array[0];
int lastItem = array[array.length - 1];

4. 배열에서 랜덤의 값 얻기

java.util.Random 객체 를 사용하면 배열에서 값을 쉽게 얻을 수 있습니다.

int anyValue = array[new Random().nextInt(array.length)];

5. 배열에 새 항목 추가

아시다시피 배열은 고정 된 크기의 값을 보유합니다. 따라서 항목을 추가하고이 제한을 초과 할 수 없습니다.

새롭고 더 큰 배열을 선언하여 시작하고 기본 배열의 요소를 두 번째 배열에 복사해야합니다.

다행히도 Arrays 클래스는 배열 값을 새로운 크기의 구조로 복제하는 편리한 방법을 제공합니다.

int[] newArray = Arrays.copyOf(array, array.length + 1);
newArray[newArray.length - 1] = newItem;

선택적으로 프로젝트에서 ArrayUtils 클래스에 액세스 할 수있는 경우 add 메서드  (또는 addAll 대안)를 사용하여 한 줄 문으로 목표를 달성 할 수 있습니다.

int[] newArray = ArrayUtils.add(array, newItem);

상상할 수 있듯이이 메서드는 원래 배열 객체를 수정하지 않습니다 . 출력을 새 변수에 할당해야합니다.

6. 두 값 사이에 값 삽입

인덱싱 된 값 문자로 인해 배열에 항목을 다른 두 개 사이에 삽입하는 것은 쉬운 일이 아닙니다.

Apache는이를 일반적인 시나리오로 간주 하고 솔루션을 단순화하기 위해 ArrayUtils 클래스에 메서드를 구현했습니다 .

int[] largerArray = ArrayUtils.insert(2, array, 77);

값을 삽입 할 인덱스를 지정해야하며 출력은 더 많은 수의 요소를 포함하는 새 배열이됩니다.

마지막 인수는 가변 인수 (일명 vararg )이므로 배열에 원하는 수의 항목을 삽입 할 수 있습니다.

7. 두 어레이 비교

배열은 Object 이므로 equals 메소드를 제공 하지만 참조 동등성에만 의존하여 기본 구현을 사용합니다.

어쨌든 java.util.Arrays ' equals 메소드를 호출하여 두 배열 객체가 동일한 값을 포함하는지 확인할 수 있습니다.

boolean areEqual = Arrays.equals(array1, array2);

참고 :이 방법은 들쭉날쭉 한 배열 에는 효과적이지 않습니다 . 다차원 구조의 동등성을 확인하는 적절한 방법은 Arrays.deepEquals 입니다.

8. 어레이가 비어 있는지 확인

이것은 배열 길이 속성을 사용할 수 있다는 점을 염두에두고 복잡하지 않은 할당  입니다.

boolean isEmpty = array == null || array.length == 0;

또한 ArrayUtils 도우미 클래스에는 사용할 수 있는 null-safe 메서드도 있습니다.

boolean isEmpty = ArrayUtils.isEmpty(array);

이 함수는 여전히 데이터 구조의 길이에 따라 달라지며, null과 빈 하위 배열도 유효한 값으로 간주하므로 다음과 같은 경우에주의해야합니다.

// These are empty arrays
Integer[] array1 = {};
Integer[] array2 = null;
Integer[] array3 = new Integer[0];

// All these will NOT be considered empty
Integer[] array3 = { null, null, null };
Integer[][] array4 = { {}, {}, {} };
Integer[] array5 = new Integer[3];

9. 배열의 요소를 섞는 방법

배열의 항목을 섞기 위해 ArrayUtil 의 기능을 사용할 수 있습니다  .

ArrayUtils.shuffle(array);

이것은 void 메서드이며 배열의 실제 값에서 작동합니다.

10. 박스 및 언 박스 어레이

우리는 종종 객체 기반 배열 만 지원하는 방법을 접합니다.

다시 ArrayUtils 도우미 클래스는 기본 배열의 박스형 버전을 얻는 데 유용합니다.

Integer[] list = ArrayUtils.toObject(array);

역 연산도 가능합니다.

Integer[] objectArray = { 3, 5, 2, 5, 14, 4 };
int[] array = ArrayUtils.toPrimitive(objectArray);

11. 어레이에서 중복 제거

중복을 제거하는 가장 쉬운 방법은 배열을 Set 구현 으로 변환하는 것입니다.

아시다시피 Collection 은 Generics를 사용하므로 기본 유형을 지원하지 않습니다.

이러한 이유로, 예제 에서처럼 객체 기반 배열을 처리하지 않는 경우 먼저 값을 상자에 넣어야합니다.

// Box
Integer[] list = ArrayUtils.toObject(array);
// Remove duplicates
Set<Integer> set = new HashSet<Integer>(Arrays.asList(list));
// Create array and unbox
return ArrayUtils.toPrimitive(set.toArray(new Integer[set.size()]));

참고 : 다른 기술을 사용 하여 배열과 Set 객체 간에 변환 할 수도 있습니다  .

또한 요소의 순서를 유지해야하는 경우 LinkedHashSet 과 같은 다른 Set 구현을  사용해야합니다 .

12. 어레이 인쇄 방법

와 동일 같음의 방법, 배열의 의 toString 기능은 제공하는 기본 구현을 사용하여 개체 유용하지 않다 클래스를.

ArraysArrayUtils  클래스는 모두 데이터 구조를 읽을 수있는 String 으로 변환하는 구현과 함께 제공 됩니다.

사용하는 약간 다른 형식을 제외하고 가장 중요한 차이점은 다차원 개체를 처리하는 방법입니다.

Java Util의 클래스는 사용할 수있는 두 가지 정적 메서드를 제공합니다.

  • toString : 들쭉날쭉 한 배열에서 잘 작동하지 않습니다.
  • deepToString : 모든 객체 기반 배열을 지원 하지만 기본 배열 인수로 컴파일하지 않습니다.

반면에 Apache의 구현은 어떤 경우에도 올바르게 작동 하는 단일 toString 메서드를 제공합니다 .

String arrayAsString = ArrayUtils.toString(array);

13. 어레이를 다른 유형에 매핑

모든 배열 항목에 작업을 적용하여 다른 유형의 객체로 변환하는 것이 유용한 경우가 많습니다.

이 목표를 염두에두고 Generics를 사용하여 유연한 도우미 메서드를 만들려고합니다.

public static <T, U> U[] mapObjectArray(
  T[] array, Function<T, U> function,
  Class<U> targetClazz) {
    U[] newArray = (U[]) Array.newInstance(targetClazz, array.length);
    for (int i = 0; i < array.length; i++) {
        newArray[i] = function.apply(array[i]);
    }
    return newArray;
}

프로젝트에서 Java 8을 사용하지 않는 경우 Function 인수를 버리고  수행해야하는 각 매핑에 대한 메서드를 만들 수 있습니다.

이제 다른 작업에 일반 방법을 재사용 할 수 있습니다. 이를 설명하기 위해 두 가지 테스트 케이스를 만들어 보겠습니다.

@Test
public void whenMapArrayMultiplyingValues_thenReturnMultipliedArray() {
    Integer[] multipliedExpectedArray = new Integer[] { 6, 10, 4, 10, 28, 8 };
    Integer[] output = 
      MyHelperClass.mapObjectArray(array, value -> value * 2, Integer.class);

    assertThat(output).containsExactly(multipliedExpectedArray);
}

@Test
public void whenMapDividingObjectArray_thenReturnMultipliedArray() {
    Double[] multipliedExpectedArray = new Double[] { 1.5, 2.5, 1.0, 2.5, 7.0, 2.0 };
    Double[] output =
      MyHelperClass.mapObjectArray(array, value -> value / 2.0, Double.class);

    assertThat(output).containsExactly(multipliedExpectedArray);
}

기본 유형의 경우 값을 먼저 상자에 넣어야합니다.

대안으로 Java 8의 Streams  로 전환하여 매핑을 수행 할 수 있습니다 .

우리는에 배열을 변환해야합니다 스트림  의 객체 의 첫번째. Arrays.stream 메서드로 그렇게 할 수 있습니다 .

예를 들어 int 값을 사용자 정의 문자열 표현 에 매핑 하려면 다음을 구현합니다.

String[] stringArray = Arrays.stream(array)
  .mapToObj(value -> String.format("Value: %s", value))
  .toArray(String[]::new);

14. 배열의 필터 값

컬렉션에서 값을 필터링하는 것은 한 번 이상 수행해야하는 일반적인 작업입니다.

값을받을 배열을 만들 때 최종 크기를 확신 할 수 없기 때문입니다. 따라서 우리는 Stream 의 접근 방식에 다시 의존 할 것 입니다.

배열에서 모든 홀수를 제거한다고 상상해보십시오.

int[] evenArray = Arrays.stream(array)
  .filter(value -> value % 2 == 0)
  .toArray();

15. 기타 일반적인 어레이 작업

물론 수행해야 할 다른 어레이 작업도 많이 있습니다.

이 사용방법(예제)에 표시된 것 외에도 전용 게시물에서 다른 작업을 광범위하게 다루었습니다.

16. 결론

배열은 Java의 핵심 기능 중 하나이므로 작동 방식을 이해하고 우리가 할 수있는 것과 할 수없는 것을 아는 것이 정말 중요합니다.

이 사용방법(예제)에서는 일반적인 시나리오에서 배열 작업을 적절하게 처리하는 방법을 배웠습니다.

항상 그렇듯이 작업 예제의 전체 소스 코드는 Github 저장소 에서 사용할 수 있습니다  .