So, dann möchte ich euch bitten, langsam das Sprechen einzustellen.
Ich hoffe, da ist nicht zu viel Feedback hier.
Geht so, ja. Okay. Ja,
herzlich willkommen zur elften Vorlesung SE1. Herzlich willkommen im Jahr 2014.
Es wird ein ganz großartiges Jahr.
Was euch möglicherweise interessiert, ist das Thema hier.
An dieser Stelle sage ich ganz gerne was zur Modulprüfung,
damit ihr einfach ein bisschen die Rahmenbedingungen kennt.
Es gibt eine Modulprüfung, die in schriftlicher Form stattfindet,
also eine Klausur. Es gibt zwei Klausurtermine für dieses Modul
. Der erste Termin ist am Montag,
den 3. Februar, 10.30 Uhr bis 12.30 Uhr.
Der zweite Termin ist einen Monat später am 3. März.
Gleiche Zeit. Ja, jeweils zwei Stunden. Das heißt,
Nicht mehr und nicht weniger, es sei denn,
ihr habt eine Ausnahmeregelung, die gibt es teilweise.
Keine Hilfsmittel außer einem Stift erlaubt, euer Gehirn müsst ihr unbedingt mitbringen.
Alles andere ist nicht erlaubt. Wir machen das als Multiple-Choice-Klausur.
Das heißt, zu jeder gestellten Frage werden mehrere mögliche Antworten vorgegeben.
Und entweder ist dann genau eine richtig oder es gibt en-richtige Antworten,
also N aus M. Das kennzeichnen wir üblicherweise bei jeder Aufgabe.
Das ist so ein bisschen so, wie in den Selbsttests,
eigentlich sehr, sehr ähnlich. Das heißt,
die Selbsttests, wenn ihr sie denn durchführt,
geben euch einen ganz guten Eindruck von der Klausur.
Die Selbsttests sind so auf viermal halbe Stunde getaktet.
Der vierte Selbsttest wird natürlich erst am Ende der Verlesungszeit kommen.
Aber das gibt euch insgesamt ein ganz gutes Gefühl für die Klausur.
Was auch eigentlich eine ganz einfache Weisheit ist,
dass wenn ihr den Übungsschein hier schafft in SE1,
dass ihr dann üblicherweise gut vorbereitet seid, um die Klausur zu schaffen.
Nicht, dass ihr sie dann mit einer guten Note besteht oder
sehr guten Note. Das ist ja nicht so weit ausgeprägt,
aber bestehen sollte möglich sein,
wenn man vernünftig im Übungsbetrieb mitgearbeitet hat
Ich sage das deshalb, weil es neuerdings die Regel gibt,
dass der Schein nicht mehr die Voraussetzung ist für die Teilnahme an der Klausur.
Der Schein ist eine Prüfungsleistung, die Klausur ist eine andere Prüfungsleistung.
Ihr könnt also theoretisch dieses Jahr die Klausur mitschreiben und im
nächsten Jahr nochmal den Schein machen. Wer immer das will,
halte ich, finde ich, so sinnvoll.
Es wäre wahrscheinlich schlauer, wenn man das Gefühl hat,
dass die Prüfung die Übungen gut gelaufen sind,
dann kann man auch an der Klausur teilnehmen.
Warum ist eine Frage? Steht auf jeden Fall bei den Aufgaben mit dabei.
Er fragt, ob bei den Aufgaben auch die Punktzahlen mit dabei steht.
Es steht üblicherweise dabei, dass es für jede richtige Antwort
einen Punkt gibt. Manchmal steht auch dabei,
dass es auch zwei Punkte gibt für die richtige Antwort.
Also so Grob stehen die Punktzahlen dabei.
Genau, ja. Okay, sonst noch Fragen zur Klausur
. Ja, da ist noch eine. Ist das wie
den Selbsttesttest, dass wir falsche Antworten nicht abgezogen werden?
Er fragt, ob es wie bei den Selbsttests ist,
dass für falsche Antworten Punkte abgezogen werden.
Ja, das ist genau wie bei den Selbsttests.
Das heißt, bei der Klausur ist es genauso,
innerhalb einer Aufgabe werden falsche Antworten von richtigen Antworten abgezogen.
Man kann innerhalb einer Aufgabe aber minimal null Punkte machen.
Es gibt also keine negativen Punkte in einer Aufgabe,
die sich auf andere Aufgaben oder die Gesamtergebnisse auswirken
Sonst noch Fragen zur Klausur. Da ist noch eine.
Aber für die Übung scheint es keine Bewerbung, oder?
Das ist nur entweder oder. Genau, er fragt,
ob es für die Übungsscheine keine Wertung gibt.
Und das ist auch so, der Übungsschein ist entweder Bestand
oder Nichtbestand. Es gibt keine Note für den Übungsschein.
Sonst noch Fragen. Da ist noch eine. Selbstverständlich,
der Mann mit Patrea, wenn man die einfach eintragen muss.
Gibt es lasst mir den Folter aufmerksam bezogen?
Er fragte, in den Selbsttests gibt es auch Fragen,
bei denen man eine Antwort eintragen muss.
Das kann ich nicht genau beantworten. Also es ist theoretisch
möglich, dass solche Fragen kommen
Müssen wir sehen. Aber also es ist denkbar,
ja. Okay, so habe ich jetzt,
glaube ich, so die meisten Fragen beantwortet.
Sehr schön. Dann habt ihr vielleicht schon mitbekommen,
dass wir ja SE1-Clips gedreht haben, also Videos,
die typische oder nicht ganz so typische Situationen im Laborbetrieb darstellen.
Und die findet ihr auch inzwischen. Wir haben fünf solche
Videos gemacht und die findet ihr auch tatsächlich über einen Link im Komsi.
Hier ist jetzt das Material im Komsi verlinkt.
Das heißt, ich müsste jetzt hier langsam mal aufpoppen,
ja,
da kommt es schon
So, SE1-Clips. Und dann könnt ihr tatsächlich hier über
diesen Link dahin kommen. Das liegt auf Lecture to go.
Da muss man dann ein Passwort eingeben. Das Passwort ist
auch angegeben. In dem Student ist temporär nicht erreichbar.
So viel dazu, dass ich mal eben ein Video zeigen wollte.
Okay, ich könnte jetzt improvisieren, mal im Kurz gucken,
ob ich hier irgendwo die... Nee.
Auch nicht so wichtig. Hätte ich jetzt gerne ein Video
ganz gezeigt, weil ihr letztes Mal so protestiert habt,
weil ich es nicht lang genug gezeigt hatte.
Ich hätte gerne das dann kommentiert, dieses Video,
weil es gibt ja so einige Dinge,
die in den Videos so speziell sind,
die man vielleicht gar nicht so bemerkt,
dass wir zum Beispiel so zwei Fortschrittsbalken haben bei jedem Studierenden.
Und der eine symbolisiert so ein bisschen den Lerneffekt und der andere den Bremsfaktor,
also den Faktor, mit dem diese Person andere Personenübungsbetrieb ausbremst.
Naja. Egal, also da kann man halt so
ein bisschen was zu sagen. Jetzt sind wir natürlich
auch an eurer Meinung interessiert. Also es kann sein,
dass ihr die Dinger noch gar nicht gesehen habt.
Es kann sein, dass ihr das lustig findet,
aber völlig uninteressant
Aber mögliche Fragen, die wir demnächst dann stellen wollen,
sind, habt ihr solche oder ähnliche Situationen schon erlebt?
Ist das überzeichnet, ist das realistisch? Vielleicht auch,
ihr habt Situationen erlebt im Übungsbetrieb, wo ihr sagt,
Mensch, das hat mich so genervt oder es war so,
auch vielleicht so positiv, dass man das vielleicht auch mal verfilmen könnte,
wenn ihr da Ideen habt, immer her damit.
Haben euch die Clips vielleicht irgendwie geholfen? Wer weiß,
dass ich gesagt habe, ja, da habe ich mal
so einen Eindruck gekriegt, wie ich es richtig mache.
Kann ja alles sein, das kommt alles noch. Das heißt,
ich sage jetzt schon mal an, die Clips sind ja verfügbar,
wenn ihr sie noch nicht gesehen habt, uns den Gefallen,
guckt sie euch nochmal an, damit wir,
wenn wir dann Fragen dazu stellen, nicht antworten müsst.
Ich habe das alles noch gar nicht gesehen.
Ihr müsst es aber auch nicht gucken, alles freiwillig.
Gut. Dann mal zu den ernsthaften Dingen des Lebens.
Wer kennt denn diese Figur hier? Da oben jemand,
wer ist das? Das ist Gamma. Sehr gut.
Da ist es Gamma, das ist eine Figur von Walt Disney.
Ich habe ja tatsächlich früher sehr viele Comics gelesen,
da komme ich leider nicht mehr zu.
Und ich habe tatsächlich auch Mickey Mouse gelesen,
falls ihr das noch kennt. Das war so ein Heft,
das man so jede Woche kaufen konnte und toll.
Und Da kam diese Figur drin vor.
So, warum erzähle ich jetzt was über diese Figur?
Wer weiß denn, was der so für besondere Eigenschaften hat,
was an dem so außergewöhnlich ist?
Ich kann es ja gleich erzählen. Diese Hose, ja,
diese Hose, die er da anhat, die ist ziemlich groß.
Er kann da alles Mögliche drin aufbewahren. Also ein Swimmingpool
oder ein Stuhl oder ein Auto, alles mögliche.
Er hat ziemlich viel Platz in der Hose.
Das ist ein bisschen absurd. Also es ist tatsächlich irgendwie
ein Behälter, in dem beliebig viel reingeht.
Der gute Gamma kommt aus der Zukunft,
hat irgendwie die vierte Dimension mitgebracht und deswegen kann er das irgendwie
Was ich damit sagen will, ist, so ein Behälter,
in dem beliebig viel reingeht, das ist genau das,
was ihr kennengelernt habt mit den Collections aus dem Collections Framework.
Es geht theoretisch beliebig viel rein, abgesehen von Speicherplatz im Rechner,
aber es gibt da keine Beschränkung. Ich habe mich immer gefragt,
als ich das gelesen habe, wie macht er das?
Also wie verdammt nochmal, kann der das hinkriegen,
so viel Zeug in so eine kleine Hose zu kriegen?
Und ja, darum geht es heute auch. Wir wollen
heute jetzt mal uns ansehen, wie man die Sammlungen,
die ihr kennengelernt habt als Klienten, also die Sammlungen wie Liste,
und Set und die Implementationen davon, wie man die implementieren kann.
Wir wollen jetzt in den nächsten zwei Wochen,
also heute und nächste Woche werde ich darüber reden,
wie man Sammlungen dieser Art implementieren kann.
Also wir wechseln jetzt die Seite. Vorher waren wir Klienten,
wir haben die Dinger benutzt und das ist die primäre Seite,
die ihr auch eigentlich einnehmen solltet.
Ihr solltet jetzt nicht denken,
weil wir jetzt hier darüber sprechen und euch vielleicht beibringen,
wie man solche Sammlungen implementiert,
dass ihr zukünftig einfach immer eure eigenen Sammlungen baut,
statt die Vorgegebenen zu benutzen.
Das ist nicht
das Ziel
Der Grund, warum wir jetzt hier über die Implementationen von Sammlungen sprechen,
ist, weil man an diesem Thema,
was da jetzt so ein bisschen aufkommt,
nämlich dynamische Datenstrukturen, extrem viel üben kann, das,
was wir bisher behandelt haben.
Das heißt, wir können sehr gut über Präferenzen sprechen und über Race,
wie man die benutzen kann, um dann etwas Dynamisches anzubieten.
Dynamisch im Sinne, die Kapazität kann beliebig vergrößert werden.
Race sind ja ziemlich blöd, wisst ihr ja,
meistens beschränkt, wenn sie erstmal angelegt sind.
Nee,
immer in Java
Und wir wollen aber ja sozusagen was Flexibles haben.
Und wie man sowas baut, das ist nicht so ganz einfach.
Okay, ich muss hier mal eben,
ich hatte die Reihenfolge ein bisschen verdreht. Ihr habt gesehen,
dass im Java Collections Framework für die Collection Interfaces, List,
Z, auch Map, dass da so einige Implementationen zur Verfügung stehen.
Und wir gucken uns jetzt an, wie die gebaut sind.
Also wir gucken uns einfach genau die Strategien an,
nach denen die gebaut sind.
Das heißt nicht, dass das die einzigen Möglichkeiten sind,
wie man
solche Interfaces bauen kann
Das sind zwei grundlegende Implementationen heute, die wir uns angucken,
für Listen. Und es sind zwei Implementationen für Mengen,
die wir uns nächste Woche angucken. Aber es kann auch
beliebig andere Möglichkeiten geben. Was bei allen gilt,
ist, dass sie auf Arrays und auf verketteten Strukturen basieren.
Das ist absolut elementar für alles, was man dynamische Datenstruktur nennt. Das heißt,
Rays einfach als ein Stück Speicher und verkettete Strukturen über Referenzen,
dass es in Java eigentlich fast alles, wie ihr ja schon mitbekommen habt.
Okay, Das heißt, die Implementation im Collections Framework machen von diesen Konzepten,
von diesen Sprachkonzepten Gebrauch, Arrays und verkettete Strukturen.
Und im Großen und Ganzen sind das genau diese vier verschiedenen Kombinationen,
einmal verkettete Listen, dann wachsende Arrays, balancierte Bäume, Hash-Verfahren.
Heute gucken wir uns die ersten beiden an. Und das
sind Beispiele für dynamische Datenstrukturen. Wenn ihr in der Literatur guckt,
da gibt es abstrakte Datentypen und die werden oft gleichgesetzt mit dynamischen Datenstrukturen.
Wir unterscheiden das hier so ein bisschen. Also abstrakte Datentypen,
das ist eigentlich das,
was die Interfaces bei uns beschreiben oder hier die Interfaces im Collections Framework,
die beschreiben einen Typ, einen Umgang,
Operation und irgendeine Leistung, die damit verbunden ist,
die ein Klient dann erwarten kann
Wie es implementiert ist. Das ist eine zweite Frage,
das kann man trennen. Und entsprechend kann man dann,
wenn man sich mit Implementierung beschäftigt, sich mit dynamischen Datenstrukturen beschäftigen.
Das ist also das Thema heute und auch nächste Woche.
Und ja, dynamische Datenstrukturen bezeichnen die Organisationsformen von veränderbaren Sammlungen von Objekten.
Klar, es geht um Samsung, Implementation. Und diese Strukturen,
die wir uns hier ansehen werden, sind Gebilde aus Elementen,
Objekte, also hier so gelbe Dinger und Beziehungen,
Relation, meistens mit zu feilen.
Üblicherweise wisst ihr, dass das in Java
dann Referenzen sind
Diese dynamischen Datenstrukturen sind meist gleichartig rückkursiv aufgebaut.
Was rekursiv jetzt hier bei Datenstrukturen heißt,
werden wir heute uns auch ein bisschen genauer angucken.
Und ändern einer Struktur heißt eben halt hinzufügen, modifizieren,
auch löschen von Elementen. Also das ist dynamisch in dem Sinne,
dass die Anzahl der Elemente in so einem Ding eben halt variieren kann.
Okay, man kann das noch so ein bisschen unterteilen in
verschiedene Arten von dynamischen Datenstrukturen. Das ist vielleicht nicht mehr
ganz so spannend jetzt hier, diese Folie.
Also dynamische Datenstrukturen nach den Eigenschaften ihrer grundlegenden Struktur eingeteilt.
Wir gucken halt hier so ein bisschen auf Mengen,
auf Listen, auf Bäume und auf Grafen.
Das ist, ja
Also das ist jetzt vielleicht nicht mehr unbedingt ganz so spannend.
Wie gesagt, wir gucken uns heute an, wie man Listen implementiert.
Also diese Logik hier, die ihr ja kennt,
den Umgang werden wir gleich nochmal uns genauer ansehen.
Und nächste Woche werden wir uns Mengen ansehen, wobei dann
Bäume benutzt werden, um Mengen zu implementieren und so weiter.
Gut. Das hier ist eine Liste,
eine angedeutete Darstellung einer Liste, die kennt ihr schon.
Also hier sieht man, es gibt irgendwie so eine
sequenzielle Struktur und es gibt auch Duplikate.
Wir werden uns jetzt zwei Implementationsformen angucken.
Eine basierend auf Verkettung, also heute vor der Pause
verkettete Listen
Wie kann man mit Hilfe von Referenzverkettung in Java
so eine Liste implementieren und danach dann wachsende Arrays,
also wie kann man Arrays benutzen, um da eine Flexibilität reinzubauen?
Gut, ganz wichtig ist erstmal wieder, sich klarzumachen,
was ist überhaupt eine Liste?
Ich meine, das haben wir jetzt mehrfach schon diskutiert.
Ich hoffe, das ist in den meisten Köpfen gut angekommen.
Es gibt immer zwei Seiten,
so ein Typ wie Liste beschreibt erstmal nur den Umgang,
also was kann ein Klient erwarten,
also kann beliebig viele Elemente enthalten,
ja genau,
beliebig viele
Auf beliebige Positionen zu greifen. Es gibt also was wie eine Index-Idee.
Ich kann also tatsächlich mit einem Index zugreifen. Wenn ich
ein Element einfüge, dann erhöht sich der Index der nachfolgenden Elemente.
Und beim Entfernen entsprechend umgekehrt. Und häufig möchte ich tatsächlich
auch bei einer Liste wissen, ob ein bestimmtes Element enthalten ist.
Um das mal zu verdeutlichen, jetzt muss ich mal ganz kurz hier,
das habe ich dummerweise iTunes noch nicht gestartet.
Möglicherweise kennt ihr iTunes. Das ist eine Software,
um Medien, beliebige Art zu verwalten,
üblicherweise auch Musikdateien
Und ich weiß nicht, ob ihr schon mal mit iTunes was gemacht habt.
Ich kann jetzt hier zum Beispiel mir eine neue Wiedergabeliste anlegen.
Also so SE1, Vorlesung 11, so.
Jetzt habe ich hier meine Liste, erstmal leer,
eine leere Liste, ganz ähnlich wie die Listen,
die ihr aus dem Plaction Frameworks benutzen könnt.
Jetzt kann ich hier sagen,
ich möchte irgendwie mal so ein paar Songs da reinschmeißen.
Diese Liste kann ich einfach rüberziehen. Ja,
und dann irgendwie noch, nehmen wir nochmal hier den dazu.
So, und dann, was nehmen wir noch?
Ja, sowas hier noch
Was ich hier gemacht habe, ist, ich habe Elemente,
Musiktitel, Stücke in einer Musikdatenbank in eine Liste gezogen.
Hier sieht man jetzt, diese Liste hat eine Reihenfolge.
Das heißt, wenn ich jetzt diese Liste abspielen würde,
dann würde zuerst Rehab kommen und dann back to black und dann,
we're this the time go.
Man sieht hier auch tatsächlich einen Index. Das heißt,
wenn ich jetzt einen weiteren Song nehme und den hier reinschiebe,
dann kann ich tatsächlich mit so einer Linie,
wird mir hier angedeutet, wo das hinkommt.
Das ist genau wie mit den Listen, die ihr kennengelernt habt.
Ich kann sagen, soll es dahin oder dahin.
Ich habe also drei Elemente und habe vier Möglichkeiten,
da was einzufügen.
Ich kann es also
jetzt hier einfügen
Das ist schon mal ganz wunderbar. Man sieht also,
es gibt eine Reihenfolge, ich kann die Reihenfolge auch ändern.
Ich kann also jetzt das Ding hier irgendwie verschieben, so.
Also das ist genau eine Liste, eine Liste.
Und warum ist es eine Liste? Erstens, weil ich
die Reihenfolge manipulieren kann. Zweitens, weil Duplikate zugelassen sind.
Wenn ich nämlich jetzt hier diesen Song nochmal in die Liste
ziehe, dann passiert folgendes.
Dann kommt hier so eine Meldung von iTunes, die sagt,
oh, es werden Duplikate zur Wiedergabe des da hinzugefügt.
Ihr seht, Apple hat uns unsere Begriffe geklaut. Also Duplikate,
sagen die auch dazu, wenn was eingefügt werden soll,
was schon drin ist.
Jetzt könnten wir eigentlich Apple verklagen,
aber ich lasse das mal sein
Auf jeden Fall kann ich jetzt hier entscheiden, ja,
das soll hinzugefügt werden.
Und dann wird tatsächlich dieser Song hier zweimal drin sein.
Das heißt, in einer Liste sind Duplikate zugelassen.
Ja, wunderbar. Also genau wie im richtigen Leben,
was wir hier in den Übungen machen. Und entsprechend dazu,
passend dazu, haben wir natürlich auch Blue Jay dabei.
Ja, ich mache mal hier iTunes zu, das brauchen wir nicht mehr.
Blue Jay, kommst du jetzt mal zu mir hier?
Ja gut, klicken wir einfach. Das ist das Projekt,
das ihr in den Übungen diese Woche bearbeiten werdet
Das sieht genauso aus wie iTunes, ne? Findet ihr
nicht auch? Also, es ist zumindest sehr ähnlich.
Was wir modelliert haben, ist genauso etwas wie eine Wiedergabeliste.
In iTunes heißt es Wiedergabeliste, wir nennen das hier eine Titelliste.
Also, eine Titelliste, hier ist das Interface, mit einem bestimmten Umgang.
Ich kann also einen Titel einfügen, hier steht Füge ein.
Als Parameter gebe ich einen Titel an, Titel ist anscheinend ein Typ,
den wir hier definiert haben, zeige ich gleich.
Und an einer bestimmten Position kann ich den einfügen.
Genau wie wir das ja haben wollen, bei Wiedergabewissen,
ich möchte an einer bestimmten Position etwas einfügen können.
Das heißt, man kann sich vorstellen, dieses Interface liegt
vielleicht irgendwo in der Implementation hinter den Kulissen von iTunes.
Nicht in Java geschrieben, wahrscheinlich, sondern was weiß ich.
Aber prinzipiell ist das Konzept das Gleiche. Genauso kann ich
dann hier auch etwas entfernen an einer bestimmten Position, an einen Titel.
Ich kann mir einen Titel, eineinhalb Stunden Position geben lassen.
Ich kann die ganze Liste lehren und ich kann auch mir
die Länge geben lassen. Also wie viele Titel sind denn
drin? Ist ja auch immer interessant. Auch bei iTunes
will man ja sehen, wie viele Titel sind denn drin.
Da wird noch mehr berechnet, wie lange die ganze Liste ist,
wenn man sie abspielt und so weiter.
Und hier ist auch so eine Operation enthält
Also kann ich fragen, ist ein Titel, den schon
in der Liste enthalten? Braucht iTunes wahrscheinlich auch intern,
weil genau diese Frage in dem Moment,
als ich den Titel doppelt reinziehen wollte,
musste ja iTunes gemerkt haben, oh, ist der schon drin?
Da muss ich ja jetzt irgendwie eine Meldung rausgeben.
Das heißt, dieses Enthält muss es auch geben. So,
und das ist eigentlich auch schon das, was wir hier mit diesem Interface abdecken.
Also eine sehr einfache Modellierung des Konzeptes einer Wiedergabeliste. Wobei die Elemente,
die man da reinfügt, das ist eine Sammlung,
eine Wiedergabeliste ist eine Lampe, eine Liste genauer.
Und der Sammlungstyp, der Elementtyp ist hier tatsächlich so ein Titel.
So ein Titel ist hier angedeutet, ist relativ langweilig,
hat so ein paar Eigenschaften, Album, Interpret,
Länge, Titelnahme und dann noch Hash Code und To String.
Das ist eher technisch, aber so fachlich sind das eben halt so die Eigenschaften,
die so ein Songtitel haben kann, zu welchem Album er gehört und so weiter.
Und was wir hier noch dazu gebaut haben, ist eine Titelbibliothek,
da kann ich mir jetzt mehr so ein Exemplar erzeugen und dann kann ich mir von dieser Titelbibliothek,
die liest sich so eine Textdatei ein aus dem Dateisystem,
mit so ein paar Jazztiteln und da kann ich mir jetzt hier mal so einen zufälligen Titel geben lassen und schwupp,
habe ich hier so ein Objekt, so ein Element,
ein Exemplar, der Klassiktitel,
dass ich mir mal die Referenz hier auf die Leiste legen.
Und dann habe ich hier so einen Titel. Mal gucken
, was das ist. Das ist irgendwie von Mark Copland
irgendwas good. Okay, also irgendeinen Jazz-Song. Und ja,
jetzt habe ich tatsächlich hier eine Referenz auf einen Titel.
Und jetzt kann ich so einen Titel ja einfügen über so
ein Interface in eine Implementation des Interfaces.
Und was ihr in den Übungen jetzt machen sollt,
ist, ihr sollt eine Array-Titel-Liste bauen und eine Linked-Titel-Liste.
Und genau das werden wir uns jetzt ansehen, wie man das beides bauen kann.
Wobei wir in den Jahren davor die Erfahrung gesammelt haben,
So der erste Jahrgang SE1,
der hat noch von uns die Aufgabe gekriegt, so,
hier habt ihr das Interface, jetzt baut mal,
baut mal die eine Implementation, braucht mal die andere.
Da haben wir gemerkt, das ist ein bisschen hart gewesen für
die Armstudierenden. Inzwischen geben wir euch so Gerüste vor.
Also das ist tatsächlich hier weitgehend implementiert und ihr müsst sozusagen
nur noch die Lücken befüllen. Aber ihr müsst trotzdem verstehen,
wie das Ganze funktioniert und dann entsprechend die fehlende Funktionalität dazubauen.
Dass das nicht funktioniert, sieht man daran,
dass die Testklassen, die ich hier sehr prominent vertreten sind,
zwei dicke, fette Testklassen,
die enthalten rein textuell mehr oder weniger
die gleichen Tests
Wir haben sie nur sozusagen in zwei Testklassen aufgespittet,
damit wir einfach, in der einen wird ein Exemplar von einem Ray-Titel,
das der erzeugt, in dem anderen ein Exemplar von Linktitel-Liste
und darauf werden dann die gleichen Tests ausgeführt.
So, wenn ich jetzt hier mal so diese Tests ausführe,
dann schlagen die halt alle fehlen. Das sind also hier
20 Testfälle und wenn ihr dann die Aufgabe bearbeitet in die Übung,
dann werden so nach und nach die Testfälle durchlaufen und wenn es gut läuft,
habt ihr irgendwann einen grünen Balken da.
Das ist ganz cool, weil dann nämlich diese Dinger hier,
das sind ausimplementierte Spezifikationen. Das heißt, das hier beschreibt,
wie eine Titelliste funktionieren soll. Da gibt es so
ein bisschen Kommentare dazu, das ist alles natürlich sprachlich.
Wir wissen jetzt, dass das wie eine Liste sich verhalten soll,
aber das ist eben halt so ein gemeinsames Wissen, das wir haben.
Wenn ihr das liest und ich gebe euch das vor,
dann ist das sozusagen, ist man sich darüber im Klaren,
was denn das jetzt leisten soll.
Aber diese Dinger hier, die benutzen das in dem Glauben,
dass das Listen sind und testen da alle möglichen Fälle.
Irgendwie drei Elemente dann das in der Mitte löschen, dann das erste löschen,
das letzte löschen, wie auch immer alle Fälle so durchgespielt.
Und wenn da irgendwas nicht klappt, dann was man sozusagen
erwarten kann bei so einer Liste, dann gibt es halt einen Fehler.
Das heißt, diese Dinger sind sehr,
sehr mächtig in dem Sinne, dass sie einfach sehr,
sehr klar festlegen, was muss die Implementation können?
Ihr werdet später nicht sehr häufig die Situation haben,
dass ihr solche Testklassen bekommt für das, was ihr implementieren sollt.
Das ist ein ziemlicher Luxus, den wir euch hier bieten,
aber ihr lernt dann eben halt auch,
dass man tatsächlich Testklassen als seine Freunde ansehen kann.
Ihr solltet sie nicht als die bösen Buben ansehen,
die ständig sagen, es ist noch nicht genug,
du hast noch nicht alles richtig gemacht.
Es wird natürlich erst mal so erscheinen, als wenn es so wäre.
Gut, das erst mal dazu, das ist der Umgang
und dann eben halt in diesem Projekt ein bisschen konkretisiert.
Jetzt listen wir Implementation im Java Collections Framework. Wie gesagt,
es gibt zwei Stunden Glück für das Interfacelist, Linklist und Arylist,
Linklist, verkettete Listen, Arylist, wachsende Arrays.
Wir gucken uns jetzt erstmal die Verkettung an.
Und jetzt muss ich nochmal wieder eins sagen, das,
was jetzt in den nächsten Folien passiert, das sieht super einfach aus.
Ich male hier so ein paar Kästchen hin,
so ein paar Pfeile dazwischen, easy.
Was wir gemerkt haben in den letzten Jahren, ist,
das ist trotzdem schwierig umzusetzen in der Implementation.
Also ihr werdet große Schwierigkeiten haben oder viele von euch
werden Schwierigkeiten haben, das direkt abzubilden.
Es ist nicht trivial. Es ist wirklich nicht trivial,
solche verketteten Strukturen zu implementieren Es ist total einfach,
das Konzept zu verstehen, aber es zu implementieren,
ist nicht so einfach. Also das ist tatsächlich so,
also so verkettete Strukturen gelten auch als, ja, beliebig kompliziert.
Man kann da sehr, sehr viel mehr machen,
als das, was wir in den Übungen jetzt mit euch machen.
Und man lernt einfach ganz viel dann mit Referenzen umzugehen. Gut,
also, verkettete Liste, endliche Folge eines Grundtyps,
Listenelemente besitzen eine Reihenfolge, Elemente des Gruppentyps können mehrfach unterhalten sein,
bla bla bla, haben wir das jetzt schon mehrfach gesagt.
Eine verkettete Liste ist grundsätzlich als Strukturteil eine Sequenz ihrer Elemente.
Also jedes Listenelement ist mit dem Nächsten verbunden,
mindestens mit dem Nächsten
Also hier sieht man, jedes Element ist mit dem Nächsten verbunden,
um vom Anfang zum Ende der Liste zu gelangen,
muss jedes Element traversiert werden.
Traversieren heißt so durchlaufen. Also wenn ich tatsächlich nur
eine Referenz auf dieses Element habe und ich will dahin,
dann muss ich mich da durchhangeln.
Ich muss also irgendwie die Referenzen durchlaufen.
Und dieses, die Referenzen durchlaufen ist nicht trivial.
Da muss man irgendwie eine Schleife bauen und sich überlegen,
wie man die Referenzen und so weiter weiterschaltet.
Die einfachste verkettete Liste, die es gibt,
ist die einfach verkettete Liste.
Das heißt, jedes Listenelement hat nur eine Referenz auf
seinen Nachfolgerelement
Und das ist die absolut einfachste Struktur, die man sich vorstellen kann.
Und kann nur elementweise vom Anfang bis zum Ende travisiert werden.
Doppelt verkettete Liste, da ist es eben so,
dass tatsächlich jedes Element sowohl eine Referenz auf sein Nachfolgelement hat,
als auch auf sein Vorgängerelement.
Wobei das erste natürlich keinen Vorgänger hat. Und das letzte hat
keinen Nachfolger, aber ansonsten hat jedes Element in der Liste
genau zwei Referenzen auf den Vorgänger, auf den Nachfolger.
Hat den Vorteil, dass ich diese Liste in beide Richtungen
durchlaufen kann. Da werden wir gleich drüber sprechen,
dass das Vorteile hat,
wenn man
das tun kann
Ja, soweit so gut. Einfach. Jetzt wollen wir aber ja
darüber reden, wie kann ich denn das objektorientiert implementieren?
Also dieses, diese Grundidee von verketteten, ob Elementen ist total einfach. Tatsächlich,
wie man es in der Objektorientierung in einem Objekt und in dem System macht,
das ist dann nochmal ein bisschen, da kann man genauer hingucken.
Das ist das Schema, was, was wir hier
auch in den Übungen jetzt mit euch umgesetzt haben.
Das heißt, es gibt ein Objekt,
das die Liste insgesamt für ihre Klienten repräsentiert.
Also hier in diesem Beispiel, LinkedTitelliste, verkettete Liste,
Das ist, das Ding repräsentiert die gesamte Liste.
Das implementiert ja auch genau das Interface-Titelliste. Okay,
dann haben wir Objekte, die als Kettenglieder dienen,
die nur dazu da sind, diese Verkettung zu ermöglichen.
Das sind hier diese rot angedeuteten Dinge, also die Dinger,
die hier so Kettenglieder sind und dann verkettet sind.
Und dann gibt es noch die Objekte, die als Elemente in der Sammlung enthalten sind.
In diesem Fall hier ist das eben halt Titel,
das Ganze ist ja eine Titellist,
das heißt der Elementtyp der Titelliste sind Titel,
die sind aber außerhalb irgendwo erzeugt worden.
Es werden ja immer nur Referenzen in so eine Sammlung in Java reingereicht.
Das heißt also, so kann man sich das ungefähr vorstellen.
Man braucht also mindestens zwei Klassen, um das zu bauen.
Und die dritte Klasse ist eben halt dann der Elementtyp der Elemente.
Gut, wenn wir uns jetzt mal so ein Kettenglied angucken
und wie baut man denn jetzt so ein Kettenglied?
Das kann man objektorientiert ganz einfach entwerfen. Ich sage einfach,
jedes Kettenglied ist ein eigenes Objekt und das heißt,
ich habe eine Klasse für die Kettenglieder und die
kann ich zum Beispiel Captain Glied nennen
Jetzt enthält jedes Kettenglied eine Referenz auf das eigentliche Element, also gelb angedeutet.
Also das ist tatsächlich dann hier Person oder in unserem Beispiel Titel,
eine Referenz auf einen Titel, das ist die Elementreferenz.
Und außerdem hat es mindestens eine Referenz auf ein weiteres Kettenglied,
bei einer einfach verketteten Liste nur genau diese eine Referenz.
Also hier so eine Verkettungsreferenz. So, ganz einfach,
jetzt, wie sieht jetzt die Klasse dazu aus?
Ja, eigentlich auch wieder ganz straightforward,
wenn man die Objektorientierung bis zu diesem Punkt ganz gut verstanden hat,
ist es nicht so schwierig. Ich sage einfach,
es gibt eine klasse Kettenglied und die hat als einen Als
eine Exemplar-Variable hier ein wahrer Benachfolger vom Typ Kettenglied.
So, das ist jetzt wieder was, was,
wenn man genau hinguckt, komisch ist.
Ich sehe jetzt hier steht, in der Definition der Klasse Kettenglied
wird Kettenglied verwendet. Das ist eine rekursive Definition.
Das heißt, hier wird tatsächlich das Ding selbst verwendet.
Das ist für uns gar nicht so schwer zu verstehen,
wenn wir bisher Referenzen schon gut verstanden haben.
Das heißt, zu sagen, naja gut,
ich habe in einem Kettenbieten eine Referenz auf ein anderes Kettenglied.
Warum nicht? Also auf irgendwas vom Typ Kettenglied
Warum nicht? In der Praxis werden die Objekte natürlich
nicht rekursiv definiert. Also selten wird ein Objekt,
diese Referenz auf das nächste Kettenglied auf sich selbst verweisen lassen.
Theoretisch wäre das ja denkbar. Also es ist nicht auf Objektebene rekursiv,
sondern nur auf der Typebene rekursiv. Das heißt tatsächlich hier,
die Klassendefinition enthält etwas, was vom eigenen Typ ist.
Das ist etwas, was man auch strukturelle Rekursionen nennt.
Wir nennen das hier auch strukturelle Rekursionen,
um es abzugrenzen gegenüber der Rekursion, die ihr schon kennengelernt habt.
Das war nämlich eine Methode, ruft sich selbst auf.
Das nennt man auch
funktionale Rekursion
Hier, jetzt geht es nur um strukturelle Rekorsion.
Also eine Struktur ist definiert über sich selbst.
Ich will es nicht zu kompliziert machen.
Also eigentlich ist es ja gar nicht so weh.
Ich habe nur den Begriff hier jetzt nochmal gebracht.
Die Rekursion ist gar nicht schwierig zu verstehen, sobald man Referenzen verstanden hat.
So, das ist ja wunderbar. Das heißt also,
jetzt haben wir das Kettenglied mehr oder weniger klargekriegt.
Das ist ganz einfach, dieses Kettenglied.
Wir können uns das auch hier mal angucken.
Das ist nämlich hier von uns schon vorgegeben, dieses Kettenglied.
Wir haben es Doppel-Link-Knoten genannt. Also das sieht mehr oder
weniger genauso aus. Hier ist erstmal
die Schnittstelle
Und da gibt es eben halt relativ einfache Operationen,
gibt den Nachfolger und gibt den Vorgänger. Kann ich mir
also sozusagen geben lassen. Ich kann den Nachfolger setzen,
ich kann den Nachfolger auch den Vorgänger auch setzen und ich kann den Titel setzen.
Also das Element, was da reingehört. Und mir den Titel
auch geben lassen. Also das ist wirklich so ein,
man würde umgangssprachlich sagen, es ist ein Datensack.
Ich kann was reinschreiben, kannst rauslesen, drei Elemente,
nämlich Titel, Vorgänger und Nachfolger. Okay,
dann gibt es aber noch einen Konstruktor,
beziehungsweise es gibt zwei Konstruktoren, einen,
der einfach einen leeren Loppeling-Knoten erzeugt und einen,
der Vorgänger und Nachfolger schon gleich bei der Konstruktion mit reingereicht bekommt.
Das werden wir uns gleich ansehen, wie man das benutzen kann.
Und in der Implementation ist es halt auch nicht wirklich spektakulär.
Es gibt hier Vorgänger, Nachfolger vom Typ Doppel-Link-Knoten,
also auch hier wieder die Rekursion.
Ja, und dann ist hier nicht wahnsinnig viel los.
Da passiert nicht viel. Könnt ihr euch in Ruhe auch in
den Übungen angucken. So, jetzt ist es so,
dass diese Kettenglieder, diese Exemplare von Kettenglied,
die innere Struktur der Liste darstellen.
Das heißt, die gesamte Liste, habe ich ja gesagt,
ist sowas wie ein Typ Linkedlist oder in unserem Beispiel
hier Linked-T-Titelliste
Und diese referenzierten Elemente, also zum Beispiel hier,
in dem Beispiel eine Person oder in unserem Beispiel Titel,
die Titel werden üblicherweise nicht als Teil der Sammlung angesehen.
Diese Elemente, diese Kettenglieder, die sind logisch eigentlich ein
Teil der Liste, an der Listenimplementation. Das heißt,
die Dinger sind wirklich logisch zugehörig, aber sie liegen außerhalb.
Das heißt, ich habe einmal eine Klasse dafür für das
gesamte Ding und einmal eine Klasse für die einzelnen Listenelemente.
Für die einzelnen Kettenglieder. Okay,
und in dieser Klasse Linklist habe ich dann üblicherweise die Referenz
auf das erste Kettenglied
Das ist hier tatsächlich schon in der Implementation vorgegeben. Also hier,
ja, ich habe hier einen Listenkopf und sogar hier
noch ein Listenende, sage ich noch mehr dazu gleich.
Und dann sieht man hier Listenkopf, kriegt hier erstmal so irgendwie was,
das gucken wir uns gleich nochmal genauer an.
Aber mindestens enthält so eine Implementation eine Referenz auf so
einen Listenknoten, auf so ein Kettenelement, Kettenglied.
So, jetzt kann man sich angucken,
wie würde denn jetzt das Einfügen in so eine Liste funktionieren?
Und das ist wieder von dem Konzept,
wie man es hier jetzt darstellt, total einfach,
wenn es implementiert,
nicht mehr ganz so einfach
Also angenommen, wir haben irgendeine Referenz auf ein neues Element.
Dieses Element möchte ich jetzt einfügen. Ich hatte irgendwie ja
so einen Titel erzeugt und den möchte ich jetzt einfügen.
Gut, dann kann ich das hier tatsächlich machen,
indem ich erstmal ein neues Kettenglied erzeuge.
Also hier wird tatsächlich ein Kettenglied erzeugt,
New Kettenglied und dann wird da gleich schon irgendwie das Element eingetragen.
So, dann soll dieses neue Kettenglied ja in der bestehenden Verkettung,
die schon da ist, angefügt werden.
An der gewünschten Stelle. Also ich sage jetzt hier an
Positionen 012, an Position 2, soll das eingefügt werden.
Also was passiert, naja,
ich setze einfach dass die Referenz auf das nächste Element hier in
dem neuen Knoten und außerdem muss ich dann diese Referenz hier umbiegen auf diesen Knoten.
Und schon ist das Element eingekettet in die Liste.
Das sieht ganz einfach aus. Das Problem ist,
ihr müsst erstmal die richtige Stelle finden. Das heißt,
ihr müsst Referenzen haben auf zumindest das Element davor.
Und da müsst ihr sozusagen durch die Liste durchlaufen,
um an die richtige Stelle zu kommen.
Und das ist nicht ganz so einfach. Das solltet ihr
dann auch in den Übungen mal implementieren. Ganz parallel dazu
das Entfernen aus der Liste, auch wieder ganz einfach.
Wir haben also unsere Liste mit diesen Elementen.
Und jetzt wird die Referenz auf ein Element, ich will,
also dieses Element möchte ich hier entfernen.
Ganz einfach, ich biege einfach die Referenz von dem Vorgänger
auf den Nachfolger um. Also zack, so. Dann
habe ich sozusagen das Element hier ausgekettet. Es zeigt jetzt
keine Referenz mehr auf dieses Element. Was passiert,
wenn auf ein Objekt in Java keine Referenz mehr verweist?
Ihr wisst es, das wird vom Garbage Collector entfernt.
Also das Ding ist dann einfach weg. Ob dann
das Element auch weggeräumt wird, interessiert uns nicht.
Wir schmeißen jedenfalls diese Referenz weg,
wenn sonst keine Referenz mehr auf das Element ist
Wird das Element möglicherweise auch weggeschmissen. Kümmert uns nicht.
Uns geht es ja hier momentan nur um die Liste.
Okay, soweit total einfach. Okay, dann wird sie
auch weggeräumt. Wir gucken uns jetzt mal an,
und das ist eine ganz gute Gelegenheit,
nochmal so diese Idee von Klassendiagrammen zu üben,
wie man jetzt das, was ich gerade als objektorientierten Entwurf gezeigt habe,
in dem UML-Diagramm darstellt.
Also wir haben unseren Elementtyp, der ist so ein bisschen an der Seite,
dann haben wir Linked List als etwas, was die ganze Liste repräsentiert.
Und wir haben diesen Typ Kettenlied. Das ist ein Klassendiagramm,
das heißt, das sind Typen hier. Okay,
und dann kann ich jetzt einfach sagen, erstens,
die Klasse Linked List hält ja einen Verweis auf die Klasse Kettenglied.
Da verwenden wir dann in der UML-Notation tatsächlich auch so einen durchgezogenen Fall.
Ganz bewusst ein durchgezogener Pfeil. Das heißt, wir sagen,
wenn, in einer Klasse, eine Exemplarvariable von dem anderen Typ existiert,
dann soll das hier so eine durchgezogene Linie sein.
Das ist ja erstmal nur eine Benutztbeziehung.
Das heißt, Linked List benutzt Kettenglieder
Ja, so ein Pfeil im OML-Deremsatz erstmal nur benutzt.
Das ist eine etwas stärkere Beziehung, diese durchgezogene Linie sagt nämlich tatsächlich,
das hält eine strukturelle, es gibt eine strukturelle Abhängigkeit.
Das heißt, das Ding hält tatsächlich dauerhaft eine Referenz auf
so etwas. Von diesem Typ. Dann gibt es,
außerdem in diesem Fall, das heißt,
Linked List hat ja in seiner Schnittstelle eine Referenz auf den Element-Typ.
Das kann man hier auch sehen, ja,
also Link-Titelliste oder auch meinetwegen Link-Titelliste hier direkt als Implementation.
Da gibt es ja hier Titel. Also in der Schnittstelle von dieser Listen-Implementation
taucht der Typtitel auf, also gibt es irgendwo eine Abhängigkeit.
Irgendwie benutzt ja wohl Link-Titelliste Titel. Aber, wenn ihr
mal oben guckt, es gibt keine Exemplarvariable vom Typ Titel.
Okay, das heißt also, deswegen ist das auch hier so,
dass diese Abhängigkeit hier eine etwas schwächere Abhängigkeit ist.
Das kann man in der UML auch darstellen.
Da sagt man einfach nur, das ist eine Dependency,
eine Abhängigkeit im Sinne von der Benutztelemente,
hält die aber nicht dauerhaft
Wer die dauerhaft hält, ist der hier. So ein Kettenglied,
so ein Kettenglied hält ja wirklich als Exemplarvariable hier so
ein Kettenglied und ein Element. Hier ist das Element,
also ist hier eine Referenz auf ein Element drin.
Und außerdem hält ein Kettenglied eine Referenz auf sich selbst,
das kann man auch so darstellen, also hier entsprechend so ein Selbstbezug.
Und schon haben wir ziemlich viel Semantik in diesem Klassendiagramm drin,
von diesem objektorientierten Entwurf.
Wenn man den Entwurf erstmal verstanden hat, ist das nachvollziehbar.
Das werdet ihr in den Übungen euch nochmal sicherlich
in Ruhe angucken können
Okay. Der Schritt jetzt von der einfach verketteten Liste zur
doppelt verketteten Liste ist total einfach. Statt dass ein
solches Kettenglied nur eine Referenz auf das nächste Kettenglied hat,
ist es jetzt so, dass sie außerdem noch eine Referenz auf das vorhergehende Kettenglied hat.
Ganz einfach. Also es gibt in jedem Kettenglied zwei Referenzen
auf Vorgänger und Nachfolger. Und es gibt ein Element.
Und das ist auch genau das, was hier in unserer Doppel-Link-Knotenklasse passiert.
Also hier gibt es Vorgänger, Nachfolger und Element vom Typtitel.
Also genau wie auf der Folie her
Ja. Okay. Was ich jetzt noch mal zeigen kann,
ist, wie man solche Kettenglieder tatsächlich hier live verketten kann.
Ich kann tatsächlich hier so einen Doppel-Ding-Knoten mir hernehmen.
Ja, ich zeige mir hier mal so einen Doppel-Ding-Knoten.
Und da kann ich jetzt mir mal anzeigen lassen, wie der so aussieht.
Das sieht man hier. Der hält jetzt in seinen Exemplar,
weil er hat erstmal nur Nall-Referenzen. Da ist nichts drin.
Okay. Und das geht ja hier im Bluejay ganz schön.
Ich kann ja tatsächlich mir jetzt hier mal sagen,
ich trage jetzt mal bei diesem Doppel-Ding-Knoten hier,
setze einen Titel rein.
Nun habe ich zufällig hier so eine Referenz auf dem Titel
rumliegen, also kann ich den Titel da eintragen
Jetzt ist hier tatsächlich der Titel eingetragen. Aber die Verkettung gibt es noch nicht,
weil das ist ja bisher das einzige Verkettungsglied, was wir haben.
Jetzt mache ich nochmal so ein Exemplar von so einem Doppel-Link-Knoten.
Und hier kann ich jetzt schon Vorgänger und Nachfolger angeben.
Jetzt mache ich mir so ein Bild, dass ich sage,
aha, dieser nächste Knoten soll sozusagen logisch dahinter kommen.
Also sage ich hier mal, der Vorgänger,
also erstmal tat ich hier wieder einen Titel ein,
dann nehme ich jetzt den gleichen Titel,
ist eine ziemlich langweilige Wiedergabeliste.
Und der Vorgänger soll
dieser andere doppelte Knoten sein
Der hier. Und der Nachfolger, da gibt's halt nichts
, da muss ich jetzt einen Nall reinschreiben.
Also da habe ich einfach noch nichts, weil das der Letzte ist.
So, jetzt habe ich zumindest schon mal von hier,
von meinem ersten Knoten aus, den zweiten Knoten verlinkt.
Also hier habe ich jetzt die Referenz auf den Titel.
Ach ne, genau. Und ich habe aber hier,
na, hier habe ich jetzt den zweiten Knoten und der
ist jetzt sozusagen logisch, da ist es der erste Knoten,
das ist der zweite Knoten.
Und dieser hier hat jetzt den Vorgänger schon eingetragen,
was wir jetzt noch nicht gemacht haben, ist,
dass wir von diesem Knoten aus den Nachfolger eintragen.
Also wir möchten jetzt bei diesem Knoten sagen, hey,
dein Nachfolger ist dieser Knoten hier.
So, und jetzt haben wir tatsächlich die Situation,
dass wir hier zum Vorgänger navigieren können.
Und genauso aber auch von dem Vorgänger mit einem Klick hier
zum Nachfolger navigieren können. Also wir können sozusagen zwischen diesen
beiden Knoten beliebig hin und her springen. Das geht hier interaktiv,
programmatisch sieht das ein bisschen komplizierter aus, aber jetzt auch keine Hexenkunst.
Man muss nur einfach ein bisschen das Konzept verstanden haben,
Referenzen verstanden haben, dann ist das machbar.
Also ihr sollt dann genau diese Verkettung hier bauen.
Also ihr seht, man kann tatsächlich hier interaktiv schon mal
so eine doppelt verkettete Liste einfach im BlueJ bauen
So, was gibt es jetzt noch für Design-Alternativen?
Das eine ist, was wir auch in den Implementierungen,
jetzt in der Implementierung gemacht haben, die wir euch vorgeben,
an den Listen enden. Also am Anfang und am Ende,
hier anfangen und Ende, da können explizit leere Kettenelemente stehen.
Diese Kettenelemente werden auch Wächter genannt. Wächter ist ein bisschen
ein großes Wort, das ist einfach ein Programmiertrick.
Wenn man mit verketteten Listen sich auskennt und das häufig programmiert hat,
dann merkt man, das ist ganz cool,
wenn ich, und das machen wir tatsächlich,
also hier ist zum Beispiel,
hier ist so ein Wächter am Ende,
der enthält üblicherweise
keine Referenz auf ein Element
Also keinen Titel oder keine Person oder was auch immer,
sondern nur das hier sind die echten Elemente.
Oder so ein Wächter am Anfang und üblicherweise, wenn man Wächter benutzt,
nimmt man einfach am Anfang und am Ende ein.
Warum macht man sowas? Warum hat man so leere Wächterknoten
am Anfang und am Ende? Das ist tatsächlich Erfahrung,
wenn man mit solchen Dingern programmiert hat,
dann hat man beim Einfügen und auch beim Entfernen und allgemein
bei diesen ganzen Operationen, die man so hat,
weniger Sonderfälle.
Nehmen wir mal den Listenanfang. Also angenommen,
es soll ein Element an Positionen X
eingefügt werden
Ich habe also hier so eine Situation, ohne Wächterknoten, Listenkopf.
Ich habe nur die eine Referenz in meiner Klasse und soll jetzt
an einer bestimmten, an irgendeiner beliebigen Position X was einfügen.
So, Einfügepositionen, möglicherweise hier 0-1,
2 und 3. Das sind die vier Möglichkeiten,
die bei einer drei elementigen Liste was einfügen kann.
Wenn ich das ohne Wächter mache, dann kann ich nicht
in jedem Fall auf das Element vor der Einfügeposition positionieren.
Also wenn ich tatsächlich jetzt das neue Element,
was ja noch dazukommt jetzt, da wird ja ein neuer Knoten angelegt,
der muss jetzt hier in die Liste rein.
Wenn der hier an Position 0 soll,
dann kann ich nicht auf das Element, auf den Knoten,
sorry auf den Knoten, vor der Position positionieren.
Weil da ist ja nichts. Da ist nur diese Referenz auf
den Listenkopf. Das heißt, ich muss den Sonderfall abfangen.
Also wenn Position null, dann muss ich das mit dem Listenkopf machen.
Ansonsten mache ich was anderes. Also ein If-Ads,
das man mehr hat in der Implementation. Mit dem Wächter
kann ich einfach beim Einfügen immer auf das Listenevent positionieren,
das unmittelbar vor der Einfügestelle steht.
Ich kann tatsächlich einfach auf notfalls auf den Wächter positionieren und
dann eben halt an den Wächter was dranhängen,
um an Position null was einzufügen
Das heißt, das einfügen sie dann immer gleich aus und
man braucht keine Bedingung dafür. Das ist also einfacher. Das heißt,
diese Wächter machen euch das Leben leichter in der Implementation.
Deswegen haben wir sie ja in der Implementation drin.
Nur wenn man nicht weiß, wofür die Dinger da sind,
ist es natürlich ein bisschen blöd.
Also dass ihr nicht überrascht seid. Um ist es eine Frage.
Wenn jetzt ein Wächter vorhanden wäre. Und man hört dann,
was auf die Position null einsetzen würde,
gerne erst nach der Vordebäcke oder nach.
Okay, sehr gute Frage. Also hier, er fragt,
wenn ein Wächter da ist am Anfang und ich möchte an Position null etwas einfügen,
würde das dann vor dem Wächter eingefügt oder nach dem Wächter?
Wer hat die Antwort
Nach dem Wächter, wer ist für vor dem Wächter?
Gibt es jemanden, der der Meinung ist, vor dem Wächter?
Genau. Also es macht ja Sinn, dass der Wächter immer vorne bleibt.
Also wird es tatsächlich dann immer nach dem Wächter eingefügt.
Genau. Okay, das ist das,
was ich hier dazu sagen wollte. Dann, ach,
da ist ja auch nochmal die Darstellung mit dem Wächter,
genau. Dann gibt es noch die Design-Alternative,
wenn man verkettete Listen baut,
dass man nicht nur eine Referenz auf den Listenanfang hält,
sondern auch auf das Listenende
Wenn man häufig den Fall hat, dass man am Ende einer Liste etwas einfügt.
Also zum Beispiel bei den Wiedergabelisten in iTunes ist es so,
wenn ihr irgendwie einen Titel einfach draufzieht, wird er am Ende eingefügt.
Das ist also ein sehr häufiger Anwendungsfall. Deswegen ist es
gut, wenn man da schnell was einfügen kann.
Wenn ich also eine Referenz auf das letzte Kettenglied halte,
kann ich da schnell auch am Ende was einfügen.
Wenn ich das nicht mache, ist es ziemlich blöd.
Da muss ich jedes Mal, wenn ich am Ende was einfügen will,
vom Listen anfangen durch die ganze Liste laufen,
um dann am Ende was einzufügen.
Das ist nicht so toll. Deswegen ist es
ziemlich schlau, auch an letzten Ende mitzuhalten
Den Exemplarvariablen der Listen-Implementation. Bei einer doppelten,
verketteten Liste ist es dann sogar so, dass ich,
auch wenn ich schlau sein will und irgendwie an Positionen kurz vor Ende was einfügen will,
da muss ich nicht vom Anfang durchlaufen,
sondern ich kann irgendwie so ans Ende und dann kurz zurücklaufen.
Bei einer doppelt verketteten Liste kann ich an beide Richtungen laufen.
So, das ist schon alles, was ich zur verketteten Listen sagen wollte.
Das sind die Grundideen. Es gibt deutlich komplexere Möglichkeiten, solche Listen zu bauen.
Es gibt in der Informatik einen ganzen Wald an verschiedenen dynamischen Datenstrukturen,
Skiplists zum Beispiel,
wo man nicht nur eine Referenz hält in einem Knoten auf Vorgänger und Nachfolger,
sondern auch auf zehn Elemente weiter,
um dann relativ schnell Strecke überbrücken zu können,
um dann irgendwann abzubremsen und dann sozusagen nur noch kleinere Schritte zu gehen.
Ganz raffinierte Dinge. Total kompliziert. Die Informatiker lieben sowas
. Es reicht, wenn ihr erstmal das Konzept einer
einfach verketteten Liste und einer doppelt verketteten Liste hier versteht.
Weil wenn ihr das könnt, dann könnt ihr schon ziemlich viel.
Was ein ganz wichtiger Tipp ist, das ist hier angedeutet,
das ist so ein Blog-Eintracht, der, glaube ich,
gar nicht mehr erreichbar ist leider.
Ich fand ihn aber trotzdem ganz schön.
Er hat Fred mir mal geschickt
Wann immer ihr mit solchen Objektstrukturen zu tun habt,
wo irgendwas verkettet ist und man muss irgendwas einführen,
man muss so ein bisschen sich das klarmachen, macht euch eine Zeichnung.
Das ist wirklich eine Skizze. Zeichnung ist schon ein großes Wort,
macht eine Skizze, so ein paar Böbbel dahin malen,
ein Objektdiagramm, ist ja nicht so schwierig, ein paar Referenzen dazwischen.
Und dann könnt ihr euch wirklich überlegen, ah,
wenn ich da jetzt, das hilft ungemein.
Die Aussage hier ist mehr oder weniger, ohne das geht
es eigentlich gar nicht, wenn man irgendwas Komplizierteres machen soll.
Also sowas wie hier so eine Liste einfach mal umdrehen.
Ich habe mal vom Kollegen erzählt bekommen,
der hat sich mal bei Google beworben,
hat dann auch mehrere Stufen geschafft und hat dann unter anderem auch so einen Test machen müssen.
Und da war eine der Aufgaben, so eine einfach verkettete Liste umzudrehen.
Also Code zu schreiben, die man in so einer einfach verketteten Liste umdreht, also reverse.
Also die Reihenfolge komplett umdrehen. Wie würde man das implementieren? So,
und da ist es echt schön,
wenn man sich mal eben so eine Zeichnung machen kann und sich das überlegen kann,
im Kopf das zu machen,
das können wahrscheinlich so wenige Superhirne auf diesem Planeten. Macht euch Zeichnung,
macht euch Skizzen, hilft ungemein.
Das war alles, was ich jetzt zu verketteten Listen sagen wollte.
Gibt es noch Fragen jetzt zu verketteten Listen
Das scheint nicht der Fall zu sein, dann machen wir
fünf Minuten Pause. Vielleicht noch mal eine kleine Bemerkung zu
dem Zitat hier auf der Folie. Good Programmers Plan,
before they right code, especially when there are a pointers involved.
Nur dass ihr Terminologie mal wieder seht,
die Referenzen in Java heißen im allgemeinen Sprachgebrauch Pointers.
Es gibt in c-basierten Sprachen,
gibt es das Konzept von Pointern und Pointer sind so ähnlich
wie die Referenzen von Java
Die Referenzen in Java sind ein bisschen sicherer als allgemeine Pointer. Das sind Sachen,
die werdet ihr vielleicht in eurem Programmierleben nochmal genauer kennenlernen,
aber ihr könnt das lesen wie Referenzen jetzt momentan für SE1. Okay.
Gut. Jetzt Nummer zwei. Ja,
also wir haben jetzt gesehen,
wie man mehr oder weniger vom Konzept das hier bauen kann,
so eine doppelverkettete Liste, Link-Titelliste mit Hilfe von so einer Knotenklasse.
Jetzt wollen wir uns angucken, wie baut man denn
eine Array-Titelliste
Und das ist eigentlich auch ganz einfach.
Die Grundidee ist wieder, es gibt eine Klasse,
zum Beispiel Arrail-Liste oder an unserem Beispiel hier LinkedRay-Titelliste,
die repräsentiert dem Klienten gegenüber die gesamte Liste.
Und bietet auch alle Operationen an der Schnittstelle an.
Das heißt also, das ist sozusagen die klassische Außensicht,
die Klientensicht. Also irgendein Klient benutzt die Arrail-Liste. So,
intern verwendet jetzt diese Klasse, diese Art der Implementation,
die wir uns jetzt ansehen, nicht eine Referenz auf irgendwelche Kettenglieder,
sondern eine Referenz auf und Array.
Und in diesem Array werden alle bisher eingefügten Elemente gehalten.
Wenn ich sage, ein Array ist es eigentlich nicht ganz richtig,
weil eigentlich hält so eine Implementation zu verschiedenen Zeitpunkten möglicherweise auch Referenzen auf verschiedene Arrays.
Aber immer zu jedem Zeitpunkt immer nur auf ein Array. Okay,
das heißt, so sieht das ungefähr aus in der Innensicht,
also Array-Liste hält eine Referenz auf eine Ray und das Array enthält dann die Elemente.
Es kann dann sein, dass das so wie hier ist,
dass es dann Referenzen auf Elemente sind.
Es kann natürlich auch so sein,
dass man als Elementtyp einen primitiven Typ angegeben hat,
aber das ist ja,
sagen wir nur,
ein Spezialfall in Java
Gut. Ja. Und die Grundidee ist eigentlich ganz einfach.
Race als unmittelbare Implementation für so eine beliebig dynamisch vergrößerware Liste,
sind ja ungeeignet, weil sie ja eine festgelegte Größe haben.
Deswegen die Idee von so einer Ray-Implementation, das ist sogenannte wachsende Arrays.
Das heißt, ich habe zuerst irgendeine Array,
lege ich an, mit einer bestimmten Kapazität.
Das ist ein ganz wichtiger Begriff. Kapazität versus Kardinalität.
Kardinalität hatten wir schon eingeführt als Begriff bei Sammlungen allgemein.
Das ist einfach die Anzahl der Elemente in so einer Sammlung
Und die kann abweichen von der Kapazität. Also hier ist es
jetzt tatsächlich so, diese Ray-Listen-Implementation hält intern eine Array.
Und dieses Array ist wie so ein Container mit einem bestimmten Platz.
Ein Beispiel hier auf der Vorlesende ist jetzt irgendwie sieben,
sieben Zellen, in denen Elemente gehalten werden können.
Also mit einer bestimmten Anfangsgröße. Und sobald da jetzt Elemente eingefügt werden,
kann ich halt diese Zellen in dem Array benutzen,
um da Elemente abzulegen.
Und wenn das Array voll ist,
dann erzeuge ich einfach ein größeres und kopiere dann
alle Daten um
Das heißt, bei so einem Ray,
was man in dieser Implementierung hier verwendet,
ist es immer so, dass die Elemente von Index null
an aufsteigend liegen und es da keine Lücken dazwischen gibt.
Das ist sozusagen immer, es läuft hier, von null
beginnt einfach voll. Und es kann eben halt volllaufen,
das können wir uns gleich genauer an.
Aber ganz wichtig, die Kapazität ist immer größer gleich der Kardinalität.
Das ist logisch, wenn die Kapazität kleiner wäre,
dann müsste ich ja irgendwie schaffen, in einem Container von,
wo Platz ist für sieben Elemente, zwölf Elemente unterzubringen,
wird nicht funktionieren.
Da ist kein Platz. Also das ist eine In-Variante.
Also wenn man so eine Implementation sich anguckt,
dann kann man das tatsächlich als eine In-Variante formulieren für diese Implementationsart.
Eine In-Variante ist eine Aussage, die nie verletzt werden kann.
Wenn sie verletzt wird, ist die Implementation kaputt.
Also üblicherweise gilt diese In-Variante. Kapazität größer gleich Kardinalität.
Okay, der Vorteil bei so einer Ray-Implementation ist,
dass der Zugriff auf ein beliebiges Element
sehr schnell ist
Das ist ja einer der Gründe, warum Arrays so toll sind,
weil sie ja direkt auf ein Segment im Speicher abbilden.
Dann kann man direkt mit einem Index basierten Zugriff in
dem Speicher schnell zu einem Element hinkommen.
Also wenn ich 200 Elemente habe, komme ich an ein
Element 100 direkt dran. Das ist also wirklich toll.
Wenn ich aber was einfügen will oder was löschen will,
dann haben wir ja gesagt, bei Listen ist es so,
dann werden alle nachfolgenden Indizes verschoben, dann ist das ein bisschen schwieriger.
Im Extremfall, wenn ich tatsächlich etwas ganz am Anfang
einfügen will, muss ich alle Elemente um ein Element verschieben
Das heißt, wenn wir uns hier mal das Beispiel angucken,
hier haben wir eine Railist-Implementation angedeutet,
also da ist das Array zu sehen, mit momentan vier Elementen.
Kardinalität ist also vier, Kapazität ist sieben.
Jetzt kommt hier eine Anforderung, ein Klärensaal,
ich möchte gerne ein Element X an Position 1 einfügen.
So, jetzt sehen wir hier an Position 1,
also an der Position, wo ich das jetzt ablegen möchte,
steht ja schon ein Element.
Blöd, kann ich ja nicht einfach überschreiben,
dann wäre das B ja weg.
Geht nicht. Also muss ich
das B wegschieben
Wenn ich das B wegschiebe, ist das C im Weg.
Also man muss sich das C wegschieben und mir das,
dann muss das eh wegschieben. Aber das eh kann nicht wegschieben,
weil hier ist ja noch Platz. Das heißt,
was tatsächlich passiert ist, ich werde jetzt hier diese Elemente schön
der Reihe nach verschieben und dann ist eine Lücklücke geschaffen,
dann kann das Element da einfügen.
Eigentlich total einfach, nur man muss es halt machen.
Das heißt, ich muss tatsächlich irgendwie rückwärts durch die Elemente
gehen und die alle eins weiterschieben,
damit ich dann die Lücke da habe an der richtigen Stelle
Okay, das ist der allgemeine Fall, der meist übliche Fall,
dass dieser Container, dieses Array, nicht voll ist.
Hinterher ist dann die Kardinalität 5, wir haben also
fünf Elemente drin, die Kapazität ist gleich geblieben.
Wir haben das Array nicht verändert, wir haben nicht ein neues Array angelegt.
Wir müssen aber manchmal ein neues Array anlegen,
nämlich wenn das Ding voll ist. Hier ist das angedeutet,
ich habe hier irgendwie jetzt sieben Elemente in meinem Array eingetragen,
ABC, DEF XY.
Und jetzt möchte ich hier wieder Kriminalität 7,
Kapazität ist auch sie,
die können gleich sein
Das Ding kann voll sein. Das ist völlig in Ordnung.
Optimale Nutzung des Speichers sozusagen. Jetzt kommt wieder so eine Anforderung,
ich möchte jetzt ein Element Z an Position 1 einfügen.
Normalerweise würde man jetzt anfangen, wieder hinten anzufangen,
die wegzuschieben, aber man kann das Y hier ja nicht wegschieben,
weil da ist einfach keine Zelle mehr im Array frei.
Ja, was macht man? Man liegt ein größeres Array
an. Jetzt ist die Frage, wie viel größer soll
das Array sein? Was sind so Vorschläge von euch?
Wie viel größer sollte das Array sein? Doppelt,
das ist eine klassische Wahl,
auch im Collection-Framework
so gemacht
Aber man könnte auch sagen, naja, es muss ein Element da rein.
Ich kann es auch einfach nur ein Element größer machen.
Ich knicke einfach jetzt einen Array an,
das acht Elemente Platz hat für acht Elemente.
Und dann kann ich gleich alle umkopieren und dann das Element einfügen.
Nur dann ist das Ding wieder voll. Beim nächsten Einfügen
muss ich wieder ein neues Array anlegen und wieder alle Elemente umkopieren.
Nicht so toll. Deswegen üblicherweise macht man da etwas,
dass man sagt, man verdoppelt die Kapazität.
Das heißt, man wird hier ein doppelt so großes
Array angelegt
Ja, Race von 0 bis 13, also 14 Zellen Platz.
Dann werden die ganzen Elemente rüber kopiert. Da kann man
zum Beispiel Ray Block Copy verwenden. Da gibt es so Bibliotheks-Routinen,
die einem das wirklich ermöglichen, dass man so das einfach
darüber kopieren lässt von einem Array in ein anderes.
So, und jetzt geht das gleiche Spiel wieder los,
wie er es schon vorher gesehen habt.
Ich verschiebe jetzt hier die Elemente und dann habe ich
Platz um das Element an der richtigen Position einzufügen.
Und dann habe ich Kapazität, ist jetzt 14,
Kapazität ist vergrößert, Kardinalität ist 8,
also habe ich jetzt hier noch Platz für sechs weitere Elemente
Das ist schon eigentlich alles, was man verstehen muss,
um wachsende Arrays zu bauen. Ich kann hier mal die
Musterlösung zeigen. Einfach, um es mal zu zeigen,
wie das aussehen kann, wenn man das dann benutzt.
Ich habe also hier tatsächlich eine Ray-Titelliste, eine Implementation.
Und da kann ich jetzt entweder eine New Array-Titelliste machen ohne Parameter,
ohne Konstruktorparameter, dann wird da eine bestimmte Kapazität angenommen.
Ich weiß gar nicht genau, welche wahrscheinlich zehn.
Dass erstmal Platz ist für zehn Elemente. Ich kann aber auch
eine Anfangskapazität angeben. Ich glaube, das ist eine Zusatzaufgabe,
die auf dem Blatt ist. Ich kann also sagen,
ich weiß, dass in das Ding schon mindestens 20.000 Elemente reinkommen,
dann wäre es ja blöd,
wenn ich bei zehn Kapazitäten anfange und immer wieder mit verdoppeln,
sondern kann ich gleich sagen, ich weiß,
es kommt viel rein.
Also fang schon mal gleich mit 20.000 Elementen an.
Kann man so machen. Wir machen es jetzt mal völlig
entgegengesetzt. Ich möchte jetzt hier zeigen, was passiert,
wenn das Ding vollläuft. Ich möchte das hier mal demonstrieren.
Also mache ich eine Rate-Titelliste mit einer Kapazität von drei?
Ziemlich doof erstmal. Das ist hier die Referenz auf
ein Objekt, das die ganze Liste repräsentiert
Kann ich mir jetzt hier anzeigen lassen. Und dann sehe
ich hier, das ist jetzt hier meine Array-Titel-Liste.
Da sind momentan null Elemente drin. Und hier habe ich
eine Referenz auf das Array und das Array ist leer.
Hat die Größe drei, ja, Länge drei.
Und es sind drei neue Referenzen drin, ist nicht drin.
Das Ding ist leer. So, jetzt fange ich wieder an,
wie hier mal von der Titelbibliothek,
mal eben so einen Titel geben zu lassen.
Holen wir den mal eben hier auf die Objektleiste.
So, gucken, was es ist.
Chat Baker, gut, gut. Den Titel möchte ich
jetzt hier einfügen
Kann ich einfach sagen, füge ein an einer bestimmten Position.
Naja, also Positionen gibt es momentan nur null,
ist die einzige Position, die möglich ist.
Und es soll eingefügt werden, genau dieser Titel hier.
Oh, danke für den Hinweis. Also hier soll eigentlich
der Titel hin. Titel und dann die Position.
Vielen Dank für den Hinweis. So, okay.
Wunderbar. Steht auch noch, so ein Titel darf nicht
null sein und so weiter, nicht nall.
So, jetzt sieht man hier, hat man gerade gesehen,
in dem Array wurde diese Referenz jetzt eingetragen.
Also tatsächlich habe ich jetzt hier in dem Array
die Referenz eingetragen. Und hier ist der Zähler
auch um eins erhöht worden
Die Liste als Gesamtobjekt weiß, ich habe jetzt ein Element drin.
Kardinalität ist jetzt eins, also Anzahl Titel, Kardinalität.
So, und jetzt kann ich hier auch gucken,
das ist wieder genau dieser Titel, den ich gerade hier gesehen habe.
Genau. Das ist nicht so spannend.
So, jetzt mache ich das Ganze nochmal.
Also hier wieder Titel 1 und hier wieder eine Position null.
Ich wirke denselben Titel ein, langweilige Liste. Es geht
ja nur ums Demonstrieren. Ihr seht, hier ist der
zweite Referenz jetzt auch drin. An Position null. Das heißt,
er hat das Ding jetzt auch weitergeschoben,
aber das kann man jetzt hier gar nicht so schön sehen,
weil ja alle Titel gleich sind,
die wir hier benutzen.
Ich mache es nochmal
Und hier wieder Titel 1 und Position 0. So,
jetzt sind tatsächlich alle drei Zellen belegt. Das heißt,
die Kapazität ist erschöpft, das Ding ist voll.
Kardinalität, gleich Kapazität. Wenn ich jetzt nochmal Füge einaufrufe
und nochmal diesen Titel einfüge, ich liebe diesen Titel,
der ist so toll, muss ich viermal hören,
dann kann ich jetzt der sogenannten Position 3 einfügen,
also am Ende, ja, drei, so.
Ich habe ja schon drei Titel drin.
Und dann tatsächlich, hier oben, habt ihr gesehen,
ist es jetzt auf vier gesprungen.
Das heißt, das Ding enthält jetzt vier Titel.
Aber dieses Array hier hat ja
nur Platz für drei
Das heißt, was die Implementationen jetzt gemacht hat,
sie hat sich tatsächlich ein neues Array angelegt und die Titel umkopiert.
Das heißt, wenn ich jetzt diese Referenz hier verfolge,
dann komme ich nicht mehr auf dieses alte Array,
sondern ich komme auf ein neues Array,
das ein bisschen größer ist, in dem Fall jetzt sechs Elemente.
Und dieses alte Array hier, wenn da keine Referenz mehr
drauf existiert, dann wird es weggeräumt.
Der Garbage Collector räumt es ab. Danke, Java,
danke diesem Garbage Collector. Also wirklich sehr angenehm,
wenn man das nicht selbst machen muss. So,
und jetzt haben wir hier tatsächlich wieder Kardinalität 4 und Kapazität
6. Das heißt, wir können hier noch zwei Elemente aufnehmen,
werden wieder mehr Anforderungen kommen,
müssen wir
wieder verdoppeln
Genau, dann habt ihr einfach mal gesehen, wie das
funktionieren kann. Und hier ist es auch hoffentlich so,
dass jetzt die ganzen Tests auch durchlaufen, mal gucken,
alles testen, jupp,
das klappt hier und das klappt hoffentlich auch hier.
Ja, also so grün kann der Balken sein.
Da werdet ihr ein bisschen für investieren müssen, um das hinzubekommen in der Übung.
Gut, das Ding wird dann vom Gabelstreck da abgeräumt.
Gut, gut, gut. Jetzt würde ich gerne
ein bisschen in einen Vergleich eintreten. Einfach nur,
man kann sich jetzt fragen, warum gibt es denn in diesem Collections Framework zwei Implementationen?
Ist das nicht total bescheuert? Warum reicht nicht eine?
Warum hat man zwei? Hat jemand eine Idee?
Warum das sinnvoll ist, zwei verschiedene Implementationen zu haben?
Ich habe euch schon das Konzept erläutert,
man hat das Interface und man kann verschiedene Implementationen haben.
Das ist ja nur erstmal das, was technisch von Java aus möglich ist.
Konzeptuell habt ihr jetzt gelernt, man kann dieselbe Schnittstelle auf verschiedene Art implementieren.
Aber warum man es dann tatsächlich tut in einem konkreten Fall,
Was für Gründe kannst du da geben, ja?
Für einige doppelt mehr. Okay, er sagt Performance-Gründe,
dass für verschiedene Anwendungsfälle verschiedene Implementationen unterschiedlich geeignet sind.
Genau darauf will ich jetzt hinaus. Genau das will ich
jetzt diskutieren, da oben ist noch eine Meldung.
Bei der Liste kann man einfach schneller ein. Also meine
Influss ist aber schneller eingegrüngen worden, bei den Raiders zu draußen.
Okay, also er sagt, bei der Linklist kann man
schneller einfügen und bei der Arrail-List kann man schneller auslesen.
Also ein Element finden an einer bestimmten Position. Das geht schon
genau in die richtige Richtung. Das sind genau die Hinweise,
die wir jetzt hier uns mal ein bisschen systematisch angucken wollen.
Also ich weiß, es gibt im Collections Framework Linked List and Arealist.
Und ich will jetzt einfach mal zwei typische Operationen mir angucken.
Also erstmal einfügen eines Elementes. Das ist ja das,
was wir durchaus häufig tun in so einer Liste.
Und Zugriff auf ein Element einander bestimmten Positionen. Wir gucken
uns erstmal das Einfügen an. Bei der Linke List,
wie eben schon angedeutet, ist es so,
ich muss erstmal an die Positionen kommen, wo eingefügt werden sollen.
Also wenn ich bei einer Liste mit 10.000 Elementen an Positionen
5.000 was einfügen will, dann muss ich da erstmal durchlaufen, traversieren.
Ich muss also erstmal da durchlaufen. Das heißt,
im Durchschnitt wird dabei ungefähr die halbe Liste durchlaufen
Bei einer doppelt verketteten Liste, wo man auch von hinten dran laufen kann,
ist es im Schnitt ein Viertel der Liste, was man durchlaufen muss.
Also ich kann ja immer mir überlegen,
wo muss ich denn hin, wie groß ist die Liste?
Die Information, wie groß die Liste ist,
habe ich ja eigentlicherweise direkt gespeichert. Dann weiß ich also,
wenn ich jetzt an Positionen 7500 was einfügen muss,
dann kann ich von 10.000 rückwärts laufen und bin schneller,
als wenn ich von vorne durchlaufe.
Aber bei einer normalen Liste, wo ich nur von vorne durchlaufen kann,
muss ich im Schnitt die halbe Liste durchlaufen.
Außerdem muss ich für jede Einführung ja so ein Kettenglied erzeugen,
Erzeugung, ist eigentlich kein echter Nachteil im Sinne von Geschwindigkeit,
weil so Objekterzeugung ist in Java, Virtual Machines dermaßen optimiert,
dass das wirklich nicht ins Gewicht fällt.
Was vielleicht eher ins Gewicht fällt, ist, dass man tatsächlich Speicherplatz verbraucht.
Also tatsächlich muss ich ja so ein Kettenglied haben,
so ein Kettenglied hat mindestens drei Referenzen gespeichert. Also das Element,
Vorgänger, Nachfolger bei so einem doppelt verketteten Ding, das sind schon,
bei so einer normalen WM mit 32 Bit sind das irgendwie vier Byte,
vier Wörter, zwei, naja, also irgendwie,
was weiß ich, sechs,
acht,
zwölf Byte
oder so
Die braucht man ja nicht bei einer Arraylist.
Vorteil, das Einfügen ist einfach. Das heißt,
ich muss nur einfach umkennen, wenn ich erst mal an der Stelle bin,
wo ich hin will, muss ich nur noch diese Verkettung da machen.
Bei der Arraylist ist es auch ein bisschen doof.
Da gibt es auch einen Nachteil. Wenn ich jetzt an einer bestimmten Position was einfügen will,
wieder das Beispiel, ich habe 10.000 Elemente,
will jetzt an Position 10.000 was einfügen bei 10.000 an Positionen,
5.000, was einfügen.
Da muss ich 5.000 Elemente verschieben, damit ich da dieses
eine Element einfügen kann. Kann bei einer großen Liste ziemlich
teuer sein, dieses 10.000 Elemente verschieben oder 5.000 Elemente.
Es wird also im Durchschnitt auch da gemittelt,
über so alle möglichen Zugriffe,
ungefähr die halbe Liste umkopiert
Und der Nachteil ist außerdem, wenn die Kapazität ausgeschöpft ist,
also wenn das Ding voll ist,
dann muss ich ja noch ein neues Array anlegen und dann alles umkopieren.
Okay, der Vorteil ist, diese Position kann ich aber direkt ansprechen.
Also wenn es tatsächlich zufällig am Ende ist, dann ist das unglaublich schnell.
Ich kann einfach direkt da was einschreiben. Bei der LinkedIn
ist es aber auch so, wenn es zufällig am Ende ist,
wenn ich halte eine Referenz auf das Ende,
geht das auch sehr schnell.
Okay, dann beim Zugriff auf eine beliebige Position,
da spielt die Arraylist ihre Stärke voll aus.
Also es ist einfach so, ich kann bei einer Arraylist
beliebig ein Element auslesen
Da muss ich bei einer Linklist einfach immer im Schnitt die
halbe Liste durchlaufen oder eine Viertel der Liste, je nachdem.
So, das heißt also, diese Implementationen sind für unterschiedliche Benutzungsprofile unterschiedlich geeignet.
Das ist genau das, was der Kommilitone eben sagte.
Es kommt auf die Effizienzanforderung an, das hängt eben davon ab,
wie man das Ding benutzt. Wenn ich relativ konstante Listen habe,
bei denen ich häufig wahlfrei auch beliebige Positionen zugreifen will,
dann ist eine Arrailis besser geeignet. Wenn ich sehr dynamische,
große Listen habe, dann kann es eventuell besser sein,
mit einer Linklist zu arbeiten,
weil ich da irgendwie sehr häufig auch am Anfang gerade einfügen muss,
dann ist das Verschieben innerhalb der Arraillist einfach ein bisschen aufwendiger.
Tatsächlich ist es so, dass der Speicherplatz,
der beansprucht wird, auch eine große Rolle spielt.
Also wenn ihr wirklich so mehrere hundert Millionen Elemente ablegen wollt,
dann wird das mit einer Raillist vielleicht noch funktionieren,
während es mit der Linklist vielleicht nicht mehr funktioniert,
weil einfach der Speicher voll ist,
weil ihr ja dann für eure 100 Millionen Elemente auch 100 Elemente,
100 Millionen Kettenglieder haben müsst.
Und die sind ja relativ groß.
Und dann ist einfach möglicherweise
der Platz voll
Der Platz ausgeschifft, der Speicher ausgeschöpft. Gut, das heißt,
die Pragmatik für Java ist eigentlich, benutzt die Arealist,
wann immer ihr könnt. Ihr solltet verstanden haben,
was die Unterschiede sind, dann wundert ihr euch nicht,
warum an manchen Stellen die Effizienz sehr unterschiedlich ist.
Ich kann das noch mal zeigen,
in dieser Musterlösung hier ist nämlich auch ein Effizienztester.
Ich glaube, das ist eine Zusatzaufgabe oder eine Aufgabe,
die ihr auch bearbeiten sollt. Da ist es tatsächlich so gemacht,
hier wird in einer Operation vermessert, also eine Titelliste,
eine Referenz auf eine Titelliste übergeben.
Es kann also eine Areal-Titelliste sein oder eine Linktitelliste,
dann wird aus der Titelbibliothek, werden irgendwo hier,
einen Titel rausgeholt und der wird dann einfach 10.000 Mal in die Liste eingefügt.
Eine Liste füge ein, diesen Titel am Ende.
Hier wird also immer am Ende eingefügt. Listegeblänge ist sozusagen
genau die Länge der Liste und da wird dann auch entsprechend an der Position eingefügt.
Und jetzt wird hier gemessen, das habt ihr schon gesehen,
irgendwie die Systemzeit wird rausgegeben und dann ausgelesen und dann wird hier irgendwie an Position null eingefügt.
Also hier werden 10.000 Elemente an Position null eingeführt.
Also es sind schon 10.000 Elemente drin,
es kommen weitere 10.000 Elemente an,
Position null dazu
Okay. Und das ist das eine und dann startet
10.000 Zugriffe auf den mittleren Titel. Also tatsächlich,
dass ich hier wieder eine Liste mache mit,
nee, auf dieser Liste jetzt tausendmal sage,
gib mir doch mal den Titel an Position 10.000,
was in der Mitte ist, weil ich ja vorher auf 20.000 aufgeblasen habe.
So, das ist derselbe Code. Ich kann damit also
ein Exemplar von Array-Titel-Liste testen. Also ich kann so mir
eine Array-Titel-Liste mehr erzeugen und kann jetzt hier mir einen Effizienz-Tester
erzeugen und den jetzt hier mal benutzen und sagen,
Effizienz-Tester, vermesse doch mal bitte die Array-Titel-Liste
So, und dann sagt er hier, Listen hält zuerst 10.000 Titel,
dann starten sich das Einfüge von 10.000 Titeln an erster Position,
das dauert ungefähr 100 Millisekunden und dann mache ich 10.000 Zugriffe
auf den mittleren Titel, das dauert 2 Millisekunden.
Wenn ich das jetzt vergleiche mit der anderen Implementation,
jetzt nehme ich hier mal die Linklist,
guck mir mal hier die, man sagt dem Tester,
mach doch das Gleiche nochmal,
bitte für die verkettete Liste,
dann kommt hier sowas raus
Das sieht gut aus, startet 10.000 Zugriff auf den mittleren Titel Dauer,
400, hä? Ach, ich habe, Entschuldigung,
ich habe Konsole löschen bei Methodenaufwurf gemacht, das ist nicht so schlau.
Also machen wir nochmal hier den Effizienztester nochmal, vermesse nochmal
die Array-Titelliste, damit wir das beides da stehen haben.
So, jetzt sieht man es nochmal. Man sieht jetzt hier,
der ist richtig schnell beim Zugriff, auf da ist er sicher,
das ist gar nicht mehr messbar, null Millisekunden,
aber das Einfügen an der ersten Position,
das dauert bei der Array-Liste sehr lange,
weil ja sehr viele Elemente verschoben werden müssen
Hier ist es genau umgekehrt. Dieses Einfügen an der ersten
Position geht bei der Verketteten. Das ist total schön.
Ich muss nur so ein paar Dinge umketten und so ein
Kettenglied erzeugen. Aber dieser Zugriff auf das mittlere Element,
das dauert deutlich länger, weil ich jedes Mal durch die
halbe Liste durchlaufen muss an die Position 10.000 zu kommen.
Wenn ihr seht, also das ist total gegenläufig.
Also es kommt ganz darauf an, wie man so ein Ding benutzt,
dann ist eben halt die eine oder die andere Implementation besser.
Okay, das haben wir jetzt gesehen. Also Linklist ist
eine Verkettung von Knoten und die eine Kette bilden in die
Ziehung durch abzählen, einfügen, neues Objekt,
das wird eingekettet,
löschen kettetledig
ein Element aus
Raylist, dynamisch wachsendes Array, direkt initiierbar, sehr schnell im Zugriff.
Einfügen erfordert verschieben und eventuell muss sogar auch was
neu erzeugt werden und umkopiert werden, löschen genauso.
Es findet bei der Arraylist im Collections Framework keine Verkleinerung statt.
Also wenn ihr so ein Raylist mal sehr groß gemacht habt
und dann wieder sehr klein gemacht habt,
dann ist immer noch das Array der Container sehr groß.
Da können also dann irgendwie, er kann Platz sein für
10 Millionen Elemente und ihr habt nur drei Elemente drin,
dumm gelaufen.
Der Platz ist einfach verbraucht. Also auch das sollte man wissen,
wenn man sehr dynamische Listen hat und man hat ein Speicherproblem
Sollte man vielleicht dann doch keine Arraillist nehmen. Okay,
das können wir uns jetzt noch mal so ein bisschen genauer angucken.
Also die Informatiker haben sich seit, was weiß ich,
im letzten halben Jahrhundert ziemlich viel Gedanken darüber gemacht,
wie man solche dynamischen Datenstrukturen implementieren kann.
Da gibt es, wie gesagt, ganz tolle Geschichten.
Und was man relativ schnell rausfinden wollte, war,
welche Vor- und Nachteile haben denn bestimmte Implementationen?
Wie kann man das formalisieren? Da ist also
auch so ein Heimatgebiet der theoretischen Informatik.
Die Frage, wie effizient ist denn so
eine Implementation
Ein oder die andere. Und da wird dann gerne untersucht,
die Laufzeit effizient, also wie schnell ist die Implementierung und die Speicherplatzeffizienz,
also wie viel Speicherverbrauch denn so eine Implementation.
Gut, und da ist es einfach so,
wenn man das jetzt mal irgendwie ein bisschen genauer betrachten will,
dann guckt man sich an,
da wird man den Aufwand benennen als eine,
als ein Produkt von elementaren Schritten.
Also elementare Schritte. Wir sind zum Beispiel eine Zuweisung,
also etwas, was in Java irgendwie eine feste Aufwand kostet,
eine Zuweisung zu machen oder einen Vergleich durchzuführen.
Das sind so Dinge,
die sind relativ konstant in dem Aufwand
Und das sind elementare Schritte oder eben halt auch so ein Objekterzeugung von
kleinen Objekten, so ein dierende Methoden und so weiter.
Also so Dinge, die sind relativ konstant.
Und das sind elementare Schritte. Und die zählt man einfach
. Man zählt einfach, wie viele Vergleiche braucht man,
wie viele Zuweisungen an Variablen und so weiter.
Also wenn ich zum Beispiel hier einfüge an Position I machen will,
dann muss ich mehrere elementare Schritte machen, muss die Liste durchlaufen.
Man muss also eine Variable ständig neu zuweisen,
muss dann mal das Kettenbild erzeugen und muss irgendwie die Dinger zuweisen.
Gut. Dann ist es so, wenn man jetzt so eine Liste,
wie wir es gerade gesehen haben, sich und überlegt,
dann ist es ja immer klar, je nachdem,
wie groß die Liste ist,
ist der Aufwand
sehr unterschiedlich
Und das ist, wenn man dann so solche Betrachtungen anstellt,
die Unterscheidung in den konstanten Anteil und in den variablen Anteil.
Der Konstantanteil ist, ich muss immer ein Kettenglied erzeugen,
um es einzufügen in der verketteten Liste.
Das ist konstant immer gleich, egal ob die Liste groß
ist oder klein. Und da gibt es den variablen Anteil.
Also abhängig davon, wie viele Elemente drin sind.
Und oft ist es so, dass der variable Anteil eben
halt genau direkt abhängig ist von der Größe der Datenstruktur.
Das heißt, man macht das zu einer Funktion.
Das heißt, der Aufwand wird zu einer Funktion,
die von der Anzahl Ende
zu verarbeitenden Datenelemente abhängt
Und dann gibt es eben halt genau solche Abschätzungen, im schlechtesten Fall,
Worst Case, oder auch die Abschätzung, Aufwand im Mittel.
Best Case, Worst Case, Average Case.
Da wird eben üblicherweise immer von großen Datenmengen ausgegangen,
um diesen konstanten Faktor irgendwie vernachlässigbar zu machen.
Und dann hat man eine Einschätzung davon, wie der Aufwand ist für bestimmte Operationen.
Und diese Aufwandsbetrachtung für Algorithmen auf solchen Datenstrukturen,
die werden, da spricht man auch von ihrer Komplexität,
Das ist also ein Begriff, der nicht zu verwechseln ist mit,
das ist komplex, das ist kompliziert,
sondern Komplexität ist ja wirklich ein Fachbegriff in der theoretischen Informatik.
Das ist der ganze Bereich der Komplexitätstheorie. Damit beschäftigt ihr
euch im nächsten Semester in FGI 1. Also dass das systematisch zu untersuchen,
wie ist das denn mit diesen Aufwänden für solche Operationen?
Und das ist dann die Notation, die da verwendet wird.
Das ist die sogenannte O-Notation, umgangssprachlich.
Landau-Notation ist eine aus der Mathematik stammende, entlehnte Notation,
die eben halt sehr gut geeignet ist,
um solche Komplexitätsklassen zu beschreiben
Typische Komplexitätsklassen, mit denen ihr,
wenn ihr euch mit Algorithmen beschäftigt, in Kontakt kommt,
ist sowas wie O von 1. Das heißt,
so viel wie konstanter Aufwand.
Also alle elementaren Schritte haben O von 1. Wir haben gerade gesehen,
bei einer verketteten Liste, alle im Element einfügen, hat O von N.
Also Einfügen oder Suchende an der Liste, O von N.
Also es ist abhängig von der Größe der Liste,
wie teuer das ist, das Element einzufügen.
Wenn das Ding, wenn ich 20.000 Elemente habe,
brauche ich halt im Schnitt ungefähr 10.000 Operationen,
um das Ding entsprechend zu suchen,
die Stelle,
an der
es eingefügt werden muss
Und es gibt aber auch sowas wie O von Login,
das werden wir uns nächste Woche mal angucken, logarithmischer Aufwand,
also ich habe N-Elemente in meiner Sammlung und der Aufwand für eine Operation,
zum Beispiel das Suchen, kann sein,
dass es in logarithmischer Zeit möglich ist.
Das gucken wir uns nächste Woche an. Dann gibt es
so was wie O von N-Log-N, eine typische Komplexitätsklasse,
nämlich für gute Sortierverfahren. Oder O von N-Quadrat,
ein Quadrat ist so eine etwas unangenehmere Komplexitätsklasse,
kann man sich schon überlegen bei einer Liste von 100 Elementen,
brauche ich 10.000 elementare Schritte,
um die zu sortieren
Das ist schon relativ viel. Der Unterschied zwischen N
mal Login und N Quadrat, der kann gravierend sein.
Jetzt gucken wir uns in zwei Wochen mal genauer an,
wenn wir uns auch mal Sortierverfahren angucken.
Und dann gibt es noch so eine Komplexitätsklasse,
die auch sehr prominent ist in der Informatik,
nämlich sowas wie hier, wovon zwei hoch N,
wo das einfach tatsächlich exponentiell ist, der Aufwand.
Und das sind so ganz unangenehme Sachen.
Das sind harte Probleme. Die sind in der Informatik,
da gibt es genügend von. Das heißt,
bei einer Liste von 100 Elementen ist für eine bestimmte Operation,
das ist jetzt ein schlechtes Beispiel bei Listen,
wäre das dann
200, das sind verdammt große Zahlen.
Da könnt ihr euch vorstellen, das ist also wirklich gigantisch.
Das ist die Komplexitätsklasse spätestens, die euch komplett demütig macht,
egal was für einen Rechner ihr habt.
Jeder super Computer dieses Planeten wird für solche Probleme ab einer
bestimmten Größe tausende Millionen von Jahren rechnen.
Das ist einfach unangenehm. Es gibt also unangenehme Probleme,
die sind so schwierig, dass sie mit Computern nur mit
sehr großem Zeitaufwand gelöst werden können.
Also solche Grenzen solltet ihr auch kennen.
Deswegen ist Komplexitätstheorie auch ein wichtiges Thema
Kommt alles ausführlicher in FG1. Hier jetzt nochmal diese Begrifflichkeiten,
die ich jetzt gerade mal so ad hoc eingeführt habe,
abgebildet auf das, was wir gerade so uns angeguckt haben.
Also bei einer verketteten Liste ist das Einfügen, hat O von N.
Linearer Aufwand, weil ich ja bis zu N-Elemente durchlaufen muss.
Und bei einer Air-Ray-List ist es auch O von N,
linearer Aufwand, da bis zu N-Elemente verschoben werden müssen.
Das gibt sich also nicht viel. Zugriffe von Element über einen Index,
da ist es eben halt tatsächlich so,
da spielt die Arrailist ihre Stärke aus,
das ist O von 1. Amortisiert O von 1,
weil natürlich, es kann sein, ach ne,
genau, O von 1,
also der Zugriff ist wirklich sehr schnell
Wenn das ein Index passiert ist. Während bei einer Linklist
muss ich halt immer wieder erstmal dahin laufen zu der Stelle.
Also O von N. So, der Test auf enthalten sein,
das ist etwas, was wir uns nächste Woche ansehen werden.
Das ist etwas, was man häufig braucht, bei Listen weniger.
Wir haben es bei Listen auch schon hiermit implementiert.
Bei Mengen ist es viel wichtiger.
Ihr habt ja eben schon gesehen bei der iTunes-Geschichte.
Es muss ja irgendwie festgestellt werden, ob es schon drin ist.
Und dieser Testaufenthaltensein, der kann ziemlich teuer sein.
Der ist üblicherweise bei einer Liste O von N.
Es gibt aber
sehr clevere Implementationen
Kann man das deutlich besser machen,
nämlich auch von Login oder sogar tatsächlich mit konstantem Aufwand.
Ich kann also konstant schnell beantworten,
ob in einer Menge von Elementen ein Element schon vorhanden ist.
Das ist richtig cooles Zeug. Das gucken wir uns nächste Woche an.
Das ist die Zusammenfassung, was wir heute gemacht haben.
Wir haben uns heute also angeguckt, wie man Listen implementieren kann.
Zwei Möglichkeiten, es gibt mehr Möglichkeiten in der Informatik,
verkettete Listen und wachsende Arrays.
Verkettetlisten können einfach doppelt verkettet sein, wachsende Rays basieren auf Arrays.
Da gibt es noch die O-Notation und wir haben noch ein
bisschen Storesi für heute, wer noch da bleiben will.
Ich kann hier nochmal eben eine kleine Abstimmung machen
Es ist eins, Vorleson, 11. Hier eine, wie ich finde,
relativ einfache Frage. Welche der vorigen Aussagen ist korrekt?
Eine Linklist ist schneller als eine Arrailist. A,
beim Einfügen in der Mitte einer großen Liste,
beim Einfügen am Anfang einer großen Liste,
beim Zugriff auf ein Element in der Mitte einer großen Liste,
also beim Zugriff oder D beim Suchen eines Elementes in einer großen Liste.
Wo ist eine Linklist ziemlich stark vermutlich schneller als eine Arraylist.
Nach dem, was ich jetzt heute erzählt habe,
wenn ihr die ganze Zeit dabei gewesen seid,
ist das eigentlich eine leicht zu beantwortende Frage.
Ja, die klackern auch hier schon massenweise rein.
Wir haben schon wieder über 100 Antworten. Großartig. Entscheidet
euch jetzt. Ja, genau, 125 Stimmen gleich.
Prima. Gut, ich halte mal an.
Dann gucken wir erstmal auf die Verteilung
und siehe da
Eine relativ große Mehrheit ist der Meinung, beim Einfügen am Anfang einer großen Liste.
Aber es gibt auch noch, die sagen, beim Einfügen
in der Mitte einer großen Liste. Korrekt ist natürlich diese Antwort,
weil nur beim Anfang ist es wirklich so,
dass die Arrailis deutlich schneller ist,
weil sobald man in die Mitte rein muss, der Liste,
muss ich ja wirklich linear durchlaufen.
Und dann ist die Arrailis beim Hinlaufen langsam. Quatsch,
die Linkliste beim Hinlaufen langsam, wie bei der Arrailis
muss ich halt relativ viel verschieben.
Aber das ist sozusagen der einzige Punkt,
wo man offensicher sein kann, da wird die Arrailis
immer schneller sein.
Gut, einen habe ich noch
Welche der folgenden Aussagen ist richtig? A, immer wenn,
ich mache nochmal, starte das schon mal.
Immer wenn bei einer Arrailist die Kardinalität gleich der Kapazität ist,
muss die Kapazität vergrößert werden. B, das Einfügen am
Ende erfordert bei einer Arrailist immer denselben Aufwand. C,
wenn bei einer Arrailist beim Entfernen von Elementen die Kapazität niemals verkleinert wird,
kann bei einer sehr dynamischen Liste viel Speicherplatz unnötig belegt sein.
D, im Java-Clections-Framework implementieren sowohl Linklist als auch Arrailist
das Interface-Set
So, welche von diesen Aussagen ist richtig? Es ist
nur eine richtig. Also nicht welche der folgenden Aussagen sind richtig,
sowas gibt es ja auch in unseren Tests,
aber das ist jetzt hier nur eine
Gut, wir haben über 100 Stimmen. Wir sind
auch mit der Zeit schon eigentlich, wir haben da eigentlich
noch ein bisschen, aber ich halte es mal an.
Toll. Wieder 127 Stimmen. Okay, da ist es
nicht mehr ganz so eindeutig. Da sind immerhin ein Fünftel der Meinung,
weil wenn bei einer Averalyst die Kardinalität gleich der Kapazität ist,
muss die Kapazität vergrößert werden.
Immerhin zehn Prozent sind der Meinung,
das Einfügen am Ende erfordert bei einer Averalyst immer denselben Aufwand.
Okay, die richtige Antwort ist diese hier.
Also die Mehrheit ist richtig. Das ist so ein klassischer
klassisches Missverständnis hier. Immer wenn bei einer Arraylist, die kanalisiert,
gleich der Kapazität ist, dann muss nicht die Kapazität vergrößert werden.
Erst wenn ich was einfüge, dann muss ich vollgrößern.
Die können gleich sein. Und am Ende bei einer Arraylist
immer denselben Aufwand, nein, es kann ja sein,
dass das Ding voll ist, dann muss es vergrößert werden.
Vielen Dank für heute.