Чому школи викладають масиви над списком? [зачинено]


36

Більшість завдань у моїй школі для початкових класів програмування вимагали від мене використання масивів. Зараз я працюю повний робочий день, і ніколи не використовував масив для жодного проекту, над яким працював. Навіть у існуючих проектах я ніде не бачив використання масивів. На мою думку, Список простіший у використанні і є стандартом. Чому професори кажуть студентам використовувати масиви у своїх завданнях? Це просто так, щоб учні розуміли основи?

Оскільки більшість університетів викладають Java, це питання є специфічним для Java.


7
Масиви є більш фундаментальними.
користувач253751

Коментарі не для розширеного обговорення; ця розмова переміщена до чату .
Світовий інженер

Відповіді:


120

Оскільки масиви викладають такі поняття, як індексація та межі, два принципово важливих поняття в комп'ютерному програмуванні.

Списки не є "стандартом". Існує велика кількість проблемних просторів, для яких масиви ідеально підходять.


Коментарі не для розширеного обговорення; ця розмова переміщена до чату .
Світовий інженер

48

Вони, напевно, хотіли почати зі структури даних, яка найбільш точно відображає, як працює комп'ютер, тому ви ознайомилися з основами, перш ніж почати вводити абстракції вищого рівня, як Списки, які полегшують роботу. Інакше у вас би не було можливості зрозуміти, чому певна структура чи алгоритм є повільним / швидким для деяких операцій, а не для інших; якби пам'ять комп’ютера насправді була складена з пов'язаних списків, світ був би зовсім іншим місцем.

Приблизно з тієї ж причини мій перший клас програмування в коледжі був у С, а решта класів усі були на C ++, Java та інших мовах. Справа не в тому, що C якось краще чи простіше, це те, що C (і масив) нічого від вас не приховують.


7
З цим списком простіше працювати з реальним кодом, але масив легше (повністю) зрозуміти, а ще важливіше зрозуміти, оскільки вони фундаментальні та універсальні, тоді як (список Java) це не так. Принаймні, це моя думка.
Іксрек

21
Завдання, в якому ви зберігаєте речі в масиві та отримуєте доступ до них пізніше, є призначенням даних . Ви просто не могли цього усвідомити. Ви повинні запитати себе, що дає кращі інженери, розуміючи основні обчислювальні моделі чи вивчаючи стандартну бібліотеку мови? Загальний консенсус (і я згоден, fwiw) полягає в тому, що ви не зрозумієте багато вмісту бібліотеки без попереднього розуміння основ обчислень.
Кент А.

12
LinkedList, ArrayList, OrрадList тощо. З якого списку ви повинні дізнатися спочатку? Як би ви оцінили те, що вони роблять для вас, якщо ви не розумієте, чому вони існують?
Кент А.

10
+1 для @KentAnderson Школи повинні виробляти інженерів, які розуміють структури даних на низькому рівні; в ідеалі виробляють інженерів, які легко реалізують основні структури в C. Будь-яка інша програма - це лише JavaSchool.
Крістіан Вілман

2
"Вони, ймовірно, хотіли почати зі структури даних, яка найбільш точно відображає, як працює комп'ютер" - Отже, що з комп'ютерами зі списком або об'єктно-структурованою пам'яттю? "Справа не в тому, що C якось краще чи простіше, це те, що C (і масив) нічого від тебе не приховують". - Якщо на вашому комп’ютері немає вказівників, як AS / 400, де C по суті працює в VM і приховує майже все для вас.
Йорг W Міттаг

21

Відповіді вище чудові, але я маю на увазі інший. main()Метод Java означає, що студенти стикаються з базовими масивами дуже рано, часто, як тільки в перший день заняття. Чому?

public static void main(String[] args)

