ChatGPT解决这个技术问题 Extra ChatGPT

如何在 Java 中将 List<Integer> 转换为 int[]? [复制]

这个问题在这里已经有了答案:如何将包含整数的 ArrayList 转换为原始 int 数组? (19 个回答) 3 年前关闭。

我是 Java 新手。如何在 Java 中将 List<Integer> 转换为 int[]

我很困惑,因为 List.toArray() 实际上返回一个 Object[],它既不能转换为 Integer[] 也不能转换为 int[]

现在我正在使用循环来这样做:

int[] toIntArray(List<Integer> list) {
  int[] ret = new int[list.size()];
  for(int i = 0; i < ret.length; i++)
    ret[i] = list.get(i);
  return ret;
}

有没有更好的方法来做到这一点?

这类似于问题How can I convert int[] to Integer[] in Java?

您只能使用以下方法转换为 Integer[]:Integer[] arr = (Integer[])list.toArray(new Integer[list.size]);
@Hardcoded 您可能想要编辑您的评论以使用 list.size() 方法并删除不必要的演员表。
现在在 Java 8 中有没有更好的方法来做到这一点?
(@Makoto:见 Pshemo's answer
int[] arr = listOfIntegers.stream().mapToInt(x->x).toArray();

P
Pshemo

在 Java 8 中添加流后,我们可以编写如下代码:

int[] example1 = list.stream().mapToInt(i->i).toArray();
// OR
int[] example2 = list.stream().mapToInt(Integer::intValue).toArray();

思考过程:

简单的 Stream#toArray 返回一个 Object[] 数组,所以它不是我们想要的。此外, Stream#toArray(IntFunction generator) 没有做我们想要的,因为泛型类型 A 不能表示原始类型 int

所以最好有一些流可以处理原始类型 int 而不是包装 Integer,因为它的 toArray 方法很可能还会返回一个 int[] 数组(返回像 Object[] 甚至盒装的 Integer[]在这里会很不自然)。幸运的是,Java 8 有这样一个流,即 IntStream

所以现在我们唯一需要弄清楚的是如何将我们的 Stream(将从 list.stream() 返回)转换为闪亮的 IntStream。在寻找返回 IntStream 的方法时快速搜索 Stream 的文档将我们指向我们的解决方案,即 mapToInt(ToIntFunction mapper) 方法。我们需要做的就是提供从 Integer 到 int 的映射。由于 ToIntFunction 是函数式接口,我们可以通过 lambda 或方法引用来提供它的实例。无论如何要将 Integer 转换为 int 我们可以使用 Integer#intValue 所以在 mapToInt 中我们可以写: mapToInt( (Integer i) -> i.intValue() ) (或者有些人可能更喜欢: mapToInt(Integer::intValue)。)但类似可以使用拆箱生成代码,因为编译器知道此 lambda 的结果必须是 int 类型(在 mapToInt 中使用的 lambda 是 ToIntFunction 接口的实现,它期望作为主体的类型方法:int applyAsInt(T value)预计将返回一个 int)。所以我们可以简单地写成: mapToInt((Integer i)->i) 另外,由于 (Integer i) 中的 Integer 类型可以由编译器推断,因为 List#stream() 返回一个 Stream,我们也可以跳过它,这给我们留下了 mapToInt(i -> i)


显然是最好的解决方案。太糟糕了,它缺乏解释。
@PimpTrizkit 稍微更新了这个答案。希望现在更清楚了。
@Pshemo - 谢谢!我个人不需要解释。但我讨厌看到没有一个完美的答案!无论如何,您的解释确实教育了我并且很有帮助。我想知道为什么 mapTo... 函数不允许 null lambda .... 像 sort 那样....这使其默认为默认行为...在这种情况下,i -> i 将是一个完美的选择默认行为。
我想这个答案比 ColinD 使用番石榴的答案要快,对吧?
@vefthym 我没有测试它,但我怀疑这两种解决方案的工作原理相同,所以我希望速度相似(但可以随意对其进行基准测试)。这个答案的一个优点是,只要我们有 Java 8,它就不需要额外的库。
J
Jon Skeet

不幸的是,由于 Java 处理原始类型、装箱、数组和泛型的性质,我不相信真的有更好的方法来做到这一点。尤其是:

List.toArray 不起作用,因为没有从 Integer 到 int 的转换

您不能将 int 用作泛型的类型参数,因此它必须是特定于 int 的方法(或使用反射来进行讨厌的诡计的方法)。

我相信有些库对所有原始类型都有这种方法的自动生成版本(即,有一个为每种类型复制的模板)。这很丑陋,但恐怕就是这样:(

即使 Arrays 类在泛型到达 Java 之前就已经出现,但如果它在今天被引入,它仍然必须包含所有可怕的重载(假设您想使用原始数组)。


另请参阅 ColinD 关于番石榴的 Ints.toArray(Collection) 的回答
@JonSkeet,您的意思是像 binarySearchcopyOfcopyOfRange 的 Arrays 类中已经存在的可怕重载......?我想知道为什么他们不能添加另一组可怕的重载。
@ron ColinD 的答案除了 OP 已经拥有的东西之外没有给出任何东西 - for 循环用非原始数组填充原始数组。
与此同时,在 Oracle 的 Java 工程师专注于使用模块使语言过于复杂......
@bvdb:我是说没有其他库(基本上仍然有循环,但在他们的代码中不是你的),我不相信有更好的方法。这比说我不知道是否有更好的方法要强得多。
d
dimo414

除了 Commons Lang,您还可以使用 Guava 的方法 Ints.toArray(Collection<Integer> collection) 执行此操作:

List<Integer> list = ...
int[] ints = Ints.toArray(list);

这使您不必进行 Commons Lang 等效项需要您自己进行的中间数组转换。


不幸的是,中间数组隐藏在 Guava 中:Object[] boxedArray = collection.toArray();
“还好,中间阵法隐藏在番石榴内部。” - 为你解决了这个问题。 ;)
A
Aaron Digulla

最简单的方法是使用 Apache Commons Lang。它有一个方便的 ArrayUtils 类,可以做你想做的事。将 toPrimitive 方法与 Integer 数组的重载一起使用。

List<Integer> myList;
 ... assign and fill the list
int[] intArray = ArrayUtils.toPrimitive(myList.toArray(new Integer[myList.size()]));

这样你就不会重新发明轮子。 Commons Lang 有很多 Java 遗漏的有用的东西。上面,我选择创建一个大小合适的整数列表。您还可以使用长度为 0 的静态 Integer 数组并让 Java 分配正确大小的数组:

static final Integer[] NO_INTS = new Integer[0];
   ....
int[] intArray2 = ArrayUtils.toPrimitive(myList.toArray(NO_INTS));

toPrimitive 链接已损坏。
这是 2.6 Commons Lang API 的链接:toPrimitive
请注意,这将需要 2 个分配和副本:myList.toArray() 将创建一个 Integer[] 并填充它,而 ArrayUtils.toPrimitive() 将分配一个 int[] 并将输入拆箱。
D
Devon_C_Miller

Java 8 为我们提供了一种通过流实现此目的的简单方法......

使用集合 stream() 函数,然后映射到整数,您将获得一个 IntStream。使用 IntStream 我们可以调用 toArray() 给我们 int []

int [] ints = list.stream().mapToInt(Integer::intValue).toArray();

to int []

to IntStream


P
Peter Mortensen

利用:

int[] toIntArray(List<Integer> list)  {
    int[] ret = new int[list.size()];
    int i = 0;
    for (Integer e : list)
        ret[i++] = e;
    return ret;
}

对您的代码进行细微的更改是为了避免昂贵的列表索引(因为列表不一定是 ArrayList,但它可能是一个链表,随机访问的成本很高)。


我不明白:在数组中查找元素并不慢。它在 O 中运行,其中 n 是数组的大小,即它完全不依赖于数组的大小或任何东西。在 C 中: myarray[c] 与: myarray + c * sizeof(myarray[0] ) ... 运行速度非常快。
该方法以 List 作为参数,并非所有实现都具有快速随机访问(如 ArrayList)
@Hugh:区别不在于如何访问数组,而在于如何访问 List。 list.get(i) 为每次访问执行边界检查和填充。我不知道新的解决方案是否真的更好,但至少迈克是这么说的,也许是这样。编辑:我忘记了Java允许索引到一个链表(我习惯了C++的std::list,它不允许它)。所以 arjantop 关于非 ArrayList 的说法也是正确的;即使没有边界检查,索引也不一定很快。
@Lajnold 在现代 JIT 中,ArrayList 的边界检查是免费的。但是,如果 Java 更遵循 STL 并且只实现真正有意义的方法,我更愿意(LinkedList::get(int) 不这样做,因为它可能会很慢)。
@maaartinus 接口的全部目的是将实现与接口分开。如果您了解基本思想,那么在 Java 中解决它的方式就很有意义。 Here 是有关它的一些信息。
P
Peter Mortensen

这是一个 Java 8 单行代码:

public int[] toIntArray(List<Integer> intList){
    return intList.stream().mapToInt(Integer::intValue).toArray();
}

M
Mr. Polywhirl

如果您只是将 Integer 映射到 int,那么您应该考虑 using parallelism,因为您的映射逻辑不依赖于其范围之外的任何变量。

int[] arr = list.parallelStream().mapToInt(Integer::intValue).toArray();

请注意这一点

请注意,并行性并不会自动比串行执行操作更快,尽管如果您有足够的数据和处理器内核,它可能会更快。虽然聚合操作使您能够更轻松地实现并行性,但您仍然有责任确定您的应用程序是否适合并行性。

有两种方法可以将整数映射到它们的原始形式:

通过 ToIntFunction。 mapToInt(Integer::intValue) 通过使用 lambda 表达式显式拆箱。 mapToInt(i -> i.intValue()) 通过 lambda 表达式的隐式(自动)拆箱。 mapToInt(i -> i)

给定一个具有 null 值的列表

List<Integer> list = Arrays.asList(1, 2, null, 4, 5);

以下是处理 null 的三个选项:

在映射之前过滤掉空值。 int[] arr = list.parallelStream().filter(Objects::nonNull).mapToInt(Integer::intValue).toArray();将空值映射到默认值。 int[] arr = list.parallelStream().map(i -> i == null ? -1 : i).mapToInt(Integer::intValue).toArray();在 lambda 表达式中处理 null。 int[] arr = list.parallelStream().mapToInt(i -> i == null ? -1 : i.intValue()).toArray();


请注意,mapToInt(Integer::intValue)mapToInt(i -> i.intValue()) 严格相同(表达完全相同的方法调用的两种方式),并且所有三个实际上相同(相同的字节码)。
这个太棒了,谢谢分享。!
T
THANN Phearum

这个简单的循环总是正确的!没有错误

  int[] integers = new int[myList.size()];
  for (int i = 0; i < integers.length; i++) {
      integers[i] = myList.get(i);
  }

“有效”不等于“理想”,性能问题可以认为是bug。正如您可能假设的那样,List#get(int) 不能保证是一个恒定时间的操作,因此它不应该用于迭代。相反,请使用专为此用例设计的迭代器。使用 Java 5+ foreach 循环或调用 List#iterator() 并使用迭代器。此外,列表的大小可能会在此循环期间发生变化,从而导致 IndexOutOfBoundsException 或不完整的数组。许多迭代器实现都有详细记录的策略来处理这种情况。
比 Java 流的狗的晚餐要好得多
这与问题中的相同。问题是“有没有更好的方法来做到这一点?”。这如何回答这个问题?大多数其他答案都试图回答这个问题。
P
Peter Mortensen

我注意到 for 循环的几种用途,但您甚至不需要循环内的任何东西。我提到这一点只是因为最初的问题是试图找到不那么冗长的代码。

int[] toArray(List<Integer> list) {
    int[] ret = new int[ list.size() ];
    int i = 0;
    for( Iterator<Integer> it = list.iterator();
         it.hasNext();
         ret[i++] = it.next() );
    return ret;
}

如果 Java 允许像 C++ 那样在 for 循环中声明多个声明,我们可以更进一步,执行 for(int i = 0, Iterator it...

最后(这部分只是我的观点),如果你打算有一个帮助功能或方法为你做某事,只需设置它并忘记它。它可以是单行或十行;如果您再也不会看它,您将不会知道其中的区别。


P
Peter Mortensen

真的没有办法“单线”你正在尝试做的事情,因为 toArray 返回一个 Object[] 并且你不能从 Object[] 转换为 int[] 或 Integer[] 转换为 int[]。


由于数组协方差,您可以在 Object[] 和 Integer[] 之间进行转换 - 但不能在 int[] 和 Integer[] 之间进行转换。
感谢您纠正我,我将编辑我的答案以反映您所说的。
r
robd
int[] ret = new int[list.size()];       
Iterator<Integer> iter = list.iterator();
for (int i=0; iter.hasNext(); i++) {       
    ret[i] = iter.next();                
}                                        
return ret;                              

一个解释将是有序的。想法/要点是什么?
P
Peter Mortensen

也可以试试 Dollar (check this revision):

import static com.humaorie.dollar.Dollar.*
...

List<Integer> source = ...;
int[] ints = $(source).convert().toIntArray();

D
Donald Raab

使用 Eclipse Collections,如果您有类型为 java.util.List<Integer> 的列表,您可以执行以下操作:

List<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int[] ints = LazyIterate.adapt(integers).collectInt(i -> i).toArray();

Assert.assertArrayEquals(new int[]{1, 2, 3, 4, 5}, ints);

如果您已经拥有像 MutableList 这样的 Eclipse Collections 类型,则可以执行以下操作:

MutableList<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int[] ints = integers.asLazy().collectInt(i -> i).toArray();

Assert.assertArrayEquals(new int[]{1, 2, 3, 4, 5}, ints);

注意:我是 Eclipse Collections 的提交者


P
Peter Mortensen

我建议您使用 Java 集合 API 中的 List<?> 骨架实现。在这种特殊情况下,它似乎很有帮助:

package mypackage;

import java.util.AbstractList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Test {

    // Helper method to convert int arrays into Lists
    static List<Integer> intArrayAsList(final int[] a) {
        if(a == null)
            throw new NullPointerException();
        return new AbstractList<Integer>() {

            @Override
            public Integer get(int i) {
                return a[i]; // Autoboxing
            }
            @Override
            public Integer set(int i, Integer val) {
                final int old = a[i];
                a[i] = val; // Auto-unboxing
                return old; // Autoboxing
            }
            @Override
            public int size() {
                return a.length;
            }
        };
    }

    public static void main(final String[] args) {
        int[] a = {1, 2, 3, 4, 5};
        Collections.reverse(intArrayAsList(a));
        System.out.println(Arrays.toString(a));
    }
}

当心装箱/拆箱的缺点。


这并没有回答 OP 的问题——OP 询问了如何从 List 转换为数组。
P
Peter Mortensen

使用 lambda 你可以做到这一点(在 JDK lambda 中编译):

public static void main(String ars[]) {
    TransformService transformService = (inputs) -> {
        int[] ints = new int[inputs.size()];
        int i = 0;
        for (Integer element : inputs) {
            ints[ i++ ] = element;
        }
        return ints;
    };

    List<Integer> inputs = new ArrayList<Integer>(5) { {add(10); add(10);} };

    int[] results = transformService.transform(inputs);
}

public interface TransformService {
    int[] transform(List<Integer> inputs);
}

当被问到这个问题时,你不可能这样做,但这是现在处理这种情况的好方法(假设你已经升级了 Java)。您所做的可以进一步修改,以提供一种通用方法来使用相同的方法转换许多事物。 +1
这里的解决方案仅在循环代码中。其余的(功能接口、lambda、main 方法、带有虚拟数据的列表)是无关紧要的,无助于回答问题。