Arrays.asList () não está funcionando como deveria?

Eu tenho um float [] e gostaria de obter uma lista com os mesmos elementos. Eu poderia fazer a coisa feia de adicioná-los um por um, mas eu queria usar o método Arrays.asList. Há um problema, no entanto. Isso funciona:

List list = Arrays.asList(1,2,3,4,5); 

Mas isso não acontece.

 int[] ints = new int[] {1,2,3,4,5}; List list = Arrays.asList(ints); 

O método asList aceita um parâmetro varargs que, para a extensão do meu conhecimento, é um “atalho” para um array.

Questões:

  • Por que a segunda parte do código retorna uma List mas não a List .

  • Existe uma maneira de corrigir isso?

  • Por que a autoboxing não funciona aqui? ie int[] para Integer[] ?

Que tal agora?

 Integer[] ints = new Integer[] {1,2,3,4,5}; List list = Arrays.asList(ints); 

Não existe uma List em Java – os genéricos não suportam primitivas.

Autoboxing só acontece para um único elemento, não para matrizes de primitivos.

Quanto à forma de corrigi-lo – existem várias bibliotecas com grande quantidade de methods para fazer coisas como esta. Não há como evitar isso, e acho que não há nada para facilitar no JDK. Alguns irão envolver um array primitivo em uma lista do tipo wrapper (para que o boxe aconteça no access), outros irão iterar através do array original para criar uma cópia independente, encheckboxndo o boxe. Certifique-se de saber qual você está usando.