Це перше, з чим вам доведеться зіткнутися, щоб написати Hello World та за її межами. (Я бачив, як деякі курси спочатку використовують навчальні IDE, такі як BlueJ, які дозволяють вам клацати мишкою та клацати для запуску довільних методів, але ми відкладемо їх в сторону ...) Хоча, можливо, варто помахати руками над деякими ці ключові слова на деякий час, рано чи пізно більшість викладачів захочуть пояснити їх. Дійсно, класичне тестове питання для початківців - це попросити учнів надати значення кожного ключового слова в базовій програмі Hello World. І що ми знаходимо як частину нашого основного методу підпису? Масив (Причина цього частково історична. ArrayList не існував у Java 1.0). Масиви є частиною базового набору знань. Список немає.

Однак це не так вже й рідкість, коли класи впроваджують ArrayList трохи пізніше в курс, особливо після того, як об'єкти та їх використання будуть охоплені. Навіть навчальна програма AP Computer Science для Java включає ArrayList (я знаю, що це раніше, і Google, схоже, вказує, що це все-таки є), хоча він ігнорує той факт, що ArrayList реалізує Список та решту колекційних програм.

Нарешті, мій досвід, що університетські програми CS використовують Java як засіб для вивчення концепцій CS та програмування, а не для того, щоб навчити студентів, як стати хорошими розробниками Java. Деякі програми можуть бути більш орієнтовані на витіснення професійних розробників, в той час як інші зосереджуються більше на теорії, але в будь-якому випадку потрібно багато дізнатися про те, як використовувати Java в реальній професійній роботі, яку не навчатимуть у більшості навчальних програм коледжу. Це варіюється від моделей та методів дизайну, таких як Ефективна Java, до таких структур, як Spring, Hibernate або JUnit, або навіть більш поширених речей, таких як JSP або JDBC. Маючи на увазі цю філософію, підкреслюючи масиви над більш часто використовуваним ArrayList має трохи більше сенсу.


3
@ Дедуплікатор впевнений. Це просто не мало відношення до моєї точки зору масивів, тому я його опустив. Я також не включав System.out.println ("Привіт, світ!"); або.
Зак Ліптон

+1 для концепцій проти ефективних прийомів для "реального світу". Ніхто також не навчав контролю над версіями, принаймні не тоді, коли я навчався в школі - але тоді я стародавній :)
Девід

Я взяв клас операційних систем, де нам довелося реалізовувати різні фрагменти, як планувальник; ми зосередилися на одному такому творі за раз (виключно), оскільки в той момент ми не були здатні зробити більше . Але думати про спробу змусити всіх цих творів працювати одночасно було те, що дало мені (початки) оцінку складнощів "реального" розвитку / "програмування у великому".
Девід

14

Однією з причин того, що класи програмування першого курсу використовують масиви - це спадщина: саме так професори спочатку засвоїли це перед тим, як ми почали використовувати стандартні бібліотеки з динамічними списками. Застосування примітивних типів даних також більш загальноприйняте: масиви існують майже на будь-якому комп’ютері мова під сонцем (і може бути реалізована в кількох інструкціях по збірці). Коли я вперше навчався програмуванню, реалізація пов'язаного списку була одним із завдань.

Набагато простіше почати з перших принципів, а потім сказати "Це основна структура. Ця мова (або її бібліотеки) дає вам цю структуру даних високого рівня, яка робить усе це, але дає вам x, y і z", ніж це означає сказати: «Ось це ці структури даних високого рівня, ось ось що під кришкою». Навчитися міркувати про те, чи слід використовувати LinkedList vs. ArrayList (або HashSet vs. TreeSet), як правило, курс з алгоритмів другого або третього курсу. Списки та Карти мають однакові інтерфейси і дають однакові результати, але можуть мати різну поведінку в застосуванні будь-якого розміру. І як тільки ви вийдете з програмування 101, немає гарантії, що програмування 102 буде використовувати ту ж мову. Якщо ви почнете з концепції масиву, ви можете просто сказати "

