将一个原始长整型数组转换成Long类型的List

150

这可能是一个非常简单的问题,但我的第一次尝试居然完全失败了。我想把一个原始长整型数组转换成列表,我尝试用以下代码实现:

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

如何正确地做到这一点?


7
我认为对于整数我们曾经有过同样的问题,是吗? - Tom Hawtin - tackline
3
@Tom,是的:https://dev59.com/R3M_5IYBdhLWcg3wPAdF - finnw
17个回答

134

自Java 8起,您现在可以使用流来实现此操作:

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

8
好的!不幸的是,而且有些神秘的是,stream 函数只定义了 int[]long[]double[] 这几种类型。 - Norswap
2
或者,您可以使用 LongStream.of(arr).boxed()... - aioobe
3
将数组转为 List 的代码:Arrays.stream(arr).boxed().collect(Collectors.toList()); 不幸的是,这只能返回 List<Object> - Senthilkumar Annadurai
1
没有为简单任务而设计的庞大库,也没有循环。非常好的答案。 - ack
@SenthilkumarAnnadurai:不,那是不正确的,这个解决方案返回一个List<Long> - Lii

118

我发现使用Apache Commons Lang ArrayUtils (JavaDoc, Maven依赖)很方便。

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

它还具有反向API。

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

编辑: 根据评论提供完整的列表转换,以及其他修复。


4
考虑到这会创建一个 Long 数组,而不是一个列表,它并没有回答 OP 的问题,我对此投了反对票。这怎么能得到 56 个赞和令人垂涎的“检查”呢? - user949300
7
因为人们很容易就可以执行 Arrays.asList(ArrayUtils.toObject(input)) 这个操作。 - Eran Medan
1
我同意@user949300的观点。这并没有回答问题。 - dev4life
1
这个答案应该更新,以提供完整的转换为列表。然而,它是解决方案的有效步骤。 - Jim Jeffers
2
@JimJeffers - 谢谢,已更新以包括完整的转换。由于OP在他们的问题中包含了List<Long> = Arrays.asList(inputBoxed),我认为重复它是多余的,因为我认为这是显而易见的,但我错了... - Eran Medan
显示剩余5条评论

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

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

9
这个是什么以及它是否为第三方库的解释将会很有帮助。 - IgorGanapolsky

35

hallidavejpalecek 的想法是正确的——遍历数组——但他们没有利用 ArrayList 提供的一个特性: 在这种情况下,由于列表的大小是已知的,创建 ArrayList 时应该指定它。

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

这样做可以避免因数组长度不够而被 ArrayList 创建出无用的数组,也避免了因 ArrayList 过度估计空间需求而浪费空位。当然,如果你继续往列表中添加元素,就需要新的支持数组了。

1
我倾向于省略长度规范,除非代码被证明是性能热点的一部分或者数组预计非常大。我认为省略长度会使代码稍微更易读。 - hallidave

20

稍微多了点描述,但这个方法可行:

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

在你的例子中,Arrays.asList()似乎将输入解释为long[]数组的列表而不是Long类型的列表。这确实有些令人惊讶。自动装箱在这种情况下并不能按照你想要的方式工作。


如果需要在Java 8之前的旧版本中进行操作,那么这是最好的选择。 - Kozmotronik

18

作为另一个可能性,Guava库 提供了 Longs.asList() 方法,还提供了其他原始类型的类似实用程序类。

import com.google.common.primitives.Longs;

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

9
该问题询问如何将数组转换为列表。到目前为止,大多数答案都展示了如何创建一个与数组相同内容的列表,或者提到了第三方库。然而,对于这种类型的转换,有一些简单的内置选项。其中一些已经在其他答案中概述(例如this one)。但是我想在这里指出和阐述实现的某些自由度,并展示潜在的好处、缺点和注意事项。

至少需要做两个重要的区分:

  • 生成的列表是数组的视图还是新的列表
  • 生成的列表是否可以修改

这里将快速总结选项,并在本答案底部展示完整的示例程序。


创建新列表与在数组上创建视图

当结果应该是列表时,可以使用其他答案中的一种方法:

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

但是,我们也应该考虑这样做的缺点:一个包含1000000个long值的数组将占用大约8兆字节的内存。而新列表也将占用大约8兆字节的内存。当然,在创建这个列表时,整个数组必须被遍历。在许多情况下,创建一个新的列表并不是必要的。相反,只需在数组上创建一个视图即可。

// 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);

(请参见底部的示例,了解toList方法的实现)

拥有对数组的视图意味着数组中的更改将在列表中可见:

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!

幸运的是,从视图创建一个副本(即,一个列表,不受数组修改的影响)非常简单:

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

现在,这是一个真正的副本,相当于上面展示的基于流的解决方案所实现的内容。
创建一个可修改的视图或不可修改的视图
在许多情况下,列表是只读的就足够了。结果列表的内容通常不会被修改,而只会传递给下游处理程序,该处理程序仅读取列表。
允许对列表进行修改会引发一些问题:
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?

可以在可修改的数组上创建一个列表视图。这意味着列表中的更改,如在特定索引处设置新值,将在数组中可见。
但是,不可能创建一个结构上可修改的列表视图。这意味着无法执行会影响列表大小的操作。这是因为底层数组的大小无法更改。
以下是一个MCVE,展示了不同的实现选项以及使用结果列表的可能方法:
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();
    }

}

示例的输出如下所示:

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

7

使用Java 8的另一种方法。

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

7
不,原始类型数组无法自动转换为其装箱引用类型的数组。您只能进行以下操作:
long[] input = someAPI.getSomeLongs();
List<Long> lst = new ArrayList<Long>();

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

6

我正在编写一个小型库,用于解决以下问题:

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

如果您关心的话,可以在这里查看。

不错的库!起初,我不确定它是否是Java...我喜欢JQuery的风格。 - Yanick Rochon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接