Chuyển đổi một mảng dài nguyên thủy thành Danh sách dài


138

Đây có thể là một chút của một câu hỏi dễ dàng, nặng nề, nhưng nỗ lực đầu tiên của tôi hoàn toàn thất bại trong công việc. Tôi muốn lấy một mảng dài nguyên thủy và biến nó thành một danh sách, điều mà tôi đã cố gắng làm như thế này:

long[] input = someAPI.getSomeLongs();
List<Long> inputAsList = Arrays.asList(input); //Total failure to even compile!

Cách đúng đắn để làm điều này là gì?


6
Tôi nghĩ rằng chúng tôi đã có cùng một câu hỏi cho ints, phải không?
Tom Hawtin - tackline

Câu trả lời:


115

Tôi thấy thuận tiện khi sử dụng apache commons lang ArrayUtils ( JavaDoc , Maven phụ thuộc )

import org.apache.commons.lang3.ArrayUtils;
...
long[] input = someAPI.getSomeLongs();
Long[] inputBoxed = ArrayUtils.toObject(input);
List<Long> inputAsList = Arrays.asList(inputBoxed);

nó cũng có API đảo ngược

long[] backToPrimitive = ArrayUtils.toPrimitive(objectArray);

EDIT: được cập nhật để cung cấp một chuyển đổi hoàn chỉnh vào một danh sách theo đề xuất của các bình luận và các bản sửa lỗi khác.


3
Xem xét rằng điều này tạo ra một loạt Longs, không phải là Danh sách , nó không trả lời câu hỏi của OP và nhận được ý kiến ​​của tôi. Làm thế quái nào điều này có được 56 upvote và "kiểm tra" thèm muốn ???
user949300

7
Bởi vì mọi người có thể dễ dàng làm Arrays.asList(ArrayUtils.toObject(input))có thể.
Eran Medan

Tôi đồng ý với @ user949300. Điều này không trả lời câu hỏi.
dev4life

1
Câu trả lời này cần được cập nhật để cung cấp một chuyển đổi hoàn chỉnh cho một danh sách. Tuy nhiên, đó là một bước hiệu quả đối với giải pháp.
Jim Jeffers

2
@JimJeffers - cảm ơn, đã cập nhật để bao gồm chuyển đổi hoàn chỉnh. Vì OP bao gồm List<Long> = Arrays.asList(inputBoxed)trong câu hỏi của họ, tôi thấy không cần thiết phải lặp lại vì tôi nghĩ đó là điều hiển nhiên, tôi đoán rằng tôi đã sai ...
Eran Medan

114

Vì Java 8, bây giờ bạn có thể sử dụng các luồng cho điều đó:

long[] arr = {1,2,3,4};
List<Long> list = Arrays.stream(arr).boxed().collect(Collectors.toList());

7
Đẹp quá Thật không may, và hơi bí ẩn, streamchức năng chỉ được xác định cho int[], long[]double[].
Norswap

1
Ngoài ra, bạn có thể sử dụng LongStream.of(arr).boxed()....
aioobe

2
Arrays.stream(arr).boxed().collect(Collectors.toList());Thật không may, điều này chỉ có thể trở lạiList<Object>
Senthilkumar Annadurai

Không có thư viện cồng kềnh cho một nhiệm vụ đơn giản, không có vòng lặp. Câu trả lời chính xác.
Alex Quilliam

37
import java.util.Arrays;
import org.apache.commons.lang.ArrayUtils;

List<Long> longs = Arrays.asList(ArrayUtils.toObject(new long[] {1,2,3,4}));

5
Một lời giải thích về những gì nó làm và liệu đây có phải là thư viện của bên thứ ba sẽ hữu ích.
IgorGanapolsky 17/03/2017

35

hallidavejpalecek có ý tưởng đúng đắn, lặp đi lặp lại trên một mảng, nhưng họ không tận dụng một tính năng được cung cấp bởi ArrayList: vì kích thước của danh sách được biết đến trong trường hợp này, bạn nên chỉ định nó khi bạn tạo ArrayList.

