힙에 새 배열을 만들지 않고 Java에서 배열의 세그먼트를 가져옵니다.


181

Java에서 배열의 세그먼트를 반환하는 메소드를 찾고 있습니다. 예를 들어 바이트 배열의 4 번째 및 5 번째 바이트를 포함하는 바이트 배열을 얻는 것이 있습니다. 힙 메모리에 새로운 바이트 배열을 만들고 싶지는 않습니다. 현재 다음 코드가 있습니다.

doSomethingWithTwoBytes(byte[] twoByteArray);

void someMethod(byte[] bigArray)
{
      byte[] x = {bigArray[4], bigArray[5]};
      doSomethingWithTwoBytes(x);
}

doSomething(bigArray.getSubArray(4, 2))예를 들어 4가 오프셋이고 2가 길이 인 곳을 수행하는 방법이 있는지 알고 싶습니다 .


1
C ++에서 JNI 매직을 사용하는 것은 어떻습니까? GC POV에서 재앙이 될 수 있습니까?
AlikElzin-kilaka

기본 바이트의 배열이어야합니까?
MP Korstanje

답변:


185

면책 조항 :이 답변은 질문의 제약 조건을 준수하지 않습니다 :

힙 메모리에 새로운 바이트 배열을 만들고 싶지는 않습니다.

( 솔직히, 내 대답은 삭제할 가치가 있다고 생각합니다. @ unique72의 대답은 정확합니다. Imma는이 편집 내용을 잠시 앉아서 다음 대답을 삭제합니다. )


추가 힙 할당없이 배열로 직접이 작업을 수행하는 방법을 모르지만 하위 목록 래퍼를 사용하는 다른 답변에는 래퍼에 대한 추가 할당이 있지만 배열은 아닙니다.이 경우에는 유용합니다. 큰 배열.

즉, 간결성을 찾고 있다면 유틸리티 방법 Arrays.copyOfRange()이 Java 6 (2006 년 말?)에 도입되었습니다.

byte [] a = new byte [] {0, 1, 2, 3, 4, 5, 6, 7};

// get a[4], a[5]

byte [] subArray = Arrays.copyOfRange(a, 4, 6);

10
이것은 여전히 ​​새로운 메모리 세그먼트를 동적으로 할당하고 그 범위를 복사합니다.
Dan

4
감사합니다 Dan-저는 OP가 새로운 배열을 만들고 싶지 않다는 것을 무시하고 구현을 보지 않았습니다 copyOfRange. 폐쇄 소스라면 아마도 통과했을 수 있습니다. :)
David J. Liszewski

7
많은 사람들이 배열에서 하위 배열을 만들고 싶어하고 더 많은 메모리를 사용한다고 걱정하지 않습니다. 그들은이 질문을 발견하고 그들이 원하는 대답을 얻습니다-유용하므로 삭제하지 마십시오-괜찮습니다.
외로운 코더

2
실제로, copyOfRange은 여전히 새로운 메모리 세그먼트를 할당
Kevingo 영

167

Arrays.asList(myArray)ArrayList(myArray)배열을 복사하지 않고 참조를 저장하는 new에 위임합니다 . List.subList(start, end)그 후에 사용 하면 a SubList는 원래 목록을 참조합니다 (아직도 배열을 참조합니다). 배열이나 그 내용을 복사하거나 래퍼를 만들지 않고 관련된 모든 목록은 원래 배열로 백업됩니다. (더 무겁다 고 생각했습니다.)


9
명확히하기 위해 Arrays혼란스럽게 불리는 개인 클래스에 위임 ArrayList하지만 실제로는 List배열을 둘러싼 java.util.ArrayList것입니다. (목록 내용의) 새로운 할당 및 제 3 자 종속성이 없습니다. 이것이 가장 정답이라고 생각합니다.
dimo414

28
실제로 이것은 OP가 원했던 것처럼 기본 유형 배열에는 작동하지 않습니다 ( byte[]그의 경우). 당신은 얻을 것이다 List<byte[]>. 그리고 변화 byte[] bigArray하는 것은 Byte[] bigArray상당한 메모리 오버 헤드를 부과 할 수 있습니다.
Dmitry Avtonomov

2
진정으로 원하는 것을 성취하는 유일한 방법은 sun.misc.Unsafe수업을 통하는 것 입니다.
Dmitry Avtonomov

39

포인터 스타일의 앨리어싱 접근 방식을 찾고 있다면 공간을 할당하고 데이터를 복사 할 필요가 없으므로 운이 좋지 않다고 생각합니다.

System.arraycopy() 소스에서 대상으로 복사되며이 유틸리티의 효율성이 요구됩니다. 대상 배열을 할당해야합니다.


3
예, 동적으로 메모리를 할당하고 싶지 않기 때문에 일종의 포인터 방법을 원했습니다. 하지만 내가해야 할 일인 것 같습니다.
jbu