Ще одна причина віддати перевагу "масиву" над "Список" у вступному курсі полягає в тому, що масиви принципово легко зрозуміти: Масив з 20 bytesзаймає 20 байт (плюс пара, щоб вказати або кінець масиву, або довжину, залежно від реалізації ).

"Список" - це зовсім інший чайник з рибою і його можна реалізувати різними способами (ArrayList, LinkedList і, мабуть, пару, про яку я забув), з принципово різними характеристиками. Не розуміючи начинку , що різні класи Списку роблять, ви не можете мати змістовну дискусію про те, коли слід використовувати List foo = new ArrayList()проти List foo = new LinkedList(). Якщо ви спробуєте змусити студента використовувати реалізацію списку, хтось запитає, чому ви використовуєте ArrayList замість однієї з інших реалізацій. І "ArrayList" включає слово "Array" і підкріплене одним, тож насправді це не величезний логічний стрибок з "масиву" на "ArrayList".

Всупереч поширеній думці, існують такі ситуації, коли має сенс використовувати масиви над списками, особливо коли ви маєте справу зі списком статичного розміру. Ось пара:

  • Пошук і ітерація дещо швидше, тому що ви не маєте справу з викликом методу накладних витрат: foo[n]перенаправлення та виконує деяку позаблокову арифметику вказівника, тоді як foo.get(n)доводиться перенаправляти, робити виклик методу, робити другу дереференцію, а потім, можливо, робити арифметику вказівника (якщо ви використовуєте ArrayList; LinkedLists потенційно потрібно повторити кожен елемент списку).
  • Ініціалізація набагато чіткіша : int[] foo = new int[]{1, 2, 3, 4, 5}порівняно з пропозиціями в іншому запитанні StackOverflow

Інший сценарій, коли масиви масово б'ють масиви, це те, коли послідовність, в якій стають доступними елементи, відповідає послідовності, в якій їх кінцеві положення стануть відомими, але не відповідає кінцевій послідовності. Якщо permце int[256]перестановка, яка може містити перестановку, її можна легко перетворити масивом: int[] inv = new int[256]; for (int i=0; i<256; i++) inv[perm[i]]=i; я не думаю, що щось, що можна було б написати, ArrayList<>було б таким чистим.
supercat

@supercat Це насправді не проблема з динамічними масивами, лише конкретна реалізація; ArrayListможна було легко отримати конструктор, який створює список, ініціалізований за замовчуванням певного розміру.
Доваль

Чи є у вас джерела для резервного копіювання твердження про те, що список масивів має стосуватися виклику методів накладних витрат? Теоретично це так, але на практиці я був би здивований, якщо getі setне нагадаю.
Доваль

@Doval: Немає причини, щоб добре розроблена мова / фреймворк не повинен надавати тип динамічного масиву, який може зручно додавати елементи з послідовності, але Java не надає такого типу.
supercat

@Doval: Навіть якщо getі setможе бути вкладений , щось подібне myList.set(23,myList.get(23)+1)буде ніде не настільки ефективним, як myArray[23]+=1. Крім того, навіть для типів, яким не потрібен бокс, я був би дуже здивований, якщо будь-який JITter міг би зробити щось еквівалентне тому, for (i=0; i<1000; i++) myList2.set(i+2000,myList2.get(i+3000));чия продуктивність знаходиться десь поблизу. System.arrayCopy(myArray1,3000,myArray2,2000,1000); Немає причини динамічний тип масиву рамки не повинен пропонувати швидку операцію діапазону копіювання, але Java ні.
supercat

7

Java дозволяє змінні будь-якого типу зберігатись у масивах. Навпаки, ArrayListдозволяє лише зберігати посилання. Таким чином, можна не корисно обговорювати, ArrayListпопередньо не розкриваючи, як автоматичний бокс перетворює примітиви в типи посилань, і як автоматичне розпакування іноді перетворює типи посилань у примітиви:

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