List<Long> list = new ArrayList<Long>(input.length);
for (long n : input)
  list.add(n);

Bằng cách này, không có mảng không cần thiết nào được tạo ra chỉ bị loại bỏ bởi ArrayListvì chúng quá ngắn và không có "khe" trống nào bị lãng phí vì ArrayListđánh giá quá cao yêu cầu không gian của nó. Tất nhiên, nếu bạn tiếp tục thêm các yếu tố vào danh sách, sẽ cần một mảng sao lưu mới.


1
Tôi có xu hướng bỏ qua đặc tả độ dài trừ khi mã được chứng minh là một phần của điểm nóng hiệu năng hoặc mảng được dự kiến ​​là cực kỳ lớn. Tôi nghĩ rằng việc bỏ đi độ dài làm cho mã dễ đọc hơn một chút.
ảo giác

19

Một chút dài dòng hơn, nhưng điều này hoạt động:

    List<Long> list = new ArrayList<Long>();
    for (long value : input) {
        list.add(value);
    }

Trong ví dụ của bạn, có vẻ như Arrays.asList () đang diễn giải đầu vào dưới dạng danh sách các mảng [] dài thay vì danh sách Longs. Một chút ngạc nhiên, chắc chắn. Autoboxing chỉ không hoạt động theo cách bạn muốn trong trường hợp này.


17

Như một khả năng khác, thư viện Guava cung cấp điều này với Longs.asList(), với các lớp tiện ích tương tự cho các kiểu nguyên thủy khác.

import com.google.common.primitives.Longs;

long[] input = someAPI.getSomeLongs();
List<Long> output = Longs.asList(input);

7

Không, không có chuyển đổi tự động từ mảng kiểu nguyên thủy sang mảng các kiểu tham chiếu được đóng hộp của chúng. Bạn chỉ có thể làm

long[] input = someAPI.getSomeLongs();
List<Long> lst = new ArrayList<Long>();

for(long l : input) lst.add(l);

7

Câu hỏi hỏi về cách biến một mảng thành một danh sách. Hầu hết các câu trả lời cho đến nay cho thấy cách tạo một danh sách mới có cùng nội dung với mảng hoặc được đề cập đến các thư viện của bên thứ ba. Tuy nhiên, có các tùy chọn đơn giản, tích hợp sẵn cho loại chuyển đổi này. Một số trong số chúng đã được phác thảo trong các câu trả lời khác (ví dụ: câu này ). Nhưng tôi muốn chỉ ra và xây dựng một số mức độ tự do nhất định cho việc thực hiện ở đây, và cho thấy những lợi ích tiềm năng, nhược điểm và cảnh báo.

Có ít nhất hai điểm khác biệt quan trọng cần thực hiện:

  • Liệu danh sách kết quả sẽ là một khung nhìn trên mảng hay liệu nó có phải là một danh sách mới không
  • Danh sách kết quả có nên sửa đổi hay không

Các tùy chọn sẽ được tóm tắt ở đây một cách nhanh chóng và một chương trình ví dụ hoàn chỉnh được hiển thị ở dưới cùng của câu trả lời này.


Tạo một danh sách mới so với việc tạo một khung nhìn trên mảng

Khi kết quả phải là một danh sách mới , thì một trong những cách tiếp cận từ các câu trả lời khác có thể được sử dụng:

List<Long> list = Arrays.stream(array).boxed().collect(Collectors.toList());

Nhưng người ta nên xem xét những hạn chế của việc này: Một mảng có 1000000 longgiá trị sẽ chiếm khoảng 8 Megabyte bộ nhớ. Danh sách mới cũng sẽ chiếm khoảng 8 Megabyte. Và tất nhiên, toàn bộ mảng phải được duyệt qua khi tạo danh sách này. Trong nhiều trường hợp, việc tạo một danh sách mới đơn giản là không cần thiết. Thay vào đó, nó là đủ để tạo ra một khung nhìn trên mảng:

