Por que as escolas ensinam matrizes sobre a Lista? [fechadas]

36

A maioria das tarefas na minha escola para as aulas de programação iniciais exigiam que eu usasse matrizes. Agora trabalho em período integral e nunca usei uma matriz para nenhum projeto em que trabalhei. Mesmo nos projetos existentes, nunca vi o uso de matrizes em nenhum lugar. Na minha opinião, a Lista é mais fácil de usar e é um padrão. Por que os professores dizem aos alunos para usar matrizes em suas tarefas? É apenas para que os alunos entendam o básico?

Como a maioria das universidades ensina Java, essa pergunta é específica para Java.

Uivos Hagrid
fonte
7
Matrizes são mais fundamentais.
user253751
Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
World Engineer

Respostas:

120

Como matrizes ensinam conceitos como indexação e limites, dois conceitos fundamentalmente importantes na programação de computadores.

Listas não são um "padrão". Existe uma grande variedade de espaços problemáticos para os quais as matrizes são um ajuste perfeito.

Robert Harvey
fonte
Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
World Engineer
48

Eles provavelmente queriam começar com a estrutura de dados que representa com mais precisão o funcionamento de um computador, para que você esteja familiarizado com os fundamentos antes de começarem a apresentar abstrações de nível superior, como Listas, que facilitam o trabalho. Caso contrário, você não teria como entender por que uma certa estrutura ou algoritmo é lento / rápido para algumas operações e outras não; se a memória do computador fosse realmente feita de listas vinculadas, o mundo seria um lugar muito diferente.

Pelo mesmo motivo, minha primeira aula de programação na faculdade foi em C, e o restante das aulas foi em C ++, Java e outras linguagens. Não é que C seja de alguma forma melhor ou mais fácil, é que C (e Array) não esconde nada de você.

Ixrec
fonte
7
Uma lista é mais fácil de trabalhar em código real, mas uma matriz é mais fácil de entender (completamente) e mais importante, pois são fundamentais e universais, enquanto a Lista (do Java) não é. Pelo menos é a minha opinião.
Ixrec 11/03/2015
21
Uma atribuição na qual você armazena coisas em uma matriz e as acessa mais tarde é uma atribuição de estrutura de dados . Você apenas pode não ter percebido isso. Você deve se perguntar: o que produz melhores engenheiros, entender os modelos computacionais básicos ou aprender a biblioteca padrão de uma linguagem? O consenso geral (e eu concordo, fwiw) é que você não entenderá muito do conteúdo da biblioteca sem antes entender os conceitos básicos da computação.
Kent A.
12
LinkedList, ArrayList, OrderedList, etc. Qual lista você deve aprender primeiro? Como você apreciaria o que eles fazem por você, se você não entende por que eles existem em primeiro lugar?
Kent A.
10
+1 para @KentAnderson. As escolas devem produzir engenheiros que entendam estruturas de dados em um nível baixo; idealmente, produzindo engenheiros que possam implementar facilmente as estruturas básicas em C. Qualquer outro programa é apenas um JavaSchool.
Christian Willman
2
"Eles provavelmente queriam começar com a estrutura de dados que representa com mais precisão o funcionamento de um computador" - E os computadores com memória estruturada em lista ou estruturada em objeto? "Não é que C seja de alguma forma melhor ou mais fácil, é que C (e Array) não esconde nada de você." - A menos que seu computador não tenha ponteiros, como o AS / 400, em que C é essencialmente executado em uma VM e oculta praticamente tudo para você.
Jörg W Mittag
21

As respostas acima são ótimas, mas tenho outra em mente. O main()método de Java significa que os alunos encontram matrizes básicas muito cedo, geralmente logo no primeiro dia de aula. Por quê?

public static void main(String[] args)

É a primeira coisa com a qual você terá que lidar para escrever Hello World e além. (Vi alguns cursos usando IDEs de ensino como o BlueJ no início, o que permite apontar e clicar para executar métodos arbitrários, mas vamos deixar de lado ...) Embora possa valer a pena passar algumas das essas palavras-chave por um tempo, mais cedo ou mais tarde a maioria dos professores desejará explicá-las. De fato, uma pergunta clássica de teste no nível iniciante é pedir aos alunos que forneçam o significado de cada palavra-chave em um programa básico do Hello World. E o que encontramos como parte de nossa principal assinatura de método? Uma matriz (a razão para isso é, em parte, histórica. ArrayList não existia no Java 1.0). Matrizes fazem parte desse conjunto básico de conhecimento. Lista não é.