Якби в коді використовувався int[3]скоріше, ніж ArrayListне, сюрпризів не було б. Усі три елементи порівнювали б однакові iта між собою. Використовуючи ArrayList, проте, незважаючи на всі три елементи списку завжди будуть порівнювати рівні i, а перший і третій будуть завжди рівними один одному, перші два елементи будуть тільки рівні один одному , коли iце 1, 10 або 100, але (для більшості реалізацій) не коли i1000 або 10000.


Чи можете ви пояснити, чому # 0 і # 1 будуть рівні, коли вони рівні 1 або 10 або 100? Я до цього моменту стежив.
Матвій

2
@MatthewRead: IntegerКлас має вкладений клас, IntegerCacheякий називається, який містить попередньо ініціалізовані Integerоб'єкти для діапазону значень, який зазвичай поширюється на -128..127; бокс для значення поза цим діапазоном вимагатиме створення нового об'єкта, але значення боксу в межах кешованого діапазону просто поверне посилання на один із попередньо ініціалізованих об'єктів, що зберігаються в IntegerCache.cache. Будь-який хороший програміст Java повинен знати, що Integerдвотипові змінні, що інкапсулюють одне і те ж значення, можуть або не можуть порівнювати однакові, але введення цієї ідеї занадто рано може змусити студентів тікати з терором.
supercat

Так, ніколи не здогадувався б, що це пов’язано з попередньо ініталізованими об'єктами. Дякую за інформацію!
Матвій

1
@MatthewRead: Я хотів би, щоб Java заборонила певні види неявного перетворення ==. Враховуючи можливість того, що авторозпакування може кинути, я схильний повністю заборонити це, але його поведінка з ==особливо жахливою. ==Оператор також погано поводиться в подібних випадках 16777217==16777216f[звітів справжніх], і неявні перетворення для long v=Math.Round(123456789), w=Math.Round(123456789012345)схильні до несподіваних [ви можете здогадатися , що ці вирази будуть давати?]
Supercat

Що стосується IntegerCache, безумовно, є випадки, коли це може сприяти продуктивності, але це часто може спричинити код, який є просто звичайним неправильним для роботи свого роду. Якимось чином я хотів би, щоб існував режим, коли бокс квазі випадковим чином повертає об'єкти з двох цілих кеш-пам'ять, а квазі-випадковим чином замінює елементи кешу новими, так що код, що спирається на поведінку боксу значень -128..127, буде навряд чи вдасться дуже довго.
supercat

6

Я думаю, що є сенс навчити спочатку користуватися масивами через те, що ArrayListвикористовує масив внутрішньо. ArrayListКлас має змінну - член з ім'ям , elementDataяке є Objectмасивом.

Із ArrayList вихідного коду 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;

Коли ви додаєте, оновлюєте, отримуєте або видаляєте елементи з нього, ArrayListвін використовує цей внутрішній масив для виконання цих операцій. Як вже зазначив користувач Ixrec - ArrayListце просто абстракція вищого рівня, з якою зазвичай простіше працювати.


Але масив використовує вказівник на блок пам'яті, внутрішньо. Навіщо починати саме з цього конкретного рівня абстракції?

Питання позначено як Java- Це було питання, чому вчити масиви, коли ти зазвичай можеш використовувати ArrayList. У Java немає покажчиків. Правильно чи неправильно, я вважаю, що C / C ++ - це краща мова, з якої можна почати слухачів, ніж Java. Багато тем програмування можна краще зрозуміти, володіючи знаннями C / C ++.
Дерек Ш

3

Якщо припустити, що з цим списком справді простіше працювати, як ви кажете - це насправді не має значення. Навчання - це більше про "базове до складного", ніж про "легке до важкого". Якби основи не були важливими, то інформатика не була б академічною сферою. Ви можете просто навчитися клацати додатки за допомогою існуючих рамок / бібліотек з онлайн-підручників. (Звичайно, хтось повинен писати ці бібліотеки ... а хтось повинен реалізувати ArrayListв першу чергу ....)