// This occupies ca. 8 MB
long array[] = { /* 1 million elements */ }

// Properly implemented, this list will only occupy a few bytes,
// and the array does NOT have to be traversed, meaning that this
// operation has nearly ZERO memory- and processing overhead:
List<Long> list = asList(array);

(Xem ví dụ ở phía dưới để biết cách thực hiện toListphương thức)

Hàm ý của việc có một cái nhìn trên mảng là những thay đổi trong mảng sẽ hiển thị trong danh sách:

long array[] = { 12, 34, 56, 78 };
List<Long> list = asList(array);

System.out.println(list.get(1)); // This will print 34

// Modify the array contents:
array[1] = 12345;

System.out.println(list.get(1)); // This will now print 12345!

May mắn thay, tạo một bản sao (nghĩa là mới danh sách không bị ảnh hưởng bởi các sửa đổi trong mảng) từ chế độ xem là không đáng kể:

List<Long> copy = new ArrayList<Long>(asList(array));

Bây giờ, đây là một bản sao thực sự, tương đương với những gì đạt được với giải pháp dựa trên luồng đã được trình bày ở trên.


Tạo chế độ xem có thể sửa đổi hoặc không thể thay đổi chế độ xem

Trong nhiều trường hợp, nó sẽ đủ khi danh sách chỉ đọc . Nội dung của danh sách kết quả thường sẽ không được sửa đổi, mà chỉ được chuyển qua xử lý xuôi dòng chỉ đọc danh sách.

Cho phép sửa đổi danh sách đặt ra một số câu hỏi:

long array[] = { 12, 34, 56, 78 };
List<Long> list = asList(array);

list.set(2, 34567);           // Should this be possible?
System.out.println(array[2]); // Should this print 34567?
list.set(3, null);            // What should happen here?
list.add(99999);              // Should this be possible?

Có thể tạo chế độ xem danh sách trên mảng có thể sửa đổi . Điều này có nghĩa là những thay đổi trong danh sách, như đặt giá trị mới tại một chỉ mục nhất định, sẽ hiển thị trong mảng.

Nhưng không thể tạo chế độ xem danh sách có thể thay đổi cấu trúc . Điều này có nghĩa là không thể thực hiện các thao tác ảnh hưởng đến kích thước của danh sách. Điều này chỉ đơn giản là vì kích thước của mảng bên dưới không thể thay đổi.


Sau đây là MCVE hiển thị các tùy chọn triển khai khác nhau và các cách có thể sử dụng danh sách kết quả:

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.RandomAccess;

public class PrimitiveArraysAsLists
{
    public static void main(String[] args)
    {
        long array[] = { 12, 34, 56, 78 };

        // Create VIEWS on the given array
        List<Long> list = asList(array);
        List<Long> unmodifiableList = asUnmodifiableList(array);

        // If a NEW list is desired (and not a VIEW on the array), this
        // can be created as well:
        List<Long> copy = new ArrayList<Long>(asList(array));

        System.out.println("array           : " + Arrays.toString(array));
        System.out.println("list            : " + list);
        System.out.println("unmodifiableList: " + unmodifiableList);
        System.out.println("copy            : " + copy);        

        // Modify a value in the array. The changes will be visible
        // in the list and the unmodifiable list, but not in
        // the copy.
        System.out.println("Changing value at index 1 of the array...");
        array[1] = 34567;

        System.out.println("array           : " + Arrays.toString(array));
        System.out.println("list            : " + list);
        System.out.println("unmodifiableList: " + unmodifiableList);
        System.out.println("copy            : " + copy);        

        // Modify a value of the list. The changes will be visible
        // in the array and the unmodifiable list, but not in
        // the copy.
        System.out.println("Changing value at index 2 of the list...");
        list.set(2, 56789L);

        System.out.println("array           : " + Arrays.toString(array));
        System.out.println("list            : " + list);
        System.out.println("unmodifiableList: " + unmodifiableList);
        System.out.println("copy            : " + copy);        


        // Certain operations are not supported:
        try
        {
            // Throws an UnsupportedOperationException: This list is 
            // unmodifiable, because the "set" method is not implemented
            unmodifiableList.set(2, 23456L);
        }
        catch (UnsupportedOperationException e) 
        {
            System.out.println("Expected: " + e);
        }

        try
        {
            // Throws an UnsupportedOperationException: The size of the
            // backing array cannot be changed
            list.add(90L);
        }
        catch (UnsupportedOperationException e) 
        {
            System.out.println("Expected: " + e);
        }


        try
        {
            // Throws a NullPointerException: The value 'null' cannot be  
            // converted to a primitive 'long' value for the underlying array
            list.set(2, null);
        }
        catch (NullPointerException e)
        {
            System.out.println("Expected: " + e);
        }

    }