Dito isso, não é incomum que as classes introduzam ArrayList um pouco mais tarde no curso, especialmente depois que os objetos e seu uso são cobertos. Até o currículo de AP Computer Science para Java inclui ArrayList (eu sei que costumava, e o Google parece indicar que ainda o faz), embora ignore o fato de que ArrayList implementa List e o restante do Framework de coleções.

Finalmente, é minha experiência que programas universitários de CS usam Java como um meio de explorar conceitos de programação e CS, em vez de ensinar aos alunos como se tornarem bons desenvolvedores de Java. Alguns programas podem se concentrar mais na criação de desenvolvedores profissionais, enquanto outros se concentram mais na teoria, mas em ambos os casos, há muito a aprender sobre como usar Java no trabalho profissional real que não será ensinado na maioria dos currículos das faculdades. Isso varia de padrões e técnicas de design como os do Effective Java a estruturas como Spring, Hibernate ou JUnit, ou coisas ainda mais comuns, como JSP ou JDBC. Com essa filosofia em mente, enfatizar matrizes sobre o ArrayList mais comumente usado faz um pouco mais de sentido.

Zach Lipton
fonte
3
@Duplicator certeza. Não era relevante para o meu ponto sobre matrizes, então eu o omiti. Também não incluí System.out.println ("Hello World!"); ou.
Zach Lipton
+1 para conceitos vs. técnicas eficazes para o "mundo real". Ninguém estava ensinando controle de versão, quer, pelo menos não quando eu estava na escola - mas então eu sou antiga :)
David
Tomei uma aula de sistemas operacionais em que tivemos que implementar várias partes, como um agendador; nos concentramos em uma peça de cada vez (exclusivamente), porque naquele momento não éramos capazes de fazer mais . Mas pensar em tentar fazer com que todas essas peças funcionassem ao mesmo tempo foi o que me deu (o início de) uma apreciação das complexidades do desenvolvimento do "mundo real" / "programação em geral".
David
14

Um motivo pelo qual as aulas de programação do primeiro ano usam matrizes é o legado: foi assim que os professores a aprenderam antes de começarmos a usar bibliotecas padrão com listas dinâmicas inseridas. O uso dos tipos de dados primitivos também é mais aplicável: matrizes existem em praticamente qualquer computador idioma sob o sol (e pode ser implementado em várias instruções de montagem). Quando aprendi a programar, implementar uma lista vinculada era uma das tarefas.

É muito mais fácil começar com os primeiros princípios e depois dizer "Essa é a estrutura básica. Essa linguagem (ou suas bibliotecas) fornece essa estrutura de dados de alto nível que faz tudo isso, mas fornece x, ye z". é dizer "Então essas são as estruturas de dados de alto nível, agora aqui está o que está por trás". Aprender a raciocinar sobre o uso de um LinkedList versus um ArrayList (ou um HashSet versus um TreeSet) geralmente é um curso de Algoritmos do segundo ou terceiro ano. Listas e Mapas têm as mesmas interfaces e fornecem os mesmos resultados, mas podem ter comportamentos dramaticamente diferentes em um aplicativo de qualquer tamanho. E depois que você sai da Programação 101, não há garantia de que a Programação 102 estará usando o mesmo idioma. Se você começar do conceito de uma matriz, poderá dizer "

Outro motivo para preferir "matriz" a "Lista" em um curso introdutório é que as matrizes são fundamentalmente fáceis de entender: uma matriz de 20 bytesocupa 20 bytes (mais algumas para indicar o final da matriz ou o comprimento, dependendo da implementação )