1
@ unique72에서 알 수 있듯이 다양한 Java 목록 / 배열 유형의 구현에서 미묘한 점을 악용하여 원하는 것을 수행하는 방법이있는 것으로 보입니다. 이것은 명백한 방식이 아니라 가능할 것으로 보이며, 그것에 너무 의존하는 것을 망설이고 있습니다.
Andrew

array*copy*()같은 메모리를 재사용 해야 합니까? 발신자가 기대하는 것과 정반대입니까?
Patrick Favre

23

한 가지 방법은에서 배열을 래핑하고 java.nio.ByteBuffer절대 put / get 함수를 사용하고 하위 배열에서 작동하도록 버퍼를 슬라이스하는 것입니다.

예를 들어 :

doSomething(ByteBuffer twoBytes) {
    byte b1 = twoBytes.get(0);
    byte b2 = twoBytes.get(1);
    ...
}

void someMethod(byte[] bigArray) {
      int offset = 4;
      int length = 2;
      doSomething(ByteBuffer.wrap(bigArray, offset, length).slice());
}

참고이 모두를 호출해야한다는 wrap()하고 slice()있기 때문에, wrap()단지 상대 풋에 영향을 미치는 그 자체로 / 기능이 아닌 절대 사람을 얻는다.

ByteBuffer 이해하기가 약간 까다로울 수 있지만 대부분 효율적으로 구현되며 학습 가치가 있습니다.


1
ByteBuffer 객체는 상당히 쉽게 해독 될 수 있다는 점도 주목할 가치가있다 :StandardCharsets.UTF_8.decode(ByteBuffer.wrap(buffer, 0, readBytes))
Skeryl

@Soulman은 설명에 감사하지만 한 가지 질문은 사용하는 것보다 효율적 Arrays.copyOfRange입니까?
ucMedia

1
2 바이트 배열의 경우 @ucMedia Arrays.copyOfRange가 더 효율적일 것입니다. 일반적으로 특정 사용 사례를 측정해야합니다.
Soulman

20

java.nio.Buffer를 사용하십시오. 다양한 기본 유형의 버퍼를위한 경량 래퍼이며 슬라이싱, 위치, 변환, 바이트 순서 등을 관리하는 데 도움이됩니다.

바이트가 스트림에서 시작된 경우 NIO 버퍼는 "직접 모드"를 사용하여 기본 자원이 지원하는 버퍼를 만듭니다. 이것은 많은 경우에 성능을 향상시킬 수 있습니다.


14

ApacheUtils 에서 ArrayUtils.subarray 를 사용할 수 있습니다 . 완벽하지는 않지만 약간 직관적 인 System.arraycopy. 단점은 코드에 또 다른 의존성을 도입한다는 것입니다.


23
Java 1.6의 Arrays.copyOfRange ()와 동일
newacct

10

subList 답변이 이미 여기에 있지만 사본이 아닌 실제 하위 목록임을 보여주는 코드가 있습니다.

public class SubListTest extends TestCase {
    public void testSubarray() throws Exception {
        Integer[] array = {1, 2, 3, 4, 5};
        List<Integer> list = Arrays.asList(array);
        List<Integer> subList = list.subList(2, 4);
        assertEquals(2, subList.size());
        assertEquals((Integer) 3, subList.get(0));
        list.set(2, 7);
        assertEquals((Integer) 7, subList.get(0));
    }
}

그러나 배열로 직접 이것을 수행하는 좋은 방법이 없다고 생각합니다.


9
List.subList(int startIndex, int endIndex)

9
먼저 배열을 목록으로 래핑해야합니다. Arrays.asList (...). sublist (...);
camickr

6

하나의 옵션은 전체 배열과 시작 및 끝 인덱스를 전달하고 전달 된 전체 배열을 반복하는 대신 이들 사이를 반복하는 것입니다.

void method1(byte[] array) {
    method2(array,4,5);
}
void method2(byte[] smallarray,int start,int end) {
    for ( int i = start; i <= end; i++ ) {
        ....
    }
}

6

List들 당신이 작업을 사용할 수 있도록 subList투명하게 뭔가. 기본 배열은 어떤 종류의 오프셋-제한을 추적해야합니다. ByteBuffer들었던 것과 비슷한 옵션이 있습니다.

편집 : 유용한 메소드를 담당하는 경우 Java 자체의 많은 배열 관련 메소드에서와 같이 범위를 사용하여 정의 할 수 있습니다.

doUseful(byte[] arr, int start, int len) {
    // implementation here
}
doUseful(byte[] arr) {
    doUseful(arr, 0, arr.length);
}

그러나 배열 요소 자체에서 작업하는 경우, 예를 들어 무언가를 계산하고 결과를 다시 쓰는 것이 확실하지 않습니까?


6