    /**
     * Returns an unmodifiable view on the given array, as a list.
     * Changes in the given array will be visible in the returned
     * list.
     *  
     * @param array The array
     * @return The list view
     */
    private static List<Long> asUnmodifiableList(long array[])
    {
        Objects.requireNonNull(array);
        class ResultList extends AbstractList<Long> implements RandomAccess
        {
            @Override
            public Long get(int index)
            {
                return array[index];
            }

            @Override
            public int size()
            {
                return array.length;
            }
        };
        return new ResultList();
    }

    /**
     * Returns a view on the given array, as a list. Changes in the given 
     * array will be visible in the returned list, and vice versa. The
     * list does not allow for <i>structural modifications</i>, meaning
     * that it is not possible to change the size of the list.
     *  
     * @param array The array
     * @return The list view
     */
    private static List<Long> asList(long array[])
    {
        Objects.requireNonNull(array);
        class ResultList extends AbstractList<Long> implements RandomAccess
        {
            @Override
            public Long get(int index)
            {
                return array[index];
            }

            @Override
            public Long set(int index, Long element)
            {
                long old = array[index];
                array[index] = element;
                return old;
            }

            @Override
            public int size()
            {
                return array.length;
            }
        };
        return new ResultList();
    }

}

Đầu ra của ví dụ được hiển thị ở đây:

array           : [12, 34, 56, 78]
list            : [12, 34, 56, 78]
unmodifiableList: [12, 34, 56, 78]
copy            : [12, 34, 56, 78]
Changing value at index 1 of the array...
array           : [12, 34567, 56, 78]
list            : [12, 34567, 56, 78]
unmodifiableList: [12, 34567, 56, 78]
copy            : [12, 34, 56, 78]
Changing value at index 2 of the list...
array           : [12, 34567, 56789, 78]
list            : [12, 34567, 56789, 78]
unmodifiableList: [12, 34567, 56789, 78]
copy            : [12, 34, 56, 78]
Expected: java.lang.UnsupportedOperationException
Expected: java.lang.UnsupportedOperationException
Expected: java.lang.NullPointerException

6

Một cách khác với Java 8.

long[] input = someAPI.getSomeLongs();
LongStream.of(input).boxed().collect(Collectors.toList()));

6

Tôi đang viết một thư viện nhỏ cho những vấn đề này:

long[] input = someAPI.getSomeLongs();
List<Long> = $(input).toList();

Trong trường hợp bạn quan tâm kiểm tra nó ở đây .


thư viện tốt đẹp Lúc đầu, tôi không chắc đó là Java ... Tôi thích phong cách JQuery
Yanick Rochon

4

Một cách khác với Java 8.

final long[] a = new long[]{1L, 2L};
final List<Long> l = Arrays.stream(a).boxed().collect(Collectors.toList());

1
Khi trả lại danh sách (không gán nó), tôi thấy tôi phải làm:Arrays.stream(a).boxed().collect(Collectors.<Long>toList());
Jon

1
Điều này giống hệt với câu trả lời hiện có này .
Pang

3