Uma "Lista" é uma chaleira de peixe completamente diferente e pode ser implementada de várias maneiras diferentes (ArrayList, LinkedList e provavelmente algumas que eu esqueci), com características de desempenho fundamentalmente diferentes. Sem entender as entranhas do que as diferentes classes Lista está fazendo, você não pode ter uma discussão significativa sobre quando você deve usar List foo = new ArrayList()vs. List foo = new LinkedList(). Se você tentar fazer com que o aluno use uma implementação de lista, alguém perguntará por que você está usando ArrayList em vez de uma das outras implementações. E "ArrayList" inclui a palavra "Array" e é apoiada por um, portanto, não é realmente um grande salto lógico de "array" para "ArrayList".

Ao contrário da crença popular, há situações em que faz sentido usar matrizes sobre listas, principalmente quando você está lidando com uma lista de tamanho estático. Aqui estão alguns:

  • A pesquisa e a iteração são um pouco mais rápidas porque você não está lidando com a sobrecarga de chamada de método: foo[n]desreferencia e faz alguma aritmética de ponteiro nos bastidores, enquanto foo.get(n)precisa desreferenciar, fazer uma invocação de método, fazer uma segunda desreferência e, em seguida, talvez fazer aritmética de ponteiro (se você estiver usando um ArrayList; o LinkedLists potencialmente precisará iterar sobre todos os elementos da lista).
  • A inicialização é muito mais limpa: int[] foo = new int[]{1, 2, 3, 4, 5}vs. as sugestões em Outra pergunta do StackOverflow
Carl C.
fonte
Outro cenário em que as matrizes são massivamente superadas é quando a sequência na qual os elementos se tornam disponíveis corresponde à sequência na qual suas posições finais serão conhecidas, mas não corresponde à sequência final. Se permé um int[256]que possui uma permutação, pode-se invertê-lo facilmente com uma matriz: int[] inv = new int[256]; for (int i=0; i<256; i++) inv[perm[i]]=i; não acho que qualquer coisa que alguém possa escrever com um ArrayList<>seria tão limpo.
Supercat
@ supercat Isso não está realmente com um problema com matrizes dinâmicas, apenas com a implementação específica; ArrayListpoderia facilmente receber um construtor que cria uma lista inicializada por padrão de um determinado tamanho.
Doval
Você tem fontes para fazer backup da alegação de que uma lista de matriz precisa lidar com a sobrecarga de chamada de método? Em teoria, sim, mas, na prática, eu ficaria surpreso se, gete setnão for informado.
Doval
@Doval: Não há razão para que uma linguagem / estrutura bem projetada não deva fornecer um tipo de matriz dinâmica que possa adicionar convenientemente itens fora de sequência, mas o Java não fornece esse tipo.
Supercat
@Doval: Mesmo se gete setpode ser in-alinhado, algo como myList.set(23,myList.get(23)+1)será longe de ser tão eficiente quanto myArray[23]+=1. Além disso, mesmo para tipos que não precisariam de boxe, ficaria muito surpreso se algum JITter pudesse equivaler a for (i=0; i<1000; i++) myList2.set(i+2000,myList2.get(i+3000));algo cujo desempenho esteja próximo. System.arrayCopy(myArray1,3000,myArray2,2000,1000); Não há razão para o tipo de matriz dinâmica de uma estrutura não oferecer uma operação rápida de intervalo de cópias, mas Java não.
Supercat
7

Java permite que variáveis ​​de qualquer tipo sejam armazenadas em matrizes. Por outro lado, ArrayListpermite apenas o armazenamento de referências. Portanto, não é útil discutir ArrayListsem antes abordar como o boxe automático converterá primitivas em tipos de referência e como o unboxing às vezes converterá tipos de referência em primitivas:

for (int i=10; i<=10000; i*=10)
{
    ArrayList<Integer> l = new ArrayList<Integer>();
    l.add(i);
    l.add(i);
    l.add(l.get(0));
    System.out.print("i=" + i);
    System.out.print(" #0==i:" + (l.get(0)==i));
    System.out.print(" #1==i:" + (l.get(1)==i));
    System.out.print(" #2==i:" + (l.get(2)==i));
    System.out.print(" #0=#1:" + (l.get(0)==l.get(1)));
    System.out.print(" #1=#2:" + (l.get(1)==l.get(2)));
    System.out.println(" #0=#2:" + (l.get(0)==l.get(2)));
}