У багатьох випадках з цим використанням ArrayListможна краще обробляти, використовуючи окремий масив і підрахунок "liveItems", за винятком того, що немає зручного способу роботи з масивом і підрахунку разом, за винятком їх агрегації.
supercat

2

Це тому, що найважливіше в академічній освіті - це навчити використовувати правильну термінологію для опису речей, які ви робите.

Список - це щось інше, що масив. і ви не можете використовувати java.util.Listв Java, тому що це інтерфейс. Зазвичай ви використовуєте те, java.util.ArrayListщо є реалізацією List, а не списком, а обгорткою об'єктів навколо динамічного масиву. Отже, ви кажете, що використовуєте "Список", але використовуєте масив.

Дуже розумно пропустити цей термінологічний хаос і просто використовувати масиви, щоб дізнати учнів, що таке масив. Якщо ви використовуєте масиви на Java, принаймні ви використовуєте масиви.

Чесно кажучи, це також аргумент, чому викладання програмування на Java не є хорошою ідеєю. Важко правильно засвоїти основні поняття програмування.


що з хешмапом? масив - це певний тип хешмапу, певним чином ..
Thufir

@Thufir, як масив може бути хешмапом? Це абсолютно різні структури даних.
Дунайський матрос

ви можете представити масив за допомогою хешмапу. що є більш "фундаментальним"? Я б стверджував, що хешмап є більш концептуально цікавим.
Туфір

1
@Туфір ні, ти не можеш. Можна лише наслідувати. Внутрішньо кожна структура даних буде такою, якою вона є. Це принципово і концептуально різні речі. Ось чому погано ідею починати вивчати програмування на таких дуже абстрактних мовах, як Java або JavaScript. Там незрозуміло, якими є насправді структури даних.
Дунайський матрос

1

Ви буквально ніколи не бачили і не використовували жодних масивів взагалі? Ми використовуємо їх постійно, окрім списків. Ми зазвичай не використовуємо Java, але використовуємо безліч інших мов, які очевидно схожі.

Між масивами та списками одна легша вага та, крім того, більш точна, тоді як інші мають більшу функціональність. Як правило, в програмуванні, коли є два подібних типи, які в основному розділені по цих лініях, вам слід скористатися легшим вагою, якщо вам насправді не потрібен більш вигадливий. На додаток до зменшення накладних витрат, це насправді допомагає зберегти обробку кількості неполадок та стану програми та, зокрема, її коду . Якщо під час тестування щось піде не так, у вас є менше місць для перегляду; і що ще важливіше у випадку масивів проти списків, люди отримують краще уявлення про обмежений обсяг того, що ви насправді використовуєте, і намагаєтеся робити з ним.

І так, з академічної точки зору, є додаткова причина навчання студентам основ. Однак це йде трохи глибше. Масиви та списки є хорошим прикладом об'ємних типів, які часто просто будуються поверх, і часто просто обертаються навколо основних примірників типів легшої ваги. Навіть коли в списках немає базових масивів, вони поводяться зовні, як і вони. Однією частиною навчання когось, що таке Перелік, було б навчити їх, що таке масив.

Я міг бачити, що це може вийти з рук на мові на зразок C ++, де масиви в основному не можуть бути більш позбавленими, ніж вони є, але на мовах вищого рівня вони майже не списки до себе. Якщо вони цілком відповідають вашим потребам у тій чи іншій ситуації, навіщо вам потрібно використовувати щось інше?


Я б не сказав, що у списку є "більше" функціональності, настільки, наскільки він має "інший" функціонал. Новий T[]буде готовий, одразу за воротами, приймати елементи в будь-якому порядку, тоді як ArrayList<T>вимагається додавання елементів для того, щоб вони могли бути змінені. A T[]може скопіювати довільний діапазон предметів у аналогічний за розміром діапазон іншого T[]порівняно швидко, тоді як ArrayList<T>потрібно буде читати предмети окремо з одного списку та зберігати їх до іншого - набагато повільніше та менш зручно.
supercat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.