Convierta una matriz de largos primitivos en una lista de largos

138

Esta puede ser una pregunta un poco fácil, de escritorio, pero mi primer intento sorprendentemente no funcionó. Quería tomar una serie de largos primitivos y convertirlo en una lista, que intenté hacer así:

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

¿Cuál es la forma correcta de hacer esto?

Brandon Yarbrough
fuente
66
Creo que hemos tenido la misma pregunta para ints, ¿no?
Tom Hawtin - tackline

Respuestas:

115

He encontrado que es conveniente hacer uso de Apache Commons Lang ArrayUtils ( JavaDoc , Maven dependencia )

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

también tiene la API inversa

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

EDITAR: actualizado para proporcionar una conversión completa a una lista según lo sugerido por los comentarios y otras correcciones.

Eran Medan
fuente
3
Teniendo en cuenta que esto crea una serie de Longs, no una Lista , no responde la pregunta de OP y obtiene mi voto negativo. ¿Cómo diablos consiguió esto 56 votos a favor y el codiciado "cheque"?
user949300
77
Porque las personas pueden hacerlo fácilmente Arrays.asList(ArrayUtils.toObject(input)).
Eran Medan
Estoy de acuerdo con @ user949300. Esto no responde la pregunta.
dev4life
1
Esta respuesta debe actualizarse para proporcionar una conversión completa a una lista. Sin embargo, es un paso eficiente hacia la solución.
Jim Jeffers
2
@JimJeffers: gracias, actualizado para incluir la conversión completa. Dado que el OP incluido List<Long> = Arrays.asList(inputBoxed)en su pregunta, me pareció redundante repetirlo ya que pensé que era obvio, supongo que estaba equivocado ...
Eran Medan
114

Desde Java 8 ahora puede usar transmisiones para eso:

long[] arr = {1,2,3,4};
List<Long> list = Arrays.stream(arr).boxed().collect(Collectors.toList());
marcinj
fuente
77
¡Buena esa! Desafortunadamente, y algo misteriosamente, la streamfunción solo se define para int[], long[]y double[].
Norswap
1
Alternativamente, podría usar LongStream.of(arr).boxed()....
aioobe
2
Arrays.stream(arr).boxed().collect(Collectors.toList());Desafortunadamente, esto solo puede regresarList<Object>
Senthilkumar Annadurai
Sin bibliotecas voluminosas para una tarea simple, sin bucles. Gran respuesta.
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}));
Marco Pelegrini
fuente
55
Sería útil una explicación de lo que esto hace y si se trata de una biblioteca de terceros.
IgorGanapolsky
35

hallidave y jpalecek tienen la idea correcta, iterar sobre una matriz, pero no aprovechan una característica proporcionada por ArrayList: dado que el tamaño de la lista se conoce en este caso, debe especificarlo cuando cree el ArrayList.

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

De esta manera, no se crean matrices innecesarias solo para ser descartadas por el ArrayListporque resultan ser demasiado cortas, y no se desperdician "ranuras" vacías porque ArrayListsobrestiman sus requisitos de espacio. Por supuesto, si continúa agregando elementos a la lista, se necesitará una nueva matriz de respaldo.

erickson
fuente
1
Tiendo a dejar de lado la especificación de longitud, a menos que se demuestre que el código es parte de un punto crítico de rendimiento o que se espera que la matriz sea extremadamente grande. Creo que omitir la longitud hace que el código sea un poco más legible.
hallidave
19

Un poco más detallado, pero esto funciona:

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

En su ejemplo, parece que Arrays.asList () está interpretando la entrada como una lista de matrices largas [] en lugar de una lista de Longs. Un poco sorprendente, seguro. El autoboxing simplemente no funciona de la manera deseada en este caso.

hallidave
fuente
17

Como otra posibilidad, la biblioteca Guava proporciona esto como Longs.asList(), con clases de utilidad similares para los otros tipos primitivos.

import com.google.common.primitives.Longs;

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

No, no hay conversión automática de una matriz de tipo primitivo a una matriz de sus tipos de referencia en caja. Solo puedes hacer

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

for(long l : input) lst.add(l);
jpalecek
fuente
7

La pregunta se refería a cómo convertir una matriz en una lista. La mayoría de las respuestas hasta ahora mostraban cómo crear una nueva lista con el mismo contenido que la matriz o referidas a bibliotecas de terceros. Sin embargo, hay opciones simples e integradas para este tipo de conversión. Algunos de ellos ya han sido bosquejados en otras respuestas (por ejemplo, esta ). Pero me gustaría señalar y elaborar ciertos grados de libertad para la implementación aquí, y mostrar los posibles beneficios, inconvenientes y advertencias.

Hay al menos dos distinciones importantes que deben hacerse:

  • Si la lista resultante debería ser una vista en la matriz o si debería ser una nueva lista
  • Si la lista resultante debe ser modificable o no