Se o código tivesse usado um int[3]e não um ArrayList, não haveria surpresas. Todos os três elementos seriam comparados ientre si. ArrayListNo entanto, usando todos os três elementos da lista sempre serão iguais ie o primeiro e o terceiro sempre são iguais, os dois primeiros só se igualam quando i1, 10 ou 100, mas (na maioria das implementações) não quando ifor 1000 ou 10000.

supercat
fonte
Você poderia explicar por que os números 0 e 1 serão iguais quando forem 1, 10 ou 100? Eu segui até esse ponto.
Mateus Leia
2
@MatthewRead: A Integerclasse aninhou a classe chamada IntegerCacheque contém Integerobjetos pré-inicializados para um intervalo de valores que normalmente se estende de -128 a 127; encaixar um valor fora desse intervalo exigirá a criação de um novo objeto, mas encaixar um valor dentro do intervalo em cache simplesmente retornará uma referência a um dos objetos pré-inicializados armazenados em IntegerCache.cache. Qualquer bom programador Java precisa estar ciente de que as Integervariáveis ​​de dois tipos que encapsulam o mesmo valor podem ou não ser iguais, mas a introdução dessa idéia muito cedo pode fazer os alunos fugir aterrorizados.
22715
Huh, nunca teria adivinhado que era devido a objetos pré-inicializados. Obrigado pela informação!
Mateus Leia
1
@ MatthewRead: Eu gostaria que o Java não permitisse certos tipos de conversão implícita ==. Dada a possibilidade de que o auto-unboxing possa ser lançado, eu estaria inclinado a desautorizá-lo completamente, mas seu comportamento ==é especialmente horrível. O ==operador também se comporta mal em casos como 16777217==16777216f[relatórios verdadeiros], e as conversões implícitas para long v=Math.Round(123456789), w=Math.Round(123456789012345)tendem a ser inesperado [você pode adivinhar o que essas expressões irá produzir?]
supercat
Quanto a isso IntegerCache, certamente há momentos em que ele pode ajudar no desempenho, mas frequentemente pode fazer com que um código que esteja simplesmente errado esteja meio que funcionando. De certa forma, eu gostaria que houvesse um modo em que o boxe retornasse objetos de forma quase aleatória a partir de dois caches inteiros e substituísse itens de cache quase aleatoriamente por novos, para que o código baseado no comportamento de boxe dos valores -128..127 fosse improvável que tenha sucesso por muito tempo.
22715
6

Eu acho que faz sentido ensinar como usar matrizes primeiro devido ao fato de que ArrayListusa uma matriz internamente. A ArrayListclasse tem uma variável de membro chamada elementDatawhich is a Objectarray.

No ArrayList código-fonte JDK :

/**
 * The array buffer into which the elements of the ArrayList are stored.
 * The capacity of the ArrayList is the length of this array buffer.
 */
private transient Object[] elementData;

Quando você adiciona, atualiza, recupera ou remove elementos de um ArrayListobjeto, ele usa essa matriz interna para executar essas operações. Como o usuário Ixrec já apontou - ArrayListé apenas uma abstração de nível superior que normalmente é mais fácil de trabalhar.

Derek W
fonte
Mas uma matriz usa um ponteiro para um bloco de memória internamente. Por que começar nesse nível específico de abstração?
A pergunta está marcada como Java- Ele estava perguntando por que ensinar matrizes quando normalmente você pode usar ArrayList. Java não tem ponteiros. Certo ou errado, tenho a opinião de que C / C ++ é uma linguagem melhor para começar os alunos do que Java. Muitos tópicos de programação podem ser melhor compreendidos com o conhecimento de C / C ++.
Derek W
3

Supondo que essa lista seja realmente mais fácil de trabalhar, como você diz - isso realmente não importa. Aprender é mais "básico ao complexo" do que "fácil ao difícil". Se os fundamentos não fossem importantes, a ciência da computação não seria um campo acadêmico. Você pode aprender como clicar em aplicativos juntos usando estruturas / bibliotecas existentes em tutoriais online. (Claro, alguém tem que escrever essas bibliotecas ... e alguém tem que implementar ArrayListem primeiro lugar ....)

