将原语 long 数组转换为 List of Long

这可能是一个有点简单的问题,但我的第一次尝试令人惊讶地完全失败了。我想获取一个原始 long 数组,并将其转换为一个列表,我试图这样做:

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

怎么做才是正确的?

105372 次浏览

有点冗长,但是这个很管用:

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

在您的示例中,似乎 Arrays.asList ()将输入解释为 long []数组列表,而不是 Long 列表。确实有点出乎意料。在这种情况下,自动装箱就是不能按照你想要的方式工作。

不,没有从基元类型的数组自动转换为其装箱引用类型的数组。你只能做

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


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

如果您想要与 Arrays.asList类似的语义,那么您需要编写(或使用其他人的) List的客户实现(可能通过 AbstractList)。它应该具有与 Arrays.asList大致相同的实现,只有 box 和 unbox 值。

Hallidave Jpalecek有一个正确的想法 & mash; 迭代一个数组 & mash; 但是它们没有利用 ArrayList提供的一个特性: 因为在这个例子中是 名单的大小是已知的,所以在创建 ArrayList时应该指定它。

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

这样,就不会创建不必要的数组,只是因为它们太短而被 ArrayList丢弃,也不会浪费空“槽”,因为 ArrayList高估了它的空间需求。当然,如果继续向列表中添加元素,将需要一个新的支持数组。

我发现使用 apache commons lang ArrayUtils (JavaDoc专家依赖)很方便

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

编辑: 更新以提供一个完整的转换列表,如评论和其他修复所建议的。

你可以使用 变形:

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

例如,如果 source 是 int 数组,那么它也可以工作。

我正在为这些问题编写一个小型图书馆:

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

在这种情况下,你关心检查它 给你

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


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

我知道这个问题已经很老了,但是... 你也可以写出你自己的转换方法:

@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;
}

在使用静态导入包含它之后,可能的用法可以是:

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

或者

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

结合 Pavel 和 Tom 的答案,我们得到了这个

   @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);
}
};
}

从 Java8开始,您现在可以使用流来实现这一点:

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

作为另一种可能性,番石榴图书馆将其作为 Longs.asList()提供,并为其他基元类型提供类似的实用工具类。

import com.google.common.primitives.Longs;


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

另一个使用 Java8的 方式

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

Java8的另一种方式。

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

问题是如何将数组转换为列表。到目前为止,大多数答案都显示了如何创建与数组内容相同或引用第三方库的 新的列表。但是,对于这种类型的转换,有一些简单的内置选项。其中一些已经在其他答案中勾勒出来(例如 这个)。但是我想指出并详细说明这里实现的某些自由度,并展示潜在的好处、缺点和警告。

至少有两个重要的区别:

  • 结果列表是应该是数组上的 风景还是应该是 新的列表
  • 结果列表是否应该是 可修改的

这里将快速总结选项,并且在这个答案的底部显示了一个完整的示例程序。


与在数组上创建 风景相比,创建一个新列表

如果结果应该是 新的列表,那么可以使用其他答案中的一种方法:

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

但是我们应该考虑这样做的缺点: 一个具有1000000个 long值的数组将占用大约8MB 的内存。新的列表将占用大约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

虽然可以创建一个新的 List 并将所有值添加到其中(通过循环或流) ,但是我一直在处理非常大的数组,并且性能很差。因此,我创建了自己的易于使用的原始数组包装类。

例如:

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

拿过来: Https://github.com/sf298/sauds-toolbox/blob/master/src/main/java/primitivearraywrapper/primitivelist.java

注意: 我还没有完全测试它,所以如果您发现任何 bug/问题,请告诉我。

你可以使用 LongStream

List<Long> longs = LongStream.of(new long[]{1L, 2L, 3L}).boxed()
.collect(Collectors.toList());