Las opciones se resumirán aquí rápidamente, y se muestra un programa de ejemplo completo al final de esta respuesta.


Crear una nueva lista versus crear una vista en la matriz

Cuando el resultado debe ser una lista nueva , se puede utilizar uno de los enfoques de las otras respuestas:

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

Pero uno debería considerar los inconvenientes de hacer esto: una matriz con 1000000 longvalores ocupará aproximadamente 8 megabytes de memoria. La nueva lista también ocupará aproximadamente 8 megabytes. Y, por supuesto, la matriz completa debe atravesarse al crear esta lista. En muchos casos, crear una nueva lista simplemente no es necesario. En cambio, es suficiente crear una vista en la matriz:

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

(Vea el ejemplo en la parte inferior para una implementación del toListmétodo)

La implicación de tener una vista en la matriz es que los cambios en la matriz serán visibles en la lista:

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!

Afortunadamente, crear una copia (es decir, una nueva lista que no se ve afectada por las modificaciones en la matriz) desde la vista es trivial:

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

Ahora, esta es una copia verdadera, equivalente a lo que se logra con la solución basada en flujo que se mostró arriba.


Crear una vista modificable o una vista no modificable

En muchos casos, será suficiente cuando la lista sea de solo lectura . El contenido de la lista resultante a menudo no se modificará, sino que solo se pasará al procesamiento posterior que solo lee la lista.

Permitir modificaciones de la lista plantea algunas preguntas:

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?

Es posible crear una vista de lista en la matriz que sea modificable . Esto significa que los cambios en la lista, como establecer un nuevo valor en un cierto índice, serán visibles en la matriz.

Pero no es posible crear una vista de lista que sea estructuralmente modificable . Esto significa que no es posible realizar operaciones que afecten el tamaño de la lista. Esto es simplemente porque el tamaño de la matriz subyacente no se puede cambiar.


El siguiente es un MCVE que muestra las diferentes opciones de implementación y las posibles formas de usar las listas resultantes:

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

}

La salida del ejemplo se muestra aquí:

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
Marco13
fuente
6

Otra forma con Java 8.

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

Estoy escribiendo una pequeña biblioteca para estos problemas:

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

En caso de que te interese, compruébalo aquí .

dfa
fuente
bonita biblioteca! al principio, no estaba seguro de que fuera Java ... Me gusta el estilo JQuery
Yanick Rochon
4

Otra forma con Java 8.

final long[] a = new long[]{1L, 2L};
final List<Long> l = Arrays.stream(a).boxed().collect(Collectors.toList());
Jin Kwon
fuente
1
Al devolver la lista (sin asignarla), descubrí que tenía que hacer:Arrays.stream(a).boxed().collect(Collectors.<Long>toList());
Jon
1
Esto es idéntico a esta respuesta existente .
Pang
3

Combinando las respuestas de Pavel y Tom, obtenemos esto

   @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);
            }
        };
    }
Duncan McGregor
fuente
2

Si desea una semántica similar Arrays.asList, deberá escribir (o utilizar la implementación de cliente de otra persona) List(probablemente a través de) AbstractList. Debería tener una implementación muy similar a Arrays.asList, solo valores de caja y caja.

Tom Hawtin - tackline
fuente
2

Puedes usar transmorph :

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

También funciona si la fuente es una matriz de entradas, por ejemplo.

cchabanois
fuente
2

Sé que esta pregunta es lo suficientemente antigua, pero ... también puedes escribir tu propio método de conversión:

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

Después de incluirlo mediante la importación estática, los posibles usos podrían ser:

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

o

    List<Long> list = toList(1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l, 9l);
Pavel Netesa
fuente
2
con respecto a: catch (ArrayIndexOutOfBoundsException ex) { /* Finished getting array elements */ }eres una persona terrible.
Brandon Yarbrough
Hola hombre, gracias por eso! Su comentario irónico me hizo encontrar una mejor solución: obtener la longitud de la matriz a través de Array.getLength ().
Pavel Netesa
1
¡Fantástico! Me alegro de que mi actitud sardónica condujera al progreso en lugar de solo sentimientos negativos en general :) Realmente, no es una buena idea usar excepciones, excepto en condiciones muy inusuales. Son sorprendentemente caros de crear. Esta nueva versión es mucho, mucho más rápida.
Brandon Yarbrough
1

Si bien es posible crear una nueva Lista y agregarle todos los valores (a través de bucles o secuencias), he estado trabajando en matrices realmente grandes y obtengo un rendimiento deficiente. Por lo tanto, creé mi propia clase de contenedor de matriz primitiva fácil de usar.

Ejemplo:

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

Consíguelo aquí: https://github.com/Sf298/Sauds-Toolbox/blob/master/src/main/java/PrimitiveArrayWrapper/PrimitiveList.java

NOTA: Todavía no lo he probado completamente, así que avíseme si encuentra algún error / problema.

sf298
fuente
0

Puedes usar LongStreampara eso

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