Java 참조는 항상 객체를 가리 킵니다. 객체에는 무엇보다도 콘크리트 유형을 식별하는 헤더가 있습니다 (따라서 캐스트가 실패 할 수 있음 ClassCastException). 배열의 경우 객체의 시작 부분에도 길이가 포함되며 데이터는 메모리에서 바로 뒤를 따릅니다 (기술적으로 구현은 원하는대로 자유롭게 수행 할 수 있지만 다른 일을하는 것은 멍청합니다). 따라서 어딘가에 배열을 가리키는 참조를 가질 수 없습니다.

C 포인터는 어느 곳에서나 가리킬 수 있으며 배열의 가운데를 가리킬 수 있습니다. 그러나 어레이의 길이를 안전하게 캐스팅하거나 확인할 수는 없습니다. D에서 포인터는 메모리 블록과 길이에 대한 오프셋을 포함합니다 (또는 끝에 대한 포인터와 마찬가지로 구현이 실제로 무엇을하는지 기억할 수 없습니다). 이를 통해 D는 배열을 슬라이스 할 수 있습니다. C ++에서는 시작과 끝을 가리키는 두 개의 반복자가 있지만 C ++은 약간 이상합니다.

Java로 돌아 가면 할 수 없습니다. 언급했듯이 NIO를 ByteBuffer사용하면 배열을 래핑 한 다음 슬라이스 할 수 있지만 어색한 인터페이스를 제공합니다. 물론 복사 할 수도 있는데, 생각보다 훨씬 빠릅니다. String배열을 슬라이스 할 수있는 고유 한 추상화를 도입 할 수 있습니다 (현재 Sun 구현 String에는 char[]참조와 시작 오프셋 및 길이가 있고 고성능 구현에는 char[]) 만 있습니다 . byte[]저수준이지만 클래스 기반 추상화는 JDK7 (아마)까지 구문을 엉망으로 만듭니다.


불가능한 이유를 설명해 주셔서 감사합니다. Btw, String은 이제 substringHotSpot에서 복사합니다 (어떤 빌드가 이것을 변경했는지 잊어 버립니다). 왜 JDK7이 ByteBuffer보다 더 나은 구문을 허용한다고 말합니까?
Aleksandr Dubinsky

@AleksandrDubinsky 작성 당시 Java SE 7은 및과 []같은 사용자 정의 유형에 대한 배열 표기법 을 허용하는 것처럼 보였습니다 . 아직도 기다리고 ...ListByteBuffer
Tom Hawtin-tackline

2

@ unique72는 간단한 함수 또는 라인으로 응답하므로 Object를 '슬라이스'하려는 각 클래스 유형으로 바꿔야 할 수도 있습니다. 다양한 요구에 맞게 두 가지 변형이 제공됩니다.

/// Extract out array from starting position onwards
public static Object[] sliceArray( Object[] inArr, int startPos ) {
    return Arrays.asList(inArr).subList(startPos, inArr.length).toArray();
}

/// Extract out array from starting position to ending position
public static Object[] sliceArray( Object[] inArr, int startPos, int endPos ) {
    return Arrays.asList(inArr).subList(startPos, endPos).toArray();
}

1

얇은 List포장지는 어떻습니까?

List<Byte> getSubArrayList(byte[] array, int offset, int size) {
   return new AbstractList<Byte>() {
      Byte get(int index) {
         if (index < 0 || index >= size) 
           throw new IndexOutOfBoundsException();
         return array[offset+index];
      }
      int size() {
         return size;
      }
   };
}

(테스트되지 않은)


바이트의 boxing-unboxing이 발생합니다. 느려질 수 있습니다.
MP Korstanje 2019

@mpkorstanje : Orable Java 라이브러리 Byte에서 모든 byte값에 대한 객체 가 캐시됩니다. 따라서 권투 오버 헤드는 다소 느려 야합니다.
Lii

1

배열 끝까지 반복해야했고 배열을 복사하고 싶지 않았습니다. 내 접근 방식은 배열 위에 Iterable을 만드는 것입니다.

public static Iterable<String> sliceArray(final String[] array, 
                                          final int start) {
  return new Iterable<String>() {
    String[] values = array;
    int posn = start;

    @Override
    public Iterator<String> iterator() {
      return new Iterator<String>() {
        @Override
        public boolean hasNext() {
          return posn < values.length;
        }

        @Override
        public String next() {
          return values[posn++];
        }

        @Override
        public void remove() {
          throw new UnsupportedOperationException("No remove");
        }
      };
    }
  };
}

-1

이것은 Arrays.copyOfRange보다 약간 가벼우 며 범위가 없거나 음수입니다.

public static final byte[] copy(byte[] data, int pos, int length )
{
    byte[] transplant = new byte[length];

    System.arraycopy(data, pos, transplant, 0, length);

    return transplant;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.