(EDIT: Eu estava supondo que o ponto de partida de um int[] não era negociável. Se você pode começar com um Integer[] então você está bem longe 🙂

Apenas para um exemplo de biblioteca auxiliar e para ligar um pouco a Guava , há com.google.common.primitive.Ints.asList .

Como matrizes java são objects e Arrays.asList() trata sua matriz int como um único argumento na lista varargs.

Digite Java 8 e você pode fazer o seguinte para coletar em uma Matriz em checkbox:

 Integer[] boxedInts = IntStream.of(ints).boxed().toArray(Integer[]::new); 

Ou isso para coletar em uma lista em checkbox

 List boxedInts = IntStream.of(ints).boxed().collect(Collectors.toList()); 

No entanto, isso só funciona para int[] , long[] e double[] . Isso não funcionará para byte[] .

Note que Arrays.stream(ints) e IntStream.of(ints) são equivalentes. Portanto, dois exemplos anteriores também podem ser reescritos como:

 Integer[] boxedIntArray = Arrays.stream(ints).boxed().toArray(Integer[]::new); List boxedIntList = Arrays.stream(ints).boxed().collect(Collectors.toList()); 

Esta última forma pode ser favorecida, pois omite um subtipo primitivo específico do Stream . No entanto, internamente ainda é um monte de sobrecarregados que, neste caso, ainda criam um IntStream internamente.

O problema não é com Arrays.asList() . O problema é que você espera que o autoboxing funcione em uma matriz – e isso não acontece. No primeiro caso, o compilador autoboxing os ints individuais antes de olhar para o que eles são usados. No segundo caso, você primeiro os coloca em um array int (não é necessária nenhuma checkbox automática) e depois passa isso para Arrays.asList() (não é possível fazer o autoboxing).

Arrays.asList(T... a) efetivamente recebe um T[] que corresponderá a qualquer array de objects verdadeiros (subclasss de Object ) como um array. A única coisa que não combina assim é uma matriz de primitivos, já que tipos primitivos não derivam de Object . Portanto, um int[] não é um Object[] .

O que acontece, então, é que o mecanismo de varags entra em ação e o trata como se você tivesse passado por um único object e cria uma única matriz de elementos desse tipo. Então você passa um int[][] (aqui, T é int[] ) e termina com uma lista de 1 elemento List que não é o que você quer.

Você ainda tem algumas boas opções:

Int.asList(int[]) da Guava

Se o seu projeto já usa goiaba, é tão simples quanto usar o adaptador que o Guava fornece: Int.asList () . Existe um adaptador semelhante para cada tipo primitivo na class associada, por exemplo, Booleans para boolean , etc.

 int foo[] = {1,2,3,4,5}; Iterable fooBar = Ints.asList(foo); for(Integer i : fooBar) { System.out.println(i); } 

A vantagem dessa abordagem é que ela cria um wrapper fino em torno do array existente, portanto, a criação do wrapper é constante (não depende do tamanho do array), e o armazenamento necessário é apenas uma pequena quantidade constante ( menos de 100 bytes) além do array inteiro subjacente.

A desvantagem é que acessar cada elemento requer uma operação de boxe do int subjacente, e a configuração requer unboxing. Isso pode resultar em uma grande quantidade de alocação de memory temporária se você acessar a lista pesadamente. Se você acessar cada object muitas vezes em média, talvez seja melhor usar uma implementação que checkboxs os objects uma vez e armazena-los como Integer . A solução abaixo faz isso.

Java 8 IntStream

No Java 8, você pode usar o Arrays.stream(int[]) para transformar uma matriz int em um Stream . Dependendo do seu caso de uso, você poderá usar o stream diretamente, por exemplo, para fazer algo com cada elemento com forEach(IntConsumer) . Nesse caso, essa solução é muito rápida e não gera nenhum tipo de encaixotamento ou unboxing, e não cria nenhuma cópia do array subjacente.

Alternativamente, se você realmente precisa de um List , você pode usar stream.boxed().collect(Collectors.toList()) como sugerido aqui . A desvantagem dessa abordagem é que ela encaixota todos os elementos da lista, o que pode aumentar sua área de memory em quase uma ordem de grandeza; ela cria um novo Object[] para armazenar todos os elementos da checkbox. Se você subseqüentemente usar a lista pesadamente e precisar de objects Integer ao invés de int s, isso pode ser recompensado, mas é algo que deve estar ciente.

Se você passar um int[] para Arrays.asList() , a lista criada será List , que não é vaild em java, nem a List correta.

Eu acho que você está esperando Arrays.asList() para auto-box seus ints, que como você viu, não vai.

Não é possível converter int[] para Integer[] , você tem que copiar valores


 int[] tab = new int[]{1, 2, 3, 4, 5}; List list = ArraysHelper.asList(tab); 

 public static List asList(int[] a) { List list = new ArrayList(); for (int i = 0; i < a.length && list.add(a[i]); i++); return list; } 

Por que a autoboxing não funciona aqui? ie int [] para Integer []?

Enquanto o autoboxing irá converter um int em um Integer , ele não converterá um int[] em um Integer[] .

Por que não?

A resposta simples (mas insatisfatória) é porque é isso que o JLS diz. (Você pode verificar se quiser.)

A verdadeira resposta é fundamental para o que a autoboxing está fazendo e por que ela é segura.

Quando você autobox 1 em qualquer lugar em seu código, você obtém o mesmo object Integer . Isso não é verdade para todos os valores int (devido ao tamanho limitado do cache de checkbox automática de Integer ), mas se você usar equals para comparar objects Integer , obterá a resposta “correta”.

Basicamente N == N é sempre verdadeiro e new Integer(N).equals(new Integer(N)) é sempre verdadeiro. Além disso, essas duas coisas permanecem verdadeiras … supondo que você fique com o código Java puro.

Agora considere isto:

 int[] x = new int[]{1}; int[] y = new int[]{1}; 

São iguais? Não! x == y é falso e x.equals(y) é falso! Mas por que? Porque:

 y[0] = 2; 

Em outras palavras, duas matrizes com o mesmo tipo, tamanho e conteúdo são sempre distinguíveis porque as matrizes Java são mutáveis.

A “promise” de autoboxing é que não há problema em fazê-lo porque os resultados são indistinguíveis 1 . Mas, porque todos os arrays são fundamentalmente distinguíveis por causa da definição de equals para arrays AND array mutability. Assim, se a autoboxing de matrizes de tipos primitivos fosse permitida, isso minaria a “promise”.


1 – ….. desde que você não use == para testar se os valores de checkbox automática são iguais.