Kết hợp câu trả lời của Pavel và Tom, chúng ta có được điều này

   @SuppressWarnings("unchecked")
    public static <T> List<T> asList(final Object array) {
        if (!array.getClass().isArray())
            throw new IllegalArgumentException("Not an array");
        return new AbstractList<T>() {
            @Override
            public T get(int index) {
                return (T) Array.get(array, index);
            }

            @Override
            public int size() {
                return Array.getLength(array);
            }
        };
    }

2

Nếu bạn muốn có ngữ nghĩa tương tự Arrays.asListthì bạn sẽ cần phải viết (hoặc sử dụng triển khai khách hàng của người khác) List(có thể thông qua AbstractList. Nó nên có nhiều cách thực hiện giống như Arrays.asList, chỉ có các giá trị hộp và bỏ hộp.


2

Bạn có thể sử dụng biến đổi :

Transmorph transmorph = new Transmorph(new DefaultConverters());
List<Long> = transmorph.convert(new long[] {1,2,3,4}, new TypeReference<List<Long>>() {});

Nó cũng hoạt động nếu nguồn là một mảng ints chẳng hạn.


2

Tôi biết câu hỏi này đã đủ cũ, nhưng ... bạn cũng có thể viết phương thức chuyển đổi của riêng mình:

@SuppressWarnings("unchecked")
public static <T> List<T> toList(Object... items) {

    List<T> list = new ArrayList<T>();

    if (items.length == 1 && items[0].getClass().isArray()) {
        int length = Array.getLength(items[0]);
        for (int i = 0; i < length; i++) {
            Object element = Array.get(items[0], i);
            T item = (T)element;
            list.add(item);
        }
    } else {
        for (Object i : items) {
            T item = (T)i;
            list.add(item);
        }
    }

    return list;
}

Sau khi bạn bao gồm nó bằng cách sử dụng nhập tĩnh, các cách sử dụng có thể là:

    long[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    List<Long> list = toList(array);

hoặc là

    List<Long> list = toList(1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l, 9l);

2
liên quan đến : catch (ArrayIndexOutOfBoundsException ex) { /* Finished getting array elements */ }, bạn là một người khủng khiếp.
Brandon Yarbrough

Này anh bạn, cảm ơn vì điều đó! Nhận xét trớ trêu của bạn khiến tôi tìm ra giải pháp tốt hơn - lấy độ dài của mảng thông qua Array.getL wavel ().
Pavel Netesa

1
Tuyệt diệu! Tôi rất vui vì thái độ mỉa mai của mình đã dẫn đến sự tiến bộ thay vì chỉ là những cảm giác tồi tệ chung chung :) Thực sự, không nên sử dụng ngoại lệ ngoại trừ trong những điều kiện rất bất thường. Chúng đắt đến mức đáng ngạc nhiên. Phiên bản mới này nhiều, NHIỀU nhanh hơn.
Brandon Yarbrough

1

Mặc dù có thể tạo Danh sách mới và thêm tất cả các giá trị vào nó (thông qua vòng lặp hoặc luồng), tôi đã làm việc trên các mảng thực sự lớn và có hiệu suất kém. Vì vậy, tôi đã tạo dễ dàng để sử dụng lớp trình bao bọc mảng nguyên thủy của riêng tôi.

Thí dụ:

long[] arr = new long[] {1,2,3};
PrimativeList<Long> list = PrimativeList.create(arr); // detects long[] and returns PrimativeList<Long>

System.out.println(list.get(1)); // prints: 2
list.set(2, 15);
System.out.println(arr[2]);  // prints: 15

Lấy nó ở đây: https://github.com/Sf298/Sauds-Toolbox/blob/master/src/main/java/PrimitiveArrayWrapper/PrimitiveList.java

LƯU Ý: Tôi chưa kiểm tra đầy đủ, vì vậy hãy cho tôi biết nếu bạn tìm thấy bất kỳ lỗi / vấn đề nào.


0

Bạn có thể sử dụng LongStreamcho điều đó

List<Long> longs = LongStream.of(new long[]{1L, 2L, 3L}).boxed()
                             .collect(Collectors.toList());
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.