Matthew Read
fonte
Em muitos casos, esse uso ArrayListpode ser melhor tratado usando uma matriz separada e a contagem de "liveItems", exceto que não há uma maneira conveniente de trabalhar com a matriz e contar juntos, exceto agregando-os.
22820
2

É porque a coisa mais crucial na educação acadêmica é ensiná-lo a usar a terminologia correta para descrever o que você faz.

Lista é outra coisa que matriz. e Você não pode usar java.util.Listem Java porque é uma interface. Você costuma usar o fato de java.util.ArrayListser uma implementação de Lista não uma lista, mas um invólucro de objeto em torno da matriz dinâmica. Então você diz que usa uma 'Lista', mas usa uma matriz.

É muito razoável pular esse caos terminológico e simplesmente usar matrizes para aprender aos alunos o que é matriz. Se você usa matrizes em Java, pelo menos usa matrizes.

Honestamente, também é o argumento de por que ensinar programação em Java não é uma boa idéia. É difícil aprender os conceitos básicos de programação corretamente.

Marinheiro Danubiano
fonte
e o hashmap? uma matriz é apenas um tipo de hashmap, de uma maneira ..
Thufir
@ Thufir como uma matriz pode ser um hashmap? Essas são estruturas de dados completamente diferentes.
Danubian Sailor
você pode representar uma matriz com um hashmap. o que é mais "fundamental"? Eu diria que o hashmap é mais conceitualmente interessante.
Thufir
1
@ Thufir não, você não pode. Você só pode emular. Internamente, cada estrutura de dados será o que é. Essas são coisas fundamental e conceitualmente diferentes. É por isso que é uma má idéia começar a aprender programação com linguagens altamente abstratas como Java ou JavaScript. Não está claro lá, o que realmente são as estruturas de dados.
Danubian Sailor
1

Você literalmente nunca viu ou usou matrizes? Nós os usamos o tempo todo, além de listas. Geralmente não usamos Java, mas usamos muitas outras linguagens que desenham semelhanças óbvias.

Entre matrizes e Listas, um é mais leve e, além disso, mais direto ao ponto, enquanto o outro tem mais funcionalidade. Como regra geral na programação, quando existem dois tipos semelhantes que são basicamente divididos nessas linhas, você deve optar pelo mais leve, a menos que realmente precise do mais sofisticado. Além de reduzir a sobrecarga, isso realmente ajuda a controlar a quantidade de confusão e o estado no programa e, principalmente, seu código . Se algo der errado durante o teste, você terá menos lugares para procurar; e mais importante, no caso de matrizes x listas, as pessoas têm uma idéia melhor do escopo limitado do que você está realmente usando e tentando fazer com ele.

E sim, do ponto de vista acadêmico, há o motivo adicional de ensinar aos alunos o básico. No entanto, isso vai um pouco mais fundo. Matrizes e listas são um bom exemplo de tipos mais volumosos, muitas vezes apenas sendo criados sobre, e frequentemente envolvendo, instâncias subjacentes dos tipos mais leves. Mesmo quando as listas não têm matrizes subjacentes, elas se comportam externamente como elas. Uma parte de ensinar a alguém o que é uma lista seria ensinar a eles o que é uma matriz.

Eu pude ver isso talvez ficando fora de controle em uma linguagem como C ++, onde as matrizes basicamente não poderiam ser mais reduzidas do que são, mas em linguagens de nível superior, elas são quase listas por si mesmas. Se eles atendem perfeitamente às suas necessidades em uma determinada situação, por que você precisaria usar outra coisa?

Panzercrisis
fonte
Eu não diria que uma lista tem funcionalidade "mais", tanto quanto funcionalidade "diferente". Um novo T[]estará pronto, imediatamente, para aceitar itens em qualquer ordem, enquanto um ArrayList<T>exige que os itens sejam adicionados, em ordem, antes que possam ser modificados. A T[]pode copiar um intervalo arbitrário de itens para um tamanho similar de outro T[]relativamente rápido, enquanto ArrayList<T>exigiria a leitura de itens individualmente de uma lista e o armazenamento na outra - muito mais lento e menos conveniente.
supercat