4, 3, 2, 1,
14.15 Uhr in Deutschland. So. Sie sehen die E-Mail,
die Christian Späh heute an Fred und mich geschrieben hat.
Ich zeige die ihnen jetzt einfach.
Sie wissen, was sie jetzt zu tun haben
So, im Großen und Ganzen,
also Fred und ich haben ja 14 Mal die Möglichkeit,
zweimal, also heute noch und dann noch zweimal und wahlweise sie zu begeistern,
uns bis auf die Knochen zu blamieren.
Sie haben genau eine Chance. Also uns zu begeistern bzw. sich,
bis sie auf die Knochen zu blamieren. Ich hoffe,
das erste, doch ich bin eigentlich zuversichtlich,
dass also 90 Prozent von ihnen das schaffen, naja,
vielleicht nicht uns zu begeistern, aber Doch, also,
jedenfalls das Zweite zu vermeiden.
So, das steht, das ist, glaube ich,
ein Abzug aus Stine, also soll keiner daherkommen und sagen,
ich habe es nicht gewusst.
Doch, doch, doch, doch, sie hätten das wissen können.
Das ist eine Klausur am Ende dieser Veranstaltung gibt.
Am Ende dieser Veranstaltung sind wir. Wir sind noch nicht ganz angekommen,
dieses Vorlesung 12. Letztes Jahr haben diese Zeit geprägt von furchtbarsten Geschichten,
die in Paris gelaufen sind,
anlässlich dieses Jahrestages
Mir erlaubt, also das Update von Jess wie Charlie
hier kurz in die kurz zu zeigen.
Also ich weltanschauliche Neutralität ist, glaube ich, davon unberührt. Ich hoffe,
dass jeder von uns, jeder, der hier anwesenden,
jeder, der uns draußen an den Fernsehschirmen verfolgt,
mit mir einer Meinung ist.
Also wir sollten unser Möglichstes tun,
dass diese Welt nicht zu einem Irrenhaus wird.
Ich versuche auch im Rahmen dieser Vorlesung mein Möglichstes dass das so ist.
So, der programmatische Wahnsinn geht weiter. Grundlagenobjektorientierter Programmierung heute,
werden wir uns hier anderthalb Stunden lang mit sogenannten dynamischen Datenstrukturen beschäftigen.
Ganz dynamisch. Wo die dynamische Datenstruktur ruft,
fragt man sich natürlich gleich, wieso dynamisch,
was ist daran jetzt so besonders dynamisch?
Was ist das Gegenteil? Das Gegenteil sind natürlich
statische Datenstrukturen. Erstmal so der Unterschied zwischen statisch und dynamisch
Letzte Woche haben wir so eine statische Datenstruktur kennengelernt, das Array.
Das ist deswegen statisch, lang andersrum. Das ist nicht deswegen statisch,
weil die Elemente, die in ihm enthalten sind,
unveränderlich sind, sondern das Array wird als statische Datenstruktur bezeichnet,
weil die Anzahl der Elemente unveränderlich ist.
Nach Erzeugung hat das Ding eine konstante Anzahl von Elementen. Das ist für viele Anwendungen,
so wie wir letzte Woche darüber gesprochen haben, ist,
dass eine durchaus angemessene Form der Verwaltung von Daten Es gibt viele Anwendungen,
oder es nicht angemessen ist.
Dafür braucht man dynamische Datenstrukturen und so,
wie sie das auf der Folie auch sehen,
dynamische Datenstrukturen sind insgesamt dadurch gekennzeichnet, dass die Anzahl der Elemente,
die in ihr ihnen enthalten sein können, während der Laufzeit eines Programms veränderlich sind.
Das ist ja, da werden wir,
sie im Laufe ihres Studiums, im Laufe ihres Berufslebens werden da,
also Anwendungen bis zum Abwinken finden,
im Rahmen dieser Vorlesung werden wir uns mit unterschiedlichen Arten von
dynamischen Datenstrukturen beschäftigen
Weil, wie sie sehen werden, also für die
unterschiedlichen Anwendungsfälle, für die sie hier ausgebildet werden.
Also sollen sie über möglichst viele Schubladen verfügen,
in die sie greifen können, um also sagen zu können,
ich sehe hier ein Problem von der Natur X oder Y
oder sonst Y oder Z. Und dafür scheint mir diese Datenstruktur eine geeignete Variante zu sein,
um dieses Problem softwaretechnisch zu lösen.
So, das, was Sie hier noch sehen,
hinzufügen, modifizieren, löschen von Elementen,
das sind alles keine Unterschiede zu den statischen Datenstrukturen
Das ist bloß, wir werden da jetzt eben im Einzelnen
deswegen auf diese Art des Umgangs, werden wir kaum noch,
also werden wir eigentlich überhaupt nicht mehr eingehen,
außer dass sich noch eine Erinnerungsfolie zeigen werde.
Das, was den eigentlichen Unterschied ausmacht,
ist die Art und Weise, wie wir diese Datenstrukturen implementieren können.
Und das ist, ob dann das, womit wir uns heute beschäftigen. So,
jetzt habe ich schon davon gesprochen,
dass es also unterschiedliche Arten von dynamischen Datenstrukturen gibt und sie sehen jetzt so auf dieser Folie so übersichtsartig grafisch dargestellt,
eigentlich ziemlich umfassende Darstellung dessen, dieser unterschiedlichen Arten von Datenstrukturen,
mit denen also sie sich, sie sich,
wie er uns überhaupt beschäftigen können.
Das kann man auf so dramatisch unterschiedliche Art und Weise tun,
dass es also auch eine ganze Reihe von Lehrveranstaltungen gibt in ihrem Studium,
die sich also mit diesen Datenstrukturen auf jeweils unterschiedliche Art und Weise beschäftigen.
Unser Schwerpunkt hier liegt darauf, dass sie das Handwerkszeug vermittelt bekommen,
um also solche Datenstrukturen geeignet implementieren zu können. Gehen wir die mal kurz durch,
die Menge Ihnen ja von der Art des Umgangs her,
was man damit tun kann, wozu die gut ist,
ist ihnen ja so, ist ihnen bekannt.
Diese Datenstruktur ist insofern jetzt mit Blick auf das,
womit wir uns jetzt im Weiteren beschäftigen, speziell,
als dass es keine Beziehung gibt zwischen den Elementen einer im Engel,
die wir in irgendeiner Form zu beachten haben.
Das unterscheidet sie. Dann schon gleich,
also von der anderen Datenstruktur, sagen wir mal,
die nächst einfachere Datenstruktur, die Liste,
wo also die Struktur zwischen den Elementen dadurch gekennzeichnet ist,
dass es eine Nachfolger-Relevanz,
Nachfolger beziehungsweise Vorgänger-Relation zwischen den einzelnen Elementen einer Liste gibt
Jetzt kommen die Datenstrukturen, oder sagen wir mal,
ausgezeichneterweise die Datenstruktur, die so eher metaphorischer Natur sind,
so wo man sich schon mal auch so als Softwareentwickler darüber im Klaren sein muss.
Und das, was wir tun, wir als Softwareentwickler,
ist, wir brauchen eine tragfähige Modellvorstellung davon,
was wir denn da jetzt eigentlich implementieren wollen,
die brauchen wir für uns,
aber insbesondere auch im Umgang mit anderen Softwareentwicklern,
wenn wir also davon sprechen, oder wenn Sie davon sprechen,
ich möchte ein bestimmtes Problem dadurch lösen,
dass ich die Datenstruktur Baum Wende.
Dann ist das, ist da ja was anderes gemeint,
als wenn ich meinen Sohn frage, warum er dieses und jenes tut.
Und dann sagt er halt als Antwort, weil bauen.
Damit meint er nicht die dynamische Datenstrukturbaum. Der Baum in der Softwareentwicklung ist dadurch gekennzeichnet,
dass es zwischen einem Vorgänger-Element und einem Nachfolger-Element oder einer Menge von Nachfolger-Elementen,
dass es eine hierarchische Beziehung gibt. Also nehmen wir mal dieses Element hier,
das hat genau einen Vorgänger und es hat mindestens, nein,
nicht mindestens, soll ich sagen, es gibt auch Blätter an diesem Baum,
die sind dadurch gekennzeichnet, dass sie keinen Nachfolger haben.
Ansonsten kann ein Baumknoten ein oder mehrere Nachfolgeelemente haben.
Insgesamt ist dieses Ding hier aber streng hierarchisch organisiert. Das heißt also,
ich habe niemals, also mehr als einen,
ich habe immer genau einen Vorgängerknoten,
während das bei einem Graphen sehr viel liberaler ist,
da kann ich also sowohl mehrere Nachfolgerknoten haben, als auch,
man kann zum Beispiel solche zyklischen Strukturen hier innerhalb eines Graphen verfolgen und das bedeutet,
Ich habe auch eine, möglicherweise größere Anzahl von Vorgängerelementen.
So, diese unterschiedlichen Arten von dynamischen Datenstrukturen, also hiermit
kann man dann zum Beispiel Familienbeziehungen, kann man hiermit gut modellieren.
Hiermit kann man zum Beispiel das Problem des Reisenden,
des Handlungsreisenden, also der möchte dann dessen Aufgabenstellung besteht,
darin, möglichst viele Orte mit möglichst geringem Aufwand besuchen zu können.
Dafür eignet sich die Datenstruktur Graf besonders gut. Sie werden
diese Datenstrukturen jetzt kennenlernen und wie man die unserem Fall in Java,
könnte aber auch jeder andere Programmiersprache sein, wie man die implementiert.
Solche Modelle, solche Modelle werden sie noch so,
das ist jetzt ein Beispiel der Modellbildung anhand von Datenstrukturen,
aber sie werden noch sehr viele weitere Modelle dieser Art kennenlernen.
Heute beschäftigen wir uns mit der Implementation von Listen,
also die einfache Datenstruktur, die dadurch gekennzeichnet ist,
dass zwischen den Elementen dieser dynamischen Datenstruktur eine sequenzielle Beziehung Ich bin sequenzielle Beziehung, besteht.
Und das ist so die Grundlage, um sich mit anderen sequenziellen Datenstrukturen zu beschäftigen.
Da sind namentlich jetzt schon zwei Datenstrukturen, sind da schon genannt.
Und es freut mich dann immer wieder,
wenn ich diese Lehrveranstaltungen vorlese oder im Rahmen dieser Lehrveranstaltung vorlese,
dass ich ihnen derjenige bin, der ihnen die Freuden von Stacks und Cues,
also Kellern und Schlangen nahelegt.
Also sie haben in dieser Vorlesung jetzt bereits schon Bäume als Metapher
gehört und Keller und Schlangen, sind also weitere Datenstrukturen,
mit denen wir uns noch im Rahmen der Veranstaltung
beschäftigen werden
Das soll doch mal einer sagen,
dass die Metapher im Alltag des Programmierens keine Rolle spielt.
Haben Sie die ersten Beispiele dafür, wozu Metaphern gut sind?
Okay, so, wir werden uns zwei Implementationsformen anschauen für Listen.
Also für diese sequenzielle Struktur. Also zwei Arten,
wie man dieses nach außen hin sichtbare Verhalten,
das ich gerade kurz skizziert habe,
wenn ich es gleich nochmal zeigen werde,
wie man das implementieren kann.
So eine basiert auf Verkettung, also die einzelnen Listenelemente werden
miteinander verkettet.
Diese Art von Implementation
Kennen sie noch nicht. Und die andere Implementation basiert auf
Race. Das ist also die Datenstruktur,
die sie im Rahmen der letzten Vorlesungen kennengelernt haben.
So, mal gucken, was jetzt passiert.
Okay, das nächste Mal mache ich das Mikro aus.
Was ist das noch Natur? Entschuldigung. So, wie
angekündigt, nochmal kurz. So, was heißt das mit
einer Liste umzugehen? Im objektorientierten Objektorientierten Sinn. So,
man kann also Elemente in eine Liste einfügen und man kann Elemente entfernen und der Effekt,
ihr besteht jeweils darin,
wenn ich ein Element in einer Liste einfüge,
dann wird der Index der nachfolgenden Elemente um eins erhöht und beim Entfernen ist das das Umgekehrte Verhalten.
Was kann ich sonst noch mit einer Liste tun? Nun,
ich kann vor allem, das ist also eine Eigenschaft aller Listen,
mit denen wir uns oder jeder Liste, mit der wir uns beschäftigen.
Ich kann schauen, nachfragen, ich kann bei der Liste
nachfragen, ob ein Element bereits in ihr enthalten ist.
So, das behalten Sie jetzt einfach so im Hinterkopf,
während wir über die Implementation sprechen
Wir werden, so im Weiteren werden wir nochmal gegen Ende dieses Vorlesungsteils,
werde ich nochmal darauf eingehen, wie aufwendig ist das jeweils.
Diese Operationen zu implementieren, dann werden sie sehen, sie sind,
das haben sie wahrscheinlich jetzt in ihren Übungen auch schon kennengelernt,
also sie treffen ständig Entscheidungen, also wie etwas implementiert werden soll.
Und das ist diese Entscheidungen, treffen sie so hoffentlich,
ich werde mich da jetzt nicht weiter, werde nicht weiter drüber spekulieren.
Diese Entscheidungen treffen sie bewusst, sie sollten diese Entscheidung jeweils motivieren können.
Und gerade die Wahl, also der Implementation einer Liste,
ist eine Entwurfsentscheidung, die wohl begründet werden kann,
unter anderem damit, wie aufwendig ist es, die Implementationen,
die auf der Liste aufgerufen werden, durchzuführen.
Das kann nämlich ganz schön unterschiedlich sein. Und wenn es darum geht,
also die Effizienz eines Programms zu beurteilen,
also die Laufzeiteffizienz eines Programms, also auch zu prognostizieren,
dann gehört es zu ihrem Handwerkszeug, dass sie auch sagen können,
wie oder dass sie berechnen können,
wie effizient ist eigentlich die Implementation,
die ich gewählt habe im Vergleich zu einer anderen möglichen Implementation.
So. Zwei Arten von Implementationen habe ich gesagt, schauen wir uns an.
Einmal die Fakett-Implementation als Verkettung und einmal die Implementation unter Verwendung eines R-Rays.
Bei der Verkettung werden wir wiederum zwei Arten unterscheiden.
Einmal die einfach verkettete Liste und einmal die doppelt verkettete Liste.
Wodurch ist die einfach verkettete Liste gekennzeichnet
Ja, ich meine, das ist an Einfachheit, kaum zu überbieten,
jedes Listen-Element hat eine Referenz, Achtung, den Begriff,
der bringt bei Ihnen jetzt auch schon was zum Klingeln.
Jedes Referenz hat, jedes Listen-Element hat eine Referenz auf ein Nachfolger-Element.
Jetzt können Sie ja gleich sehen,
wenn ich aus dieses Element hier zugreifen möchte, ist,
es gibt keinen anderen Weg, als also von vorne,
element, elementweise durch die Liste zu gehen,
bis man bei dem Element angekommen ist,
zu dem man eigentlich hin wollte
Das Motiv dafür ist völlig irrelevant. So,
das ist die einfach verkettete Liste und die andere,
die Art von Implementation, die wir noch kennenlernen werden,
ist jetzt nicht die zweifach verkettete Liste, sondern die nennen wir doppelt verkettete Liste.
Da hat sich jemand überlegt, es ist also um möglicherweise den Suchaufwand.
Wenn ich schon so eine Idee davon habe,
an welchem Ende der Liste befindet oder sagen wir mal
gegen welches Ende der Liste befindet sich möglicherweise das Element,
das ich suche,
ist es doch vielleicht eine gute Idee,
wenn ich nicht nur in diese Richtung durch die Liste gehe
Kann, sondern ich habe auch eine Vorgängerrelation und dann kann ich möglicherweise auch vom Ende der Liste her,
wo jetzt hier Anfang und Ende ist,
also im Sinne der Betrachtung völlig egal,
dann kann ich jedenfalls auch durch die andere,
in der anderen Richtung, also in diese,
durch diese Liste traversieren, wie das technisch,
wie das technisch heißt.
So, das sind jetzt so die beiden Modellvorstellungen,
so bis jetzt habe ich ja noch keine Zeile Code gezeigt oder auch nicht gezeigt,
also wie das in Java realisiert werden könnte.
Diese beiden Modellvorstellungen einer einfachen, einer doppelt verketteten Liste,
die haben sie jetzt so weit verinnerlicht.
Jetzt kommen wir dazu, wie können diese Modellvorstellungen unter Verwendung,
so das bisher vermittelten Gedankengut zur Objektorientierten Programmierung eigentlich implementiert werden.
Wir sind immer noch entfernt von Java. Es geht einfach nur darum,
jetzt, dass wir so eine Grundidee davon entwickeln,
wie Listen im Falle der Verkettung dieses Mal,
wie die eigentlich implementiert, also prinzipiell implementiert werden sollen.
So, das hier ist die Ihnen bekannte Liste,
also mit ihren Operationen einfügen,
entfernen,
ist das Element enthalten
Die Grundidee dieser Objektorientierten Entwurfs einer Liste besteht jetzt darin,
dass sie einzelne Kettenglieder implementieren und diese Kettenglieder sind dafür zuständig,
dass sie das Traversieren durch die Liste ermöglichen.
Für den Moment, so sollten sie auch festhalten,
dass die Liste, nämlich so das,
was nach außen hin einem Klienten sichtbar ist,
dass die jetzt nochmal unterschieden wird von, also der Realisierung der einzelnen Kettenglieder.
Und die wiederum, also haben nichts mit dem zu tun,
was in dieser Liste eigentlich verwaltet wird, nämlich den Elementen.
Also das, wofür wir diese Liste, diese Sammlung,
diese dynamische Datenstruktur eigentlich realisieren. Wir haben also konzeptionell unterscheiden wir diese drei,
diese drei Bestandteile eines in einem Objekt orientierten Entwurf einer Liste,
die Liste selbst, die realisierenden Kettenglieder und die Verwalt,
die durch die Kettenglieder verwalteten Elemente.
Wie kann jetzt ein Kettenglied aussehen und jetzt gleich werde ich einen Begriff verwenden, der,
der das ein oder andere Nackenhaar wahrscheinlich in eine horizontal,
nee, aufrecht in eine vertikale Richtung bringen wird.
Wir werden die, für die Konstruktion eines Kettengliedes
ein Mittel verwenden, nämlich strukturelle Rekursion. Weil das,
was wir zur eines zur Realisierung von Kettengliedern brauchen,
ist, dass Kettenglieder auf sich selbst verweisen können.
Und das tun sie, indem sie zum Beispiel,
so wie sie das hier sehen, eine Verkettungsreferenz in dem Kettenglied enthalten haben.
So, das Kettenglied, das hier ist jetzt so ein UML-Objektdiagramm,
dieses Kettenglied enthält also eine Referenz auf ein Element.
Das ist so ein Beispiel, hier ist es eine Person.
Und sie enthält eine Referenz auf ein weiteres Kettenglied.
Und das ist dann wieder ein Objekt der Klasse, Kettenglied.
Und so kann das dann in Java auch mal hingeschrieben werden,
damit dieses Geheimnis auch mal gelüftet wird. Also so eine Klasse Kettenglied,
die besteht, also aus mindestens zwei,
jetzt so in der Datenseite her betrachtet, aus zwei Exemplarvariablen,
nämlich der Referenz auf ein weiteres Kettenglied, nämlich das nächste,
das ist jetzt für die einfach verkettete Liste und aus einer Referenz auf einen zu verwaltendes Element.
Also das, was eigentlich in der Liste in dieser dynamischen Struktur,
in der dynamischen Datenstruktur verwaltet werden soll.
Und das ist jetzt so vom, ich habe den Begriff schon genannt,
der strukturellen Rekursion. Das ist deswegen strukturell rekursiv,
weil für die Definition eines Kettengliedes wird auch ein Kettenglied verwendet.
Das ist, ich hoffe, das ist halbwegs nachvollziehbar,
dass Kettenglieder deswegen, also Kettenglieder, verwalten müssen,
weil sie ja ihre Nachfolger und im Falle der doppelt verketteten Liste auch noch einen,
hätten sie eben noch ein zusätzliches strukturelles Element hier,
nämlich ein Kettenglied namens Vorgänger.
So, so ist dann die verkettete Liste, jetzt mache ich das,
was ich Freue mich schon auf das Sommersemester, die Allergien.
So, also so sieht dann diese verkettete Liste vom objektorientierten Entwurf
her aus. So, und jetzt sehen Sie,
das ist jetzt ein grafisch angereichertes, angereichertes UML-Objektdiagramm,
weil in dem Objektdiagramm sind die Objekte ja eigentlich alle gleich groß,
werden alle gleich groß dargestellt.
Das ist jetzt ein künstlerischer Aspekt,
dass die dass die Liste viel größer ist als ihre Kettenglieder.
Der eine Punkt, der aber an der Stelle nochmal,
den ich nochmal betonen möchte, die Liste ist nicht gleich der Elemente,
die in ihr enthalten sind.
So wie das da jetzt entworfen worden ist,
kann ich die Liste löschen, dann ist die Liste weg.
Das bedeutet aber nicht, dass die in der Liste
enthaltenen Elemente weg sind
Das wundert einen doch jetzt erst mal, oder? Sagen wir mal,
wenn ich einen Ordner,
ich stelle mir jetzt mal so einen Ordner vor,
wirklich so ein Ding,
was ich so in die Hand nehmen kann,
wenn ich den wegwerfe, dann ist ja nicht nur der Ordner weg und dann auch der Inhalt des Ordners.
Weg. So wie das da jetzt entworfen worden ist,
also bedeutet das Wegwerfen eines Ordners,
ich werfe die Hülle weg,
aber den Inhalt,
den behalte ich,
den muss ich
getrennt wegwerfen
Ich sage Ihnen das jetzt nur,
damit so diese Modellvorstellung des Trennens von Kettengliedern, Elementen und Listen,
dass Sie jetzt nicht nur denken, das ist eine Entwurfsentscheidung,
die kann man so treffen oder das kann man auch sein lassen,
dass diese Entwurfsentscheidung eben auch mit Konsequenzen verbunden ist.
In diesem Fall eben die Unabhängigkeit von Elementen,
Kettengliedern und Liste bedeutet, ich muss mich um alle drei selbstständig kümmern,
was insbesondere beim Entfernen von etwas beim Löschen von etwas, also mit dem gerade beschriebenen Effekt daherkommt.
Man tut das im Übrigen, um das auch nochmal zu sagen,
so wer weiß denn, also ob dieses Element hier,
das jetzt zufällig als erstes in dieser Liste enthalten ist,
das kann ja sonst was, also kann das ja noch referent,
sonst wo kann das ja noch referenziert werden,
das dann zu löschen, kann ja zu Seiteneffekten alias Effekten,
wir haben darüber auch schon gesprochen,
an ganz anderen Stellen im Programm führen und das ist etwas,
was man ja eigentlich vermeiden möchte.
Also dieser Entwurf hier trägt dieser Problem Rechnung. So,
jetzt kommen wieder so ein paar, also Kartoffeldruckartige,
Animationen, die wir liebevoll für Sie,
also die Vorgänger von mir in dieser Veranstaltung, liebevoll für Sie hergestellt haben.
Jetzt haben wir mal so, was wir tun wollen,
was ich Ihnen zeigen möchte, ist,
wie wird etwas in eine verkettete Liste eingefügt,
und zwar so an irgendeiner Stelle dieser verketteten Liste.
Und der Ausgangspunkt ist, es gibt also so ein neues Element,
das haben wir bereits an anderer Stelle, haben wir das erzeugt,
dieses Element.
So, der nächste logische Schritt besteht dann darin,
dass wir ein Kettenglied erzeugen
Dieses Kettenglied, müssen wir dann die Referenz auf das erzeugte Element,
das war ja dieses hier, müssen wir dann eintragen.
Und jetzt müssen wir die Stelle suchen, an der wir
dieses neu erzeugte Kettenglied in die lineare Liste einfügen.
Das bedeutet, wir müssen vom Listenkopf ausgehend, müssen wir
die Stelle finden, in die wir uns einsortieren wollen.
So, das ist jetzt hier schon angedeutet.
Und jetzt müssen wir die neue Verkettung herstellen.
Und das ist führt so manchmal zu Verknotung im Kopf,
diese Stelle zu finden, beziehungsweise dann die Referenzen auch entsprechend umzuhängen.
Deswegen geben wir uns jetzt ja auch so viel Mühe,
ihnen das zu zeigen. So, wenn wir das jetzt,
dieses Element, dieses Kettenglied zwischen dieses und dieses Kettenglied hängen wollen,
dann befinden wir uns jetzt meinetwegen hier und wissen also,
diese Referenz zeigt auf das Kettenglied,
auf das dieses Kettenglied jetzt gleich zeigen soll.
Das bedeutet, wir müssen jetzt eine Referenz umhängen und zwar muss diese Referenz hier kopiert werden,
sodass sie jetzt auch auf dieses Element zeigt.
Und dass wir jetzt noch tun müssen, ist,
wir müssen diese Referenz so umhängen,
dass sie eben nicht mehr hierhin zeigt, sondern auf diese Referenz.
Und das... So, und schon haben wir ein
neues Element in diese verkettete Liste eingefügt.
Jetzt gucken wir mal da drauf, oder lassen Sie sich
das so kurz nochmal zu spulen, Sie mental zurück?
Wie teuer ist es jetzt gewesen, dieses Element an
der Stelle einzufügen
Ich sage Ihnen, das war ganz schön billig,
weil wir mussten insbesondere, mussten wir an keiner Stelle,
mussten wir jetzt, also dieses Element oder dieses Element anfassen.
So anfassen im Sinne von, wir müssen das irgendwie im Speicher verschieben,
so im RE einen neuen Index geben, also irgendwelche Elemente kopieren, konnten wir uns alles sparen. Das einzige,
was wir tun mussten, oder nicht das einzige,
die elementaren Schritte, die wir gegangen sind,
sehen Sie hier, wir haben halt, also das,
was so halbwegs aufwendig gewesen ist,
so halbwegs ist in dem Fall sogar,
also auch rechnerisch richtig, wir mussten die halbe Liste,
jedenfalls der Ausschnitt, den wir hier sehen,
mussten wir durchwandern, um die Stelle zu finden,
an der wir dann diese sehr billige,
kostengünstige Einfügeoperation durchführen konnten.
So, das Entfernen aus einer verketteten Liste ist noch einfacher.
Wir suchen also die Stelle, also die wir,
wir suchen das Element, das wir löschen wollen.
So, dann wird dieses Element gefunden haben,
das war jetzt ein bisschen schnell, das ist das Element,
das gelöscht werden soll, das besteht, löschen,
besteht ausschließlich darin, dass wir eine Referenz umhängen.
Also jetzt diese Referenz nehmen und sie hierhin kopieren.
Und dann haben wir dieses Kettenglied gelöscht. Dieses Kettenglied zu löschen. Wenn das das einzige,
also wenn die Referenz, die wir gerade umgehängt haben,
die einzige Referenz auf dieses Kettenglied gewesen ist,
dann sorgt also mindestens in Java dieses Gerät namens Garbage Collector dazu,
das im Hintergrund unauffällig mitläuft,
dafür,
dass der Speicherplatz dieses Objektes
auch wieder freigegeben wird
Und darum braucht man sich nicht zu kümmern.
Und dann ist also derselbe Prozess, schaut ja auch nach,
gibt es noch eine Referenz auf dieses Element?
Und wenn es keine mehr gibt, wird es gelöscht.
Und wenn es das noch an einer anderen Stelle gibt,
ich hatte den Unterschied zwischen Element, Kettenglied und Liste ja erläutert,
dann bleibt dieses Element erhalten.
Also das Löschen eines Elementes aus der Liste bedeutet nicht gleichzeitig
das Element gelöscht wird. Club. Weg bist du.
So, das Klassendiagramm zeige ich Ihnen jetzt auch nochmal.
Und der Vollständigkeit halber
Okay. So, die Linked List, die eigentlich jetzt,
also strukturell aus nichts anderem besteht als einem Verweis auf,
also ihr erstes Kettenglied und das Kettenglied,
das aus einem Verweis auf das nächste Kettenglied besteht.
Deswegen dieser Pfeil und einem Verweis auf das zu verwaltende Element.
Die Linked List kennt dieses Element indirekt,
weil die Operation einfügen, ist dieses Element schon enthalten.
Ja, indirekt
Eine Kenntnis der Elemente auch erfordern. Und die direkte Verwendung findet dann
in den einzelnen Kettengliedern statt. So, das ist jetzt,
also so sich mit der doppelt verketteten Liste zu beschäftigen,
ist jetzt eine Variation über demselben Thema,
was hier eigentlich nur relevant ist, ist also das Java-Collection-Framework.
Deswegen steht es hier, der Linkedlist,
also der verketteten Liste basiert auf der doppelt verketteten Liste.
Warum auch nicht? Das ist also die, was das
vom Modell her bedeutet, hatte ich ja bereits erläutert.
Sie haben nicht nur die Nachfolgerrelevat Relation, bei den Kettengliedern implementiert,
sondern auch eine Vorgängerrelation und das ermöglicht halt das Traversieren einer Liste in zwei Richtungen.
Das, was sie, da kommt so der,
so eine gewisse Bösartigkeit bei mir durch,
wenn ich mich jetzt schon darüber freue,
sie tranversieren über Listen und das, was ihnen,
also hoffe ich jedenfalls auch passieren wird, ist,
sie tranversieren über das Listenende hinaus, das können sie natürlich nicht,
aber sie tranversieren weiter,
als sie wollen
Dann stellen sie fest, die sind am Ende der Liste
angekommen und haben also die Referenz auf den Vorgänger verloren und können jetzt nichts mehr machen.
Jetzt sind sie einfach am Ende der Liste angekommen. Dass sie sich einen anderen Algorithmus überlegen. Und es ist nun mal so,
nicht nur, also das, was man konzeptionell,
also dieses lästige, also dieses lästige,
so zu weit über zu weit gehen, um das zu vermeiden,
um dieses Problem zu umgehen bzw. einen für alle Mal gar nicht erst auftauchen zu lassen,
ist das, was die Softwaretechnik dafür, die Programmierung dafür erfunden hat,
sind sogenannte Wächter Objekte, die also den Anfang und das Ende einer Liste markieren.
Und wenn Sie mit solchen Wächterelementen umgehenden Sentinels,
dann können Sie die Anzahl der Behandlung von Sonderfällen.
Können Sie dadurch minimieren? Das kann man jetzt tun,
das erhöht also die Sicherheit, also bei Programmen,
bei Programmen, wir werden allerdings auch noch,
also erstens werden wir noch weitere Techniken kennenlernen,
mit denen also sichergestellt werden kann,
dass solche Programmierfehler minimiert werden
Und zum anderen sollte man sowieso bestrebt sein,
also solche Basisoperationen so schnell wie möglich zu verlassen und sich dann nur noch mit,
also so etwas zu beschäftigen,
wie also dem objektorientierten Konzept einer Liste, das hat man halt,
also da braucht man sich mit dem Traversieren,
dem eigentlichen Travisieren auch nicht mehr beschäftigen,
weil dann sagt man Einfügen eines Elementes
Entfernen eines Elementes und das war's dann und das Traversieren,
das muss halt einmal so implementiert werden,
dass es dessen Korrektheit
nachgewiesen werden kann
So, ich wollte jetzt noch,
jetzt ist jetzt die Implementation als verkettete Liste, ich habe gesagt,
also dass wir die andere Implementation jetzt noch dagegenstellen,
dass wir das, es wird noch uns anschauen,
wie denn unter Verwendung eines Arrays, das kennen sie ja schon,
wie man unter Verwendung eines Arrays all das Konzept der Liste implementieren kann.
Die Grundidee ist also einem Klienten, soll so so eine Array-Liste so zur Verfügung stehen,
dass es für ihn eigentlich egal ist, also nachrangig ist.
Also ob dahinter, so wie in diesem Fall,
ein Array als Implementierungsvariante gewählt worden ist oder ob eine verkettete Liste gewählt worden ist.
So, das sollte für den Klienten, also für den hier,
sollte das wirklich bedeutungslos sein, abgesehen davon,
dass also im Inneren andere Algorithmen implementiert worden sind und die dann auch ein unterschiedliches Laufzeitverhalten zeigen.
So, die Datenstruktur Array ist ja,
das hatte das Ganze am Anfang ja gesagt,
also dass es eine statische Datenstruktur,
kann man also überhaupt auf die Idee kommen,
dass eine statische Datenstruktur geeignet sein könnte, um so eine dynamische Datenstruktur,
wie also eine, wie eine Liste zu implementieren.
Und der Trick sind die sogenannten wachsenden Airrays und die wachsen nicht.
Also das ist jetzt auch mehr so eine,
so eine metaphorische, eine schiefe Metapher, sondern das,
was man sich hier als Idee als nachvollziehbare Idee hoffentlich,
was man hier wählt, ist, also naja,
wir gucken mal am Anfang, was könnte Pi mal Daumen,
so könnten 100 Elemente ausreichen.
Wir stellen irgendwann fest, huhu, es wird knapp.
Also erzeuge ich mir ein größeres Array, sagen wir,
200 Elemente und dann kopiere ich alles rüber. Das muss ich dann,
also diesen Preis muss ich dann bezahlen,
wenn ich also eine, so weit zugrunde liegend,
ist halt diese statische Datenstruktur und das ist in Java so,
wenn die einmal mit einer bestimmten Größe erzeugt worden ist,
dann bleibt das auch so.
Wenn ich diesen Algorithmus oder dieses Verfahren hier wähle,
dann muss ich zwei Konzepte unterscheiden,
nämlich zwei,
ja,
zwei Konzepte unterscheiden
Die logische Größe einer Liste,
das ist die Kardinalität und die physikalischen Größe, also das,
was tatsächlich drin sein kann, das ist die Kapazität.
Die Kapazität muss natürlich immer größer gleich sein der Kardinalität einer Liste.
So, das fällt mir jetzt gerade auf,
ich glaube, der Plural von Index ist irgendwie anders.
Ich hätte da jetzt Indie-Cs hingeschrieben,
aber das ist jetzt nur so auf der Tonspur.
So, jetzt kriegen sie wieder Kartoffeldruck
Was bedeutet das auf ein wachsendes Array zuzugreifen? Hier steht schon so,
dass die Antwort auf die Frage, die ich nicht gestellt habe,
also der Zugriff, der geht schnell.
Das ist ja klar. Ich kann hier,
also wahlfrei, anders als bei der verketteten Liste,
greife ich gleich auf das Element meiner Wahl zu.
Jetzt hatte ich schon angedeutet, dadurch,
dass ich das Einfügen so gelobt habe bei der verketteten Liste als billige Operation,
dass das beim Array, beim wachsenden Array wohl ein bisschen anders aussieht.
Und das kann ich Ihnen jetzt also auch demonstrieren.
Wir wollen also dieses Element einfügen Also,
was ich jetzt alles tun muss, nicht, hui.
So, ich muss also diesen ganzen Verschiebeoperationen durchführen,
um dieses Element X, an der für es vorgesehenen Stelle einzufügen.
Das ist also teuer und hier steht auch gleich drauf,
so im Extremfall müssen alle Elemente um eine Position verschoben werden.
Und das ist etwas, was man natürlich unbedingt vermeiden möchte
Ja. So, jetzt vergleichen wir diese beiden Operatoren,
diese beiden Implementationen, und die vergleichen wir jetzt nochmal,
mit Blick auf zwei typische Operationen,
nämlich einfügen und den Zugriff auf ein Element.
So, und jetzt können Sie sich das alles nochmal,
also so, was ich jetzt in den letzten 40 Minuten,
so vor mich hin, geredet habe,
kann man sich jetzt hier nochmal so eigentlich so zusammenfassend vor Augen führen.
Was ist passiert also beim Einfügen in eine
als Linked-List implementierte Liste
Naja, ich muss also erstmal traversieren, bis ich das,
bis ich die Stelle erreicht habe. Im Durchschnitt wird man
wohl von ausgehen können, wo sich die halbe Liste durchlaufen.
Objekt, ich muss für jedes, also jedes Objekt,
sodass in die Liste eingefügt wird, muss ich ja mindestens dieses Kettenglied erzeugen.
Das ist wahrscheinlich nicht so teuer, aber ich muss es tun. Andererseits,
das Einfügen ist, wie das hier steht, sehr einfach,
haben sie ja gesehen, vor allem im Vergleich zu dem,
also wie das bei der Array-List-Implementation geschehen ist,
braucht nur was umketten und das bedeutet einen konstanten Aufwand.
So, wie ist das Einfügen in eine Array-List
Okay, das ist, ich muss,
die halbe Liste möglicherweise umkopieren. Ich kriege zwar die Stelle,
also an der ich etwas einfügen möchte, möglicherweise sehr schnell,
aber ich muss also das umkopieren, was das kostet,
habe ich im Moment keine wirklich genaue Vorstellung.
Das kann ja bei größeren, es sind eigentlich nur Referenzen,
die umkopiert werden müssen.
Na gut, also das ist eben stark abhängig von
der Anzahl der Elemente
So, der größte Nachteil besteht wohl darin,
wenn ich so eine schlechte Abschätzung getroffen habe und ich möglicherweise
mehrfach alles in ein neues Array umkopieren muss, hui,
das könnte ganz schön teuer werden.
Das will dann eben abgewogen werden gegenüber dem Vorteil.
Die Position zum Einfügen kann direkt angesprochen werden. So,
einfügen wollte ich besprechen und ich wollte den Zugriff auf eine beliebige Position.
Besprechender der Zugriff, das ist ja völlig klar, bei der Arraylist,
Das ist also ein konstanter Aufwand, der ist gleich eins.
Und die Linked List, da muss ich halt durch die halbe Liste.
So, und jetzt kriegen sie von mir die Antwort,
die keiner hören will, wenn er eine Frage stellt,
also auf die er eine klare Antwort haben möchte,
welche Implementation soll ich denn jetzt wählen?
Und die Antwort heißt, das kommt drauf an.
Ist jetzt nicht meine, und damit lasse ich das jetzt
auch hier bei sich bewenden. Meine Aufgabe ist es nicht,
Ihnen zu sagen, nimm mal in 80 Prozent der Fälle,
die Linked List ist schon gut, 20 Prozent.
Also da muss man sich Gedanken drüber machen.
Ich habe Ihnen jetzt hier zwei verschiedene Arten von Implementationen vorgestellt.
Und ab jetzt ist es Ihr Problem, zu argumentieren,
welche der Implementationen Sie denn für angemessen halten.
Schließlich sind Sie ja die Experten, die wir ausbilden.
Ich stelle Ihnen ja hier nur, das sind die Möglichkeiten,
das sind die prinzipiellen Alternativen, die Sie zur Verfügung haben.
Und jetzt müssen Sie sich mal ihren eigenen Kopf machen.
Jetzt nur noch ganz kurz ein paar Bemerkungen zum Aufwand für Operationen
Ihr habt das ja jetzt eher auf der Ebene von ungefähr
die Hälfte der Liste, Objekterzeugung ist teuer, ist billig.
Das kann man auch formalisierter machen. Und das ist etwas,
womit wir uns hier im Rahmen dieser Lehrveranstaltung,
also aber auch wirklich nur,
wenn wir hier schon beim mathematischen Verfahren sind,
also damit werden wir uns hier nur tangenzial beschäftigen.
Das ist noch nicht mals eigentlich, tangenzial. Also wir
schrammen da mal so ganz an der Oberfläche dran vorbei.
Ich verspreche ihnen, sie werden Lehrveranstaltungen hören,
wo sie das in der Tiefe,
wo sie sich in der Tiefe noch damit beschäftigen werden
Hier nur mal ganz kurz das, was Sie prinzipiell unterscheiden müssen,
bei der Aufwandsabschätzung für solche Operationen ist der Konstante und der variable Teil des Aufwandes.
Der konstante Teil jetzt, Achtung, der ist konstant und
der Variableanteil des Aufwands, der ist variabel.
Und was das bedeutet, also an dieser unfassbar komplexen Definition,
ist, also sehen Sie hier am besten an diesem Beispiel,
beim Einfügen ist das der Aufwand für Erzeugen und Verketten ist immer gleich.
Das, was sich halt unterscheidet, ist, also der Aufwand,
den es braucht, um also zu dem Element zu kommen, das ich brauche.
Und jetzt kann man sagen, weiß man halt nicht.
Also brauche ich durchschnittlich, also die Hälfte ist das der,
es ist der normale Fall, der Durchschnittsfall.
Und jetzt gibt es so unterschiedliche Methoden, unterschiedliche Herangehensweise,
diesen Auf- und abzuschätzen. Es gibt so diese Worst-Case-Szenarien.
Es gibt so, es gibt so Mittelwerte. Das muss aber typischerweise,
also das, Den größten Einfluss darauf hat,
also welchen Aufwand sie zu erwarten haben, sind die zu erwartenden Datenmengen.
Wenn sie von kleineren Datenmengen ausgehen können,
und das können sie, wenn sie die Anwendungsfälle kennen,
mit denen sie sich beschäftigen, dann hat das,
also andere wird das andere Entwurfsentscheidungen nach sich ziehen,
als wenn sie von sehr großen Datenmengen ausgehen.
Und diese Abhängigkeit von den Datenmengen lässt sich, also lassen sich
auch solche Aufwandsfunktionen dann berechnen. Und diese Aufwandsfunktionen geben ihnen,
dann wiederum ein Hinweis darauf, also ob eine bestimmte,
ob eine bestimmte, ob Implementation angemessen ist.
Sondern diese Aufwände für Funktionen werden dann in sogenannten Komplexitätsklassen,
werden die beschrieben. Und sie sehen hier die,
also so eigentlich alle gängigen, typischen Komplexitätsklassen. Also das,
was man ganz toll findet, ist,
dass in unabhängig von der Menge der Daten, die sie vergleichen,
dass sie einen konstanten Aufwand für die Operationen haben,
die auf dieser Datenstruktur ausgeführt werden.
Das ist eine Illusion
Das gibt es so jedenfalls nicht für alle Operationen.
So ein Logarithmischer Aufwand, also der ist so,
das würde ich mal sagen, der ist extrem angenehm.
Wir haben, ganz am Anfang habe ich gesagt,
wir werden diese dynamische Datenstruktur Baum noch kennenlernen.
Das Suchen in ausgeglichenen, binären Bäumen ist angenehm,
weil es nur mit einem logarithmischen Aufwand bezüglich der Menge der verwalteten Elemente versehen ist.
Linearer Aufwand ist, also bei dem Suchen in Listen,
Die Komplexitätsklasse, wir werden uns noch mit Sortierverfahren beschäftigen.
Also suchen und sortieren ist etwas, das machen Informatiker, Programmierer.
Damit beschäftigen die sich, also mit Wonne und gute Sortierverfahren haben,
also diesen Aufwand, der extrem viel gutartiger ist als so ein quadratischer Aufwand. Das ist so,
man kann davon, man kann so als Faustregel sagen,
je mehr Gehirnschmalz sie in ein Sortierverfahren gesteckt haben,
desto eher liegt der Aufwand,
also im Ablauf dann bei in dieser Komplexitätsklasse und die einfachstmöglichen Sortierverfahren,
die halt mal in quadratischen Aufwand,
brauchen sie aber überhaupt kaum Gehirnschmalz reinstecken.
Die sind zwar schnell implementiert, laufen dann aber bei größeren Datenmengen
extrem lange. Und das hier ist so die Komplexitätsklasse,
die man eigentlich meiden möchte,
weil das ist so keine Frage der Hardware mehr,
dass das Zeug, also lange Laufzeiten hat,
das ist also höchstens, also dem Problem darf das dann noch angemessen sein.
So, jetzt übergebe ich an Fred und ich wünsche Ihnen
das drittletzte Mal ein schönes Wochenende
Also das drittletzte Mal in diesem Semester
So, bin ich hörbar? Ich habe in den letzten Wochen
immer vergessen, hier auf diese gelben Knöpfe zu drücken.
Ich mache das einfach mal wieder. In der Hoffnung,
dass es irgendwie das Bild schöner macht. So. Ja,
nach der letzten Vorlesung kam ein interessierter Student zu mir
und hat mich auf einen Fehler aufmerksam gemacht.
In diesem Primzahlen-Rechner-Algorithmus, den schauen wir uns nochmal ganz kurz an.
Ich hoffe, ihr erinnert euch noch,
das war ein ziemlich langer Code
Also wir hatten uns hier dieses Sieb angelegt,
wo wir für jede Zahl uns gemerkt hatten,
ich sehe eine Primzahl ja oder nein.
Deswegen ist es ein Bullian Array. Dann haben wir erstmal
alles auf True gesetzt. Also solange wir keinen Gegenbeweis finden,
gehen wir jetzt mal davon aus, dass die Zahl eine Primzahl ist.
Dann sind wir langsam durch das Sieb durchgegangen, haben gesagt,
jedes Mal, wenn wir eine Primzahl finden,
müssen wir die vielfachen ausgrauen.
Und am Ende müssen wir dann einmal noch schauen,
wie viele Primzahlen haben wir gefunden?
Dann legen wir entsprechend das Array an, wo die Primzahlen reinkommen.
Und dann kopieren wir die Primzahlen da rein. Das waren so
die einzelnen Schritte des Algorithmus. Und der Herr meinte,
Und das ist nicht in Ordnung, ist,
dass hier das kleinere Zeichen steht.
Und ich war mir nicht so 100 Prozent sicher.
Und da habe ich mal so ein bisschen nachgedacht,
wann macht das eigentlich einen Unterschied?
Und ich habe tatsächlich einen Testfall gefunden, der diesen Fehler aufdeckt.
Das ist immer das, was man machen sollte.
Wenn man glaubt, einen Fehler gefunden zu haben,
schreibt erstmal einen Test, der den aufdeckt.
Ich starte mal die Testklasse. Und tatsächlich, ja,
dieser alte Test, den wir letzte Woche geschrieben haben,
der funktioniert. Und ein zweiter Test funktioniert nicht und
den schauen wir uns mal ganz kurz an
Worin unterscheiden Sie jetzt diese beiden Testfälle? Also das ist der alte Testfall,
das ist der neue Testfall und es ist tatsächlich ein einfacherer Test.
Wir hatten hier die Prinzzahlen, die kleiner sind
als 100 berechnet und dann kam das Richtige raus.
Und dann habe ich hier die Primzahlen, die kleiner sind
als 50 berechnet und da kommt nicht das raus, was ich erwarte.
Ihr könnt mir glauben, dass es ansonsten wirklich exakt dasselbe nur,
dass ich quasi hier dann das RE abgeschnitten habe, gedanklich.
Das ist hier einfach eine kleinere Serie. Warum gerade
die 50? Tja, wir können einfach mal gucken,
was der Algorithmus macht, wenn wir die 50 da reinstecken
Also, wenn wir die 50 da reinstecken und hier die Wurzel berechnen.
Die Wurzel von 50 ist 7, ein bisschen.
Dann wird das hier auf Int gecastet, dann ist es sieben.
Und hier sage ich, ich muss mir nur die Zahlen
angucken, die kleiner sind als sieben. Das heißt,
ich werde nur Vielfache von zwei, drei und fünf ausblenden.
Nicht Vielfache von sieben. Das ist natürlich schlecht,
weil die 49 ist ja zum Beispiel sieben mal sieben und
die wird dann von diesem Algorithmus nicht ausgeblendet.
Also dieser Algorithmus glaubt, dass 49 eine Primzahl ist.
Es hat tatsächlich ziemlich lange gedauert, diesen Fehler zu finden
Okay, man kann diesen Fehler auf verschiedene Arten und Weisen beheben. Ich kann sagen,
hier muss Kleiner gleich stehen oder ich könnte auch sagen,
ich lasse dieses Gecaste einfach weg und sage,
ich bleibe hier mal halbwegs genau,
ich will hier keine Informationen wegschmeißen.
Dann sollte, der Test war hoffentlich durchlaufen. Das habe
ich noch gar nicht probiert. Ja, jetzt geht's.
Okay. Tja, ja, also so Fehler sind manchmal
sehr heimtürisch und treten nur bei bestimmten Randfällen auf.
Gut, das heißt also,
wenn die letzte Zahl in meinem RE ein Quadrat einer Primzahl ist,
nur dann tritt der Fehler auf und das ist
eben rein durch Zufall passiert sowas nicht besonders häufig
Okay, dann ist mir noch was aufgefallen. Und zwar habe ich
ja hier gesagt, wir müssen die Vielfachen von I ausblenden.
Und wir haben nicht mit I selber angefangen,
weil sonst würden wir die Primzahlen auch mit ausblenden.
Sondern wir haben mit zweimal I angefangen. Und habe ich mir irgendwann überlegt,
das ist ja eigentlich Quatsch, jedes Mal mit zweimal I anzufangen,
weil die Vielfachen von zwei werden ja relativ schnell gekillt.
Im allerersten Durchgang werden ja alle Vielfachen von zwei gekillt.
Und wenn ich dann die Vielfachen von drei killen will,
ist es eigentlich Quatsch bei sechs anzufangen,
weil die sechs ist ja eine gerade Zahl,
die wurde ja gerade schon ausgeblendet.
Hat jemand eine Idee, was hier sinnvoller wäre,
statt bei zweimal I anzufangen, wo ich mir Arbeit ersparen kann?
Auch das hat ziemlich lange gedauert, bis ich das verstanden habe.
Also möglicherweise, wenn ihr das jetzt nicht schon wisst,
Ja. I Quadrat. Genau, weil alle Faktoren,
die kleiner sind als I, haben wir ja schon bereits ausgeblendet,
in einem Durchläuf davor. Sehr schön. Okay,
ich glaube, das reicht aber auch zu den Primis,
weil wir gucken mal kurz, ob die Tests noch durchlaufen.
Okay, wir haben nichts zusätzliches kaputt gemacht, das ist schon mal gut.
Okay, hier sind bestimmt noch weitere Fehler drin.
Eine Sache wollte ich euch noch zeigen, die mir aufgefallen war.
Ich hatte ja hier dieses Java-Youtel Arays mir importiert,
um irgendwie mal das Eric schön auf die Konsole zu schreiben.
Und ich habe mir diese Klasse noch mal so ein bisschen angeguckt,
was da eigentlich noch so für Hilfsmethoden drin sind.
Und zum Beispiel diese Schleife kann man wunderbar mit so einer Hilfsmethode setzen.
Also wenn wir jetzt hier lange genug auf die Schleife raufschauen,
ah ja, das scheint so ein Standardfall zu sein,
wir fangen vorne an,
wir wollen ganz bis zum Ende gehen und an der jeweiligen Stelle wollen wir dann Schuhe reinschreiben.
Auf Deutsch heißt es ja,
ich will alle Elemente auf True setzen und dafür gibt es schon eine Hilfsfunktion,
die wollte ich euch einmal ganz kurz zeigen und sagt einfach,
arays.phil, welches aray und welcher Wert.
Liest sich vielleicht ein bisschen schöner,
dann muss man nicht im Kopf diese Schleife durchgehen und sich
den Sinn dieser Schleife selber erschließen, sondern man sieht,
ah, es soll ein Aray erfüllt werden.
Welche Serie soll überfüllt werden? Was soll der Wert sein? Auch hier sofort,
immer wenn ich irgendwas ändere in meinem Programm,
lasse ich sofort die Tests durchlaufen,
dass ich mich sicher bin,
dass ich keine neuen Fehler eingebaut habe.
Gut, und so ein bisschen um RRS soll es ja
heute auch gehen. Er ist als Implementationsgrundlage für unsere Arraylist.
Dann schaue ich mal, ob das Projekt noch hier ist.
Ja, genau. Ich habe immer ganz bewusst hier
ein leeres Projekt genommen. Und ich habe mir gedacht,
heute ist mal wieder ein toller Tag, um über Testen zu reden.
Wir haben ja letztes Jahr schon so ein bisschen über Testen
geredet, da war die Motivation. Wir haben Code,
wir finden einen Fehler in dem Code,
wir schreiben einen Testfall, der den Code,
der den Fehler aufdeckt und dann beheben wir den Fehler.
Man kann das noch radikaler machen und sagen,
wir schreiben den Test, bevor es überhaupt irgendeinen Code gibt,
den wir testen können
Also wenn man jetzt hier rangeht und sagt,
ich will eine Area-Liste implementieren,
dann würde jetzt der normale Programmierer, der gesunde Menschenverstand sagen,
dann mache ich mir doch erstmal eine Klasse, namens zum Beispiel Arealist,
ich nenne sie mal vielleicht lieber Mai-Realist oder so,
weil in Java gibt es ja schon ein Arealist.
Aber der testgetriebene Entwickler sagt, nee, das kannst du nicht machen.
Ich habe noch keinen Testfall, der fehlschlägt,
um auf diese Arealist zuzugreifen. Also nennen wir das vielleicht
mal Mai-Realist-Test. Das soll ein Unit-Test sein. So,
jetzt habe ich also quasi den Testhut auf. Ich wollte mir
eigentlich einen Hut heute mitnehmen, habe ich dummerweise vergessen.
Also stell dir vor, ich habe jetzt einen Testhut,
aber ich bin jetzt ein Tester.
Okay. So, dann machen wir uns doch mal
ein Exemplar unserer Klasse,
Die nennen wir jetzt mal schön kurz,
weil die brauchen wir ständig, nennen sie einfach mal List.
So, dann schreiben wir uns mal einen kleinen Konstruktor für die Testklasse.
Und da erzeugen wir jetzt einfach mal... Nee,
stimmt gar nicht. Ich gehe schon wieder zu weit.
Ich muss meinen Hut jetzt einmal ganz kurz abnehmen.
Was wird passieren, wenn ich diese Testklasse übersetze?
Da müsste es ja hier einen Fehler geben.
Genau, cannot find similar class my earlist.
Jetzt nehme ich meinen Test tut wieder ab und sage,
ich will irgendwie dafür sorgen, dass diese Zeile übersetzbar ist.
Also muss ich jetzt eine Klasse schreiben, die my earlist heißt.
Okay, schreibe ich eine neue Klasse,
ganz normale Klasse im Mai,
So, jetzt gibt es diese Klasse.
Und jetzt ist auch der Test übersetzbar. Jetzt darf ich
mir wieder den Testtod aufsetzen und weiterschreiben in meiner Testplasse.
Okay, dann mache ich mir doch mal vielleicht ein neues Exemplar.
Und vielleicht wünschen wir uns jetzt mal,
dass es einen Konstruktor gibt, dem man sagen kann,
wie groß soll denn das Eri am Anfang sein?
Soll da Platz für drei oder fünf oder zehn Elemente sein?
Und damit unsere Testfälle nicht so kompliziert werden, nehme ich vielleicht mal drei.
Also damit sage ich, ich brauche eine neue Erialist.
Und am Anfang soll das Eri, was ihm zugrunde liegt,
gerade mal Platz für drei Elemente bieten.
Und wenn ich später mal mehr brauche,
muss das dementsprechend größere Eris
angelegt werden
Jetzt übersetze ich das wieder und der Compiler wird sich wieder beschweren.
Der Compiler sagt, es gibt keinen Konstruktor, der einen Int entgegennimmt.
Okay, dann gehe ich jetzt wieder zurück in diese Klasse.
Jetzt setze ich den Testhut also wieder ab und schreibst so
einen Konstruktor. So, Public, Mai, Arealist,
Int, nennen wir das mal, vielleicht Start,
Kapaz, Start, Capazität.
Das ist ein deutsches Wort, oder?
So, jetzt gibt es diesen Konstruktor. Jetzt setze ich
wieder meinen Testhut auf und jetzt ist das übersetzbar.
Der Konstruktor macht noch nichts besonders Sinnvolles,
aber ich darf nichts Sinnvolles in den Konstruktor reinschreiben,
solange ich keinen Test habe, der das sozusagen aufdeckt.
Das ist vielleicht
ein bisschen sehr radikal
Okay, ja, was kann man denn jetzt eigentlich testen?
Ich habe jetzt hier mal so ein Skelett-Vorbereitung,
wo man so mehrere Testfehler reinschreiben kann. Also wenn ich
so mir eine Airy-Liste erzeuge, wo drei Elemente reinpassen,
könnte ich ja vielleicht erst mal testen, dass die leer ist.
Ich gehe also davon aus, wenn ich mir eine neue
Airy-Liste erzeuge, dann sollte die irgendwie leer sein.
Also nenne ich das doch mal neue Liste, ist leer.
Okay? So, jetzt kommt wieder DJ Unit-Experten.
Wie hieß das nochmal? Asserd True gab es doch so
eine Methode, oder? Ist ja True,
List ist MT
Diese Methode gibt es noch gar nicht, aber ich würde sie gerne haben.
Testgetriebene Entwicklung, ich überlege mir, welche Methoden würde ich gerne haben.
Dann übersetze ich das wieder, unter Compile beschwert sich,
dass es eine solche Methode gar nicht gibt.
Okay, dann schreiben wir die doch mal. Ich schreibe jetzt sofort diese Methode,
ich schreibe immer noch nichts in den Konstrukt da rein.
Also diese Methode gibt einen Bullion zurück. Und was,
ja, das kann ich jetzt nicht übersetzen, weil ich hier nicht zurückgebe.
Ich muss also irgendwie den minimalsten Quote jetzt hier reinschreiben,
der irgendwie dafür sorgt, dass das übersetzbar ist,
sagen wir mal, true.
Okay, jetzt setze ich wieder meinen Testtod auf und
jetzt ist das hier übersetzbar
Kann ich die Tests jetzt auch schon ausführen? Wollen wir
das mal machen? Wollen wir die Tests ausführen? Ja,
das ist jetzt sehr trügerisch. Drei Tests laufen durch,
also Testfall 2 und Testfall 3 machen ja noch gar nichts.
Und das ist jetzt mehr reiner Zufall, dass das durchläuft,
weil diese Methode ja immer true zurückliefert.
Okay. Gut, ich kann mir eine leere Liste erzeugen.
Ich kann gucken, ob sie leer ist.
Mehr kann ich noch gar nicht machen.
Das wäre irgendwie doof. Wollen wir vielleicht mal gucken,
wenn ich was in die Liste reintue,
dass sie dann nicht mehr leer ist.
Also vielleicht Liste mit einem Element ist nicht leer.
Füge ich jetzt mal aus in die Liste dazu,
wie warum ist das vielleicht,
er pennt, füge ich es ans Ende an.
Und wir nehmen mal... Flott Streams. Strings sind immer
so schön anschaulich. Also, ich erpenne mal Hello.
So. Und jetzt gehe ich davon aus, wenn ich das gemacht habe,
dass die Liste dann nicht mehr leer ist.
Das ist ein Tipp. Neue Liste ist leer. Wenn
ich was hier zufüge, dann sollte sie nicht mehr leer sein.
Tja, das wird jetzt nicht übersetzbar sein,
weil es so eine Appent-Methode ja noch gar nicht gibt.
Dann muss ich die jetzt schreiben. Okay. Ja,
falsche Klasse. Wir brauchen eine Appent-Methode. Der ich
ein String übergebe, nehmen wir das mal Element oder so
So. Tja. Wollen wir so langsam mal uns wirklich mal
in Ehre erzeugen bis jetzt haben wir das Ehre ja noch gar nicht erzeugt.
Vielleicht machen wir das. Vielleicht ist jetzt ein guter Zeitpunkt dafür,
dass wir uns tatsächlich mal so ein R erzeugen.
Also wie man RS deklariert, wisst ihr ja schon?
Oder Referenz war ja, ob man die auf RS verweisen können.
Ich brauche also ein String-Array und nenne das mal RA.
So. Okay, dann erzeuge ich hier ein RA mit einer entsprechenden Kapazität.
Das kennt ihr ja auch von letzter Woche,
wie das geht.
So, ne
Okay. So, ein Element in einer Liste hinten anzufügen,
setzt ja irgendwie voraus, dass ich weiß,
wie viele Elemente schon da drin sind.
Jetzt muss man ein bisschen vorsichtig sein. Diese Startkapazität,
die man hier angeht, sagt ja nicht,
wie viele Elemente in der Liste drin sind,
sondern nur wie viele reinpassen, wenn man so will.
Also, wie viele da logisch reinpassen. Wenn ich also sage,
New String 3, das machen wir einmal ganz kurz interaktiv im DoJ,
dass ihr seht, was dann passiert,
dann könnt ihr es euch besser vorstellen.
Ich sage einfach mal New String 3,
das ist das, was da tatsächlich intern passiert.
Und das ist ein R, wo drei Variablen vom Typ String
drin sind. Und was ist seine Variante vom Chip String?
Was ist da drin? Da sind ja immer Referenzen drin.
Also haben wir hier dreimal Referenzen. So sieht das also aus.
Ich kann jetzt also z.B. nicht, dass R.I. fragen,
hey, Eray, wie lang bist du denn?
Und dann würde er mir sagen, null, weil sozusagen
in Anführungsstrichen noch nichts drin ist. Sondern die Frage,
wie viele Positionen ich jetzt hier schon belegt habe,
könnte ich mir jetzt theoretisch mit einer linearen Suche,
also ich könnte jetzt einfach gucken, wo steht die erste Referenz,
die nall ist oder so.
Das ist mir aber zu kompliziert. Ich glaube,
es ist einfacher, wenn wir uns da einfach einen Zähler mitlaufen lassen.
Das nennt man dann die sogenannte Kardinalität der Liste.
Dafür mache ich mir einfach meine eigene Variable. Ich nenne
die jetzt mal Zeiss. Und am Anfang sage ich,
dass diese Größe null ist. Also nochmal ganz kurz zusammengefasst.
Wenn ich sage erry.längs, dann weiß ich, wie viel Platz ist.
Das nennen wir Kapazität. Und diese Variable Size sagt mir,
wie viel ich bis jetzt ausgenutzt habe von dem RA sozusagen.
Das nennen wir dann Kardinalität. Okay. An welcher Stelle
in dem RA muss ich jetzt denn das Element einfügen? Also wenn es leer ist,
muss ich es an Position null einfügen, wenn schon ein Elementar ist,
muss ich es an Position 1 anfügen, wenn schon zwei da sind,
muss ich es an Position 2 einfügen und so weiter.
Was muss ich jetzt hier hinschreiben?
Kann ich das hier noch irgendwie sehen,
dieses RAG ist hier
immer noch offen
Hier, ne? Also ich glaube, das kann ich auch
interaktiv sagen. Ich vergesse das immer, dass man das
so schön visualisieren kann. Also wie heißt das hier?
String 1 sind ziemlich blöder Name. Also am Anfang würde
ich an Position 0 einfügen, zum Beispiel Hello.
Dann seht ihr, dass hier das Hello drinsteht.
Wenn ich jetzt noch was einfügen will, dann muss ich ja
an Position 1 reintun. Hups. So, ne?
Zum Beispiel. Und wenn dann schon zwei drin sind,
dann muss ich es an Position 2 einfügen.
Tja, wo muss ich es also allgemein einfügen?
Hängt das von der Länge des RS ab oder von
irgendwas anderem
Ja, das heißt, und dann kann man,
das heißt ja auch eine Bühne. Genau, sehr schön.
Und ich habe vergessen, ihr den unterstrichen zu schreiben.
Fällt mir gerade auf. An der Stelle,
es heißt, also die Variable, das heißt,
es gibt an, wo Platz ist in dem Area.
Und dann muss ich sie anschließend natürlich auch erhöhen.
Vielleicht können wir das immer interaktiv testen,
dass diese Struktur nochmal so ein bisschen klarer wird.
Ich kann ja immer wunderbar hier schummeln und in die Objekte
reingucken bei Blue Chell. So, dann erzeuge ich mir doch,
ich erzeuge jetzt mir tatsächlich mal so eine New Mayer-Realist.
Mit drei, das hatten wir eben auch,
weil das so schön anschaulich war.
So nenne ich sie jetzt immer ein bisschen schöner,
nenne ich sie mal
So, hier ist sie jetzt. Und hier seht ihr jetzt
den Zustand. Also wir haben diese Referenz auf das RRE.
Und wenn ich darauf blicke, werde ich diese Drei-Neil-Referenzen sehen.
Hier sind sie. Okay, und momentan haben wir noch
keinen Platz genutzt. Das heißt also,
diese Null ist der Index in dem RRE,
wo jetzt der nächste Nutzeintrag reinkommen würde.
Ich will irgendwie alles gleichzeitig sehen, dass ihr seht,
wie sich was verändert. Ich hoffe, dass man das
tatsächlich sieht, wenn ich jetzt inteaktiv was mache.
Also wenn ich zum Beispiel sage, a.appent hello,
passiert dann das, was wir uns vorher überlegt haben.
Ach, man sieht es leider nicht.
Aber ich habe das Simikron dann vergessen.
Habe ich
Enter gedruckt
Ah, seht ihr, was passiert ist? Es wurde
an Position null eingefügt und anschließend wurde die Größe um einen erhöht.
Also, falls wir irgendwann nochmal was einfügen wollen, wird es dann hier eingefügt.
Das machen wir doch mal sofort. A, Punkt,
erpennt, ups, world, not see your world,
an Position 1 eingefügt worden und dann wurde die Größe auf 2 erhöht.
Und jetzt machen wir nochmal so. Und dann wurde das
hier entsprechend auch an die richtige Stelle getan.
Das ist doch schon mal nicht schlecht. Wer hat es
bis hierhin verstanden? Also ein Meinungsbild, okay, gut.
Ich glaube, es ist ganz anschaulich hier mit diesen,
mit diesen beiden Objekten, die man so sieht.
Gut, diese Operation ist MT. Behauptet ja momentan,
da ist nie was drin in der Liste,
das stimmt jetzt ja nicht mehr.
Was kann ich denn jetzt hier sinnvoller hinschreiben,
dass er ja eben den richtigen Wert zurückgeht.
Also wenn, ja. Genau. So. Ich würde gleich
noch mal versuchen, noch mal nach AfD zu sein.
Du möchtest noch was einfügen? Das ist natürlich wahrscheinlich nicht
so eine gute Idee. Dann wird er ja versuchen,
aber ja, ich weiß nicht, sind die Objekte noch da?
Ich habe es ja nicht übersetzt, können wir machen.
Da wird eine Acception kommen
Gucken. Also er wird es irgendwie sagen,
Arry Index out of Bounds Acception heißt es, glaube ich,
in Java. Genau, da ist sie, ne?
Also du kannst nicht anstelle 3 in einem Arry was einfügen,
wo es nur von Löwstwahl geht.
Darum müssen wir uns später kümmern. Okay,
also dieses Ist-Emd, ich sollte jetzt was halbwegs Sinnvolles zurückliefern.
Das heißt, dieser Testfall,
den wir irgendwie vor fünf bis zehn Minuten geschrieben haben,
der müsste jetzt eigentlich wieder durchlaufen.
Das probieren wir auch einmal kurz. Hier ist eine Testfolge.
Wir testen. Genau, jetzt läuft wieder alles durch.
Das ist doch schön.
Ja, ich bin jetzt in der Implementation so ein bisschen vorgeprescht.
Wir müssen jetzt mit der Testklasse mal so ein bisschen hinterherhecheln.
Das passiert mir immer, weil ich dann so begeistert von der Implementation bin.
Also man sollte vielleicht auch testen, dass das,
was man an die Liste ranhängt, auch tatsächlich da drin steht.
Das wird jetzt natürlich schwierig, weil es dafür noch keine Methoden gibt,
aber wir wollen ja erstmal den Test schreiben.
Also wir könnten ja zum Beispiel sagen,
ich füge irgendwie drei Elemente in die Liste ein und dann gucke ich,
ob genau die Elemente in der richtigen Reihenfolge da drin stehen.
Also sagen wir mal, richtige Reihen,
Reihenfolge von drei Elementen
oder so
Scrollen wir mal so ein bisschen. Also, wir tun.
Ich mache das jetzt genauso wie eben. Wir tun also Hello.
World. Oh, danke. Es ist natürlich toll,
wenn die Methoden deutsche Namen haben und die Varianten und englische Namen.
Okay. Richtig, genau. So, jetzt würde ich gerne schauen.
Tja, was würde ich denn jetzt gerne als nächstes schauen?
Ja, vielleicht irgendwie sowas wie Asserd Equels.
Wenn ich also zum Beispiel die Liste frage,
gib mir doch mal das Element an der Position Null,
dass dann Hello zurückkommt.
Ist das irgendwie so ein sinnvoller Testfall
Nee, es war uns genau anders,
aber ich muss ja erst das Element hinschreiben, was ich erwarte.
Das verwirrt mich manchmal so ein bisschen, so rum ist es,
glaube ich. Okay. Und dann machen wir das hier
so und so und passen hier die Intenses an.
Ja, wir hatten so eine kleine Leertaste dazwischen,
also ich tue drei Elemente rein und dann erwarte ich,
dass die an den entsprechenden Stellen stehen.
Das ist, glaube ich, ganz nett als Testfall.
Oh, ja, natürlich, es gibt die Methode nicht,
genau. Also, setze ich mein Testthund wieder ab.
Ich glaube, ich hatte vergessen, ihn aufzusetzen.
Stellt euch das einfach vor. So, dann schreiben wir
doch mal diese Methode. Also, was ist denn
der Rückgabewert dieser Methode
Rückgabetyp, Entschuldigung. String, so ein String zurückkommen,
dann nennen wir sie Get, dann kommt irgendwie so ein Index rein,
also 0-1 oder 2 zum Beispiel.
Ja, und wie liefere ich jetzt das Element,
was in dem RA an der Position Index steht?
Ja. Ja, genau. Genau. Das ist ja die ganz normale Ehre,
Index, Syntax, die habt ihr, glaube ich,
auch schon selber mal benutzt,
als sie dieses TikTok-Toe-Spielfeld letzte Woche implementiert hat,
wenn mir nicht alles täuscht.
Okay. Genau, das ist die neue Methode und
jetzt wird sofort wieder getestet, ob der Test jetzt durchläuft
Hier, dieser Test, der ist also durchgelaufen.
Wunderbar. Wenn wir uns jetzt irgendwie aus irgendeinem blöden Grund
vertan hätten und zum Beispiel hier, weiß ich nicht, null hingeschrieben hätten.
So, was wäre dann passiert? Ja, dann hätte
der Test sofort gemerkt, dass da irgendwas nicht stimmt.
Also ich habe irgendwie erwartet, dass in der Mitte World steht,
aber dass diese My Ariles hat behauptet, es wäre Hello,
weil hier immer nur das erste zurückgeben wird.
Tja, oder wenn man ja einen ganz groben Programmierfehler drin hat,
wenn man zum Beispiel hier Size hinschreibt, da passiert,
glaube ich,
noch was Spannenderes
Dann ist dir das X nicht grau, sondern rot.
Das heißt also sozusagen, es ist nicht ein String zurückgekommen,
den ich nicht erwartet habe, sondern es ist eine Ausnahme aufgetreten.
Das ist irgendwie ein, ja zum Beispiel diese Array Inics
Out of Bounce Accession, hier sieht man ja.
Also er konnte diese Methode gar nicht komplett durchführen und mir ein Ergebnis liefern,
sondern er ist so vorerst abgestürzt bei der Ausführung der Methode.
Also achtet mal hier darauf, welche Farbe diese Xe haben.
Wenn die grau ist, kam ein falscher Wertzeug.
Und wenn es so rot ist, kam gar kein Wertzeug,
sondern es ist abgestürzt in gewissem Sinne.
Okay, aber wir wissen ja, dass der Index vernünftiger ist.
Gut. Ein einfacher Test, den man auch hätte machen können,
wäre zum Beispiel zu sagen, dass sich die Größe entsprechend richtig anpasst.
Ich kopiere mir mal ganz kurz diesen Anfang, dieses Testfalls.
So, also ich könnte zum Beispiel sagen,
wenn ich drei Elemente eingefügt habe, dann sollte anschließend,
sagen wir das mal so, die Größe der Liste drei sein.
Und auch das ist jetzt nicht übersetzbar,
wenn man von außen noch gar nicht fragen kann,
wie groß die Liste ist.
Oh, Entschuldigung, ich habe das jetzt genauso genannt.
Nennen wir es mal, ja,
Größe einer Drei-Element-Tiken-Liste oder so.
Genau, die Methode Size gibt es noch nicht,
dann wechsle ich jetzt wieder zurück in die Implementation.
Tun wir sie vielleicht mal. Hirsch erscheint sie mir irgendwie logisch.
Ist ja letzten Endes völlig wurscht. So, ne? Okay,
dann schauen wir noch mal, ob jetzt wieder alles durchläuft.
Jo, haben wir schon vier sinnvolle Tests, oder?
Man könnte auch gucken, man könnte das im Prinzip nach jedem Mal machen.
Also man könnte auch sagen, pass auf, tuck.
Das ist so ein bisschen Geschmackssache,
wie viel man da jetzt tatsächlich reintun will
Ja, ich glaube, da gibt es jetzt in
dem Sinne kein richtig und kein falsch.
Okay, läuft das noch? Ich habe es gar nicht
getestet. Tja. Sieht gut aus. Okay. Ja
, das ist doch sehr schön. Dann komme ich,
glaube ich, mal als nächstes zu diesem Punkt,
der aus dieser Richtung im Publikum kam.
Was ist, wenn man jetzt noch mehr einfügt?
Vielleicht nehme ich mir jetzt einfach mal diesen Testfall.
Sag mal, ich probiere es mal mit vier Elementen.
Da muss ich ja noch ein Element einfügen.
Sagen wir mal, Hellow World Test Fest
So. So. Habe ich alles sinnvoll angepasst? Ich glaube ja
, ne? Gut. Das kann ich jetzt übersetzen,
weil ich hier ja keine Methoden benutze, die es noch nicht gibt.
Aber der Test wird nicht, ich sag mal so,
der Balken wird nicht grün sein, der Balken wird rot sein.
Das gucken wir mal kurz, ob das wirklich so ist.
Genau. Und ihr seht jetzt hier ein rotes X.
Das heißt, es gab also nirgendwo das Problem,
dass diese Get-Methode mir ein unerwartetes Ergebnis zurückgeliefert hätte,
also dass da irgendwie statt fest Rest rauskam oder so.
Sondern diese Methode ist überhaupt nicht durchgelaufen oder möglicherweise auch Methoden
davor nicht, sondern da gab es irgendwo eine Acception.
Kann ich eigentlich sehen, in welcher Zeile es
diese Acception gab
Also das Problem ist bei Appent aufgetreten, kann ich hier
wie eine Zeilennummern sehen. Ah, 56. Guck mal,
hier ist der Fehler aufgetreten. Hier hat die Methode,
pennt gesagt, sorry, kann ich nicht machen,
geht nicht, ich schmeiße meine Acception.
Klar, weil ja kein Platz mehr in der Meere ist.
Das heißt, wir müssen jetzt irgendwie dafür sorgen,
in unserer Implementation, dass man auch ein viertes Element einfügen kann.
Jetzt sehe ich oft Programmieranfänge,
die schreiben dann hier einfach 1000 rein oder eine Million,
das ist natürlich nicht Sinn der Übung.
Also man wird tatsächlich mit einem kleinen R
anfangen und dann nach Bedarf größer RS anlegen
Also wir müssten vielleicht erst mal schauen,
bevor ich das Element so ganz unschuldig da reinstecke in das RAI,
ob das überhaupt noch geht. Wie kann ich feststellen,
ob das ein gültiger Index in das RAI ist oder nicht?
Ja, du hattest heute noch nicht.
Ja. Die Größe des RAIS kann ich ja recht einfach
abfangen mit Array.lenks
Also du hattest sowas vorgeschlagen, wenn Size minus eins
und jetzt, was war das gleich, Array.lank?
Ungleich? Kleiner gleich. Dann wäre sozusagen Platz. Ups
. Also unter den Umständen wäre Platz, meinst du?
Ja, Hard sieht, glaube ich, ganz gut aus.
Kann man das vereinfachen? Kann ich nicht irgendwie
das gleiche Zeichen wegnehmen und dieses minus eins wegnehmen?
Genau, dann wäre Platz. Und was ist die umgekehrte Bedingung?
Also wie kann ich auch sagen, es ist voll?
Also ich will sozusagen irgendwie, wenn es voll ist,
will ich es irgendwie größer machen.
Was müsste ich dann hier als Vergleichsopparator hinschreiben
Größer gleich. Also man kann sich dann auch noch überlegen,
Entschuldigung, der Fall, dass die Größe größer ist, kann eigentlich nie auftreten.
Also vielleicht ist es tatsächlich einfacher verständlich, wenn hier ein Doppelgleis steht.
Also wenn drei Elemente in das RAI reinpassen und ich habe drei Elemente drin,
dann ist es voll, dann kann nicht noch mehr rein.
Also ich muss jetzt hier dann irgendwie dafür sorgen, größer machen.
Okay. Und anschließend kann ich dann den Code ausführen,
der auch vorher schon funktioniert hat.
Also wenn ich sozusagen hier in Zeile 28 ankomme,
entweder war diese Bedingung falsch, das heißt,
da war sowieso noch Platz in dem RAI,
dann kann ich das einfach machen.
Oder ich habe hier dafür gesorgt, dass ich,
ich habe für Platz gesorgt
Okay. Vielleicht können wir das tatsächlich mal als eigene Methode machen,
dann ist es, glaube ich, besser lesbar.
Also ich schreibe hier vielleicht mal hin, verdoppt,
ne, ich habe das hier alles auf Englisch gemacht,
ich will das nicht mischen.
Double Capacity nenne ich das, glaube ich, immer,
so verdopple die Kapazität. Verdopple die Kapazität des RAS.
Dafür schreibe ich mir jetzt mal hier so eine kleine Hilfsmethode.
So, die soll sich also nur darum kümmern,
tja, worum soll die sich jetzt kümmern?
Vielleicht nochmal interaktiv in Bluetray. Also, wir machen uns
nochmal ganz kurz. Ich komme ja ruhig so eine Arealyst
mal wieder anlegen, das macht ja
so viel Spaß
So. Da ist sie. Ich mache sie wieder auf,
dann sieht man sie so schön.
Ist schon so lange her, dass wir sie gesehen haben.
Also hier ist das R, dann füge ich mal wieder
was ein. Oh, ich vergesse mal meinen Semikolon.
Da ist das erste Element. Da ist das zweite,
genau hier. Und da ist das dritte. So,
was jetzt nicht geht, ist zu sagen,
dieses Ja, dieser Kasten mit abgehundeten Ecken,
wie nennt man sowas denn, Box oder was auch immer.
Diese Box, ich kann die nicht hier irgendwie größer ziehen,
so mit der Maus gedrückt halten und so nach unten ziehen.
Das geht nicht. Das geht in Java nicht.
Die Alternative ist zu sagen, ich lege mir ein RA
an, was größer ist, ein neues RA.
Das zeige ich euch einmal, ich lege mir ein neues RA an.
New String, sagen wir mal doppelt so groß.
Ihr müsst es alles sehen, sonst ist es vielleicht nicht
so verständlich, nennen wir das mal neu.
Hier ist es. So. Und was ich jetzt als nächstes
machen muss, ich muss alle Elemente,
die ich vorher in dem alten RA hatte,
muss ich jetzt hier rüber kopieren.
Ich muss also quasi sagen,
neu an Stelle Null muss,
komme ich denn da jetzt rammen
Achso, das ist ja privat,
da komme ich jetzt gar nicht ran in der Serie.
Also wenn es irgendwie gehen würde, wäre das irgendwie quasi so.
Das darf ich nicht, weil Eric privat ist.
Oh, oh, Tatsache, danke. So. Wollen wir mal gucken,
ob das funktioniert, dass hier jetzt das X entsteht.
Ach, da ist es toll. Und dann,
wieso sehe ich meinen Kraus auch immer nicht,
an der Stelle eins, muss quasi dann das sein.
Und anstelle zwei, wo ist mein Förser?
Muss das sein. So kann man das quasi kopieren.
Intern können wir dann auch direkt auf das RAI zugreifen.
Wir müssen sie also wirklich alle hier so rüber kopieren,
okay. Aber dieses neue RAI ist dieser Arealist ja
noch gar nicht bekannt
Ja, also ihr habt ja verstanden, wenn ich hier
auf diese Referenz klicke, wird ja dieses RAI aufgemacht.
Ich muss jetzt also irgendwie dann intern dafür sorgen,
dass diese Referenz nicht mehr auf dieses RAI zeigt,
auf das Alte, sondern auf das Neue.
Zeige ich euch gleich, wie man es macht.
Das ist eine ganz einfache Zuweisung von Referenzen. Aber erstmal,
dass euch klar ist, wir müssen uns ein größeres RAI
anlegen und wir müssen die Elemente da so einzeln rüberkopieren.
Von alleine wird das nicht passieren. Und anschließend Referenz umbieten
. Gut. Wer hat das verstanden? Erstmal so,
dass wir das machen müssen und wir das so,
ja, okay, sehr gut.
Dann können wir es auch programmieren, wenn das verstanden ist.
So. Also, neues RAI-Anlagen. Wir müssen dieses RAI anlegen
. Tja, dann mache ich das doch einfach mal
Nennen wir das ruhig mal. Ich bin auf eben neu.
So. Doppelt. Was muss ich denn jetzt hier reinschreiben?
Damit das R. doppelt so groß ist wie das Alte.
Ihr hattet alle heute schon mal du.
Genau. Zweimal Sice. Man könnte auch genauso gut schreiben,
zweimal Array Langs. Aber wir haben ja hier festgestellt,
dass Sice und Array Lang längst den gleichen Wert haben.
Also macht es hier keinen Unterschied. Genau. Es war
noch so, ist sehr schön
So, jetzt muss ich rein konzeptionell, muss ich jetzt,
wie gesagt, sowas schreiben, neu an der Stelle null,
ist gleich RAA an der Stelle null.
Und dann irgendwie copy-paste. Aber das wird jetzt natürlich nur funktionieren,
wenn ich das das erste Mal mache,
nach dem Motto, ich weiß ja, als Programmierer,
dass es drei sind und so.
Aber wenn ich jetzt irgendwann das von sechs auf zwölf erweitern will,
dann reicht es natürlich nicht aus, nur die ersten drei zu kopieren.
Und wenn man hier so eine Zuweisung sieht,
die sich jeweils nur in dem Index unterscheidet,
was kommt euch dann in die Birne, was man da normalerweise verwendet?
Also man weiß nicht genau, wie oft man das machen muss,
es hängt irgendwie von Avariabler ab,
ja.
Eine Vorschlag war sehr schön
Und wir gucken uns jetzt einfach ganz blöd an.
Was ist denn der erste Index, für das wir das machen müssen?
Neuer Offenbar null. Ich muss mit null anfangen.
Dann überlegen wir hier noch mal ein bisschen.
Und anschließend muss ich eh um einen erhöhen,
weil diese Zahlen hier ja immer größer werden.
Okay, dann schreibe ich das jetzt mal. Also eine
neue an der Stelle eh muss Eri an der Stelle eh sein.
So. Okay, das heißt, ich brauche das alles nicht mehr.
Wobei, naja, bloß es ist erst mal jetzt hier
die Frage stellen, wie lange muss ich denn weiterschleifen?
Was muss für I gelten, damit ich hier noch mal reingehen muss?
Ich will es ja für null machen, für 1 und 2,
aber für 3 will ich es nicht mehr machen,
wenn die Größe 3 ist.
Und wenn die Größe 6 ist, würde ich es für 0,
1, 2, 3,
für 5 machen,
aber nicht mehr für
Du sagst I kleiner gleich Size. Gibt es andere Vorschläge
? Ja. Egal. Kleiner, richtig. Weil wenn
die Größe 3 ist, es gibt ja keinen Index 3.
Für den Index 3 darf ich diesen Kopiervorgang nicht machen,
sondern nur für alle Zahlen, die echt kleiner sind als 3. Also so.
Okay, dann nehme ich das hier unten jetzt wieder weg.
Ihr glaubt mir hoffentlich oder habt euch selber davon überzeugt,
dass das jetzt alle Elemente kopieren wird.
Ja, mit einem Index, der kleiner ist als das.
Das ist ja sinnvoll. Okay, dann übersetzen wir.
Moment, ein Schritt fehlt ja noch.
Also wir haben das jetzt kopiert
Aber diese Referenz zeigt ja immer noch auf das alte R.A.
Wie kann ich denn jetzt dafür sorgen,
dass diese Referenz auf das neue R.A. zeigt, ja?
Array ist... Genau. Man muss sich immer klar machen,
Arrays werden ja immer indirekt in Java angesprochen.
Also dieses R.A., das, was wir hier haben,
ist ja nur eine Referenz auf ein String Array.
Und dieses R.A., was wir hier haben,
ist ja nur eine Referenz auf ein String Array.
In den Variablen ist also irgendwie abgehängt, wo sind denn diese R.A.s?
Und wenn ich jetzt so eine Zuweisung mache,
heißt es, ich kopiere die Information,
wo das R.A. lebt
Also die Variable Array wird anschließend auf dasselbe Ehre zeigen,
wie die Variable neu.
Also auf das, was ich hier quasi angelegt habe.
Das müsste funktionieren. Und das testen wir jetzt mal interaktiv.
Wir testen es einmal mit dem Jay-Unit-Test, der läuft jetzt durch.
Aber ich will auch, dass es interaktiv getestet wird,
dass euch nochmal klar ist, was da passiert.
Also, New My Ralyst, ups.
Also ich hatte den Parameter vergessen. Hallo? Wir schauen
Sie uns wieder an.
Hier ist sie
Hier ist das alte RAL, jetzt ganz schnell mal hier
was reintun, X. Da ist es, hier ist das
Y. Und da ist das Z, sehr schön.
Und jetzt will ich noch was reintun. Ach so,
hier mal, keine Ahnung, Quark. So,
man sieht jetzt nichts, aber ihr werdet jetzt was sehen.
Wenn ich jetzt auf diese Referenz klicke, die hat sich geändert,
das wird leider im Blutjob irgendwie nicht hervorgeben,
dass die sich jetzt geändert hat.
Ich klicke jetzt mal auf,
hier ist jetzt das neue RAL
Das ist jetzt das Neue. Dieses Ehre ist also
gar nicht mehr verbunden über diese Referenz. Wenn ich das
jetzt hier schließe, werden wir da nie wieder rankommen.
Also verabschiedet euch jetzt bitte davon, ich schließe es jetzt.
Das ist jetzt das Neue Ehre. Also die
alten Elemente kopiert und dann hat er anschließend Quark eingefügt.
Das ist doch wunderbar. Wir können ja nochmal
ganz schnell nochmal Quark reintun und nochmal und dann vielleicht,
was gibt es denn noch außer Quark?
Joghurt. So, und jetzt hat sich wieder diese Referenz geändert.
Jetzt ist hier sein RR der Größe 12 entstanden, wo er dann,
also diese ganzen Elemente rüber kopiert hat und dann in die Orte eingefügt hat.
Okay, wir haben jetzt nur noch vier Minuten,
das ist ja blöd
Da kommen ja dann kaum noch zu interessanten Fällen.
Dann würde ich noch mal ganz kurz einen Testfall schreiben.
Wir haben jetzt ja immer hinten eingefügt. Das ist relativ einfach.
Ich würde ganz gerne noch mal irgendwo in der Mitte einfügen.
Welchen Testfall kopiere ich mir da mal? Vielleicht fangen wir
mal wieder hier an und nennen das dann vielleicht mal,
keine Ahnung, in der Mitte einfügen oder so.
So, und dann mache ich jetzt mal, dass es
einigermaßen anschaulich ist, tut da vielleicht mal ein paar Buchstaben ein.
Ja, sagen wir mal, A, B, E zum Beispiel.
Und nach dem Motto, ich habe irgendwie vergessen,
das C einzufügen und ich will dann irgendwie sowas sagen wie,
füg doch mal anstelle zwei das C ein.
Da ist dann also quasi die Herausforderung zu sagen,
A und B bleiben da, wo sie sind,
aber D und E müssen ja jetzt verschoben werden,
damit Platz für das C ist.
Okay, Insel gibt es natürlich noch nicht,
das müssen wir jetzt programmieren.
Okay, wo tun wir die hin? Schauen Sie mal
hier hin. Ups, da habe ich mich vertippt.
Wäre Insel at
ein besserer Name
Ja, kann ich gerne ändern. Oh,
ich sehe gerade, wir haben ja noch gar nicht getestet.
Das muss man natürlich auch machen. Also ich gehe aus,
davon aus, dass anstelle null anschließend das A steht.
Und dann hier entsprechend wie CDE. Also das Spannende ist,
dass D und E jetzt neue Positionen haben und C
da in die Mitte reingefügt wurde.
Okay, Insel Ed hatte ich das umbenannt? Ja,
habe ich gemacht, danke. Ja, und es liest
sich tatsächlich ein bisschen schöner. Und in der Übung,
die ihr programmiert, gibt es tatsächlich auch nur so eine Methode.
Ich glaube, die heißt einfach nur Ich füge hinzu,
ich weiß es gar nicht genau. Also eine,
die immer am Ende anführt, gibt es da gar nicht.
Das hatte ich jetzt nur aus pädagogischen Gründen gemacht.
Damit wir uns um das Verschieden noch nicht so Gedanken machen müssen.
Okay. Ja, was machen wir jetzt? Also wir
nehmen jetzt mal diesen Fall, den wir eben hatten.
Aus Zeitenmangel kann ich das jetzt, glaube ich, grafisch so schnell nicht machen.
Genau, also wenn ich diese Situation habe, A,
B, D, E, wie muss ich dann verschieben?
Also das hier ist ja in Position null, das ist an Position 1,
das ist an Position 2 und das ist an Position 3.
Und wenn ich anschließend will, dass das dass die an
diesen beiden Positionen sind drei und vier und nicht mehr zwei und drei.
Durch welche Zuweisung kann ich diese Elemente jetzt sinnvoll verschieben?
Das kann ich ja erst mal einmal ganz kurz hart kodieren,
dass ihr das Schema seht. Also ich tue jetzt mal so,
als würde der Benutzer hier immer an Position 2 einfügen wollen.
Und ich tue jetzt mal so, als wären schon vier Elemente drin.
Okay? Dann muss ich jetzt irgendwie dafür sorgen,
dass dieses E von Position 3 an Position 4 wandert.
Ich zeige euch einmal ganz kurz die Syntax dafür.
Ich sage also an Position 4 will ich das reinkopieren,
was momentan an Position 3 steht.
Das ist eine ganz normale Zuweisung. Also die Referenz,
die an Position 3 steht,
die kopiere ich anstelle
Würde das ehrlich so aussehen, A, B, D,
E, E. Habe ich da zweimal das E drin.
Okay. Und dann muss an Position 3 muss das Element
stehen, was vorher an Position 2 stammt.
Grafisch wäre das wahrscheinlich ein bisschen besser,
aber ich habe jetzt leider die Zeit nicht mehr dafür.
Okay. Gut. Ist das irgendwie klar,
dass diese beiden Zuweisungen stattfinden müssen, damit das E an
Position 4 wandert und das D an Position 3?
Ist das so einigermaßen klar? Ja, es wird schon
langsam etwas unklarer. Das müsst ihr euch dann auch nochmal
flat auf Papier klar machen, dass das sinnvoll ist.
Okay, und dann ist jetzt die Frage,
wo kommen diese Indexe eigentlich ganz allgemein her?
Also diese vier Ist ja zum Beispiel deswegen wichtig,
weil ich auf jeden Fall, wenn ich irgendwo in der Mitte was einfüge,
muss ich das letzte Element als erstes nach hinten schieben und dann das vorletzte und dann das vorvorletzte.
Also wir werden hier immer anfangen müssen mit der Größe.
Sehe ich das richtig? Ich glaube ja. Also hier
wird also allgemein, wird hier mit der Größe angefangen werden.
Und hier muss ich dann entsprechend einen davor anfangen.
Und wann muss ich aufhören? Wenn ich an dem Index
angekommen bin und der muss verschoben werden, einen weiter zur Nachbarposition.
Und dazwischen können theoretisch beliebig viele Zahlen stehen.
Seht jemand diese allgemeine Formel sozusagen
Ich muss als erstes an Position 4 in unserem Beispiel schreiben,
abhängig davon, wie groß die Liste ist,
kann das auch ein anderer Wert sein.
Und ich muss eben das von links rüber kopieren.
Und das letzte Element, was ich kopieren muss,
steht an der Position, wo ich einfügen will.
Das ist irgendwie logisch, weil wenn ich das nicht vorher
woanders hin kopiere, dann würde es ja verschwinden.
Macht euch das mal ruhig an mehreren Beispielen klar,
dass das die erste Zuweisung ist,
die wir machen müssen und dass das die letzte Zuweisung ist,
die wir machen müssen.
Und jetzt überlegen wir uns einfach, was gilt denn für diese Indiziers,
damit wir wieder so eine schöne Vorschleife machen können?
Das mache ich jetzt noch kurz mit euch
Also, ich nehme jetzt einfach mal der Einfachheit, Haban,
weil ich das aus Erfahrung weiß, will ich über diese Indizes schleifen.
Ich sage also, was ist der erste Index?
Das ist ja dieser, er steht ja hier,
das ist das erste, was ich machen muss.
Das überlegen wir uns noch. Und werden diese Zahlen jetzt
größer oder kleiner? Also, wenn ich bei dem größten
Index anfange und mich dann so langsam in diese Richtung bewege,
werden die dann größer oder kleiner, natürlich kleiner.
Ich muss also meine Vorschläge machen, die ausnahmsweise mal runterzählt.
Was gilt für alle diese, ja,
Positionen oder Indexes in dem RA,
was jetzt für die nächste Zuweisung, die sinnlos wäre,
nicht mehr gelten würde?
Also, wenn ich diese Zuweisung jetzt auch noch machen würde,
das wäre irgendwie Quatsch.
Ja, ich muss ja nichts verschieben, was vor dem Element steht.
Was gilt für diese Positionen aber nicht mehr
für diese?
Sieht das jemand
Größer Index, sehr schön. I größer Index. Dann
sage ich, Herr Rean. I ist RAM minus eins.
Wir haben uns ja immer klargemacht, dass der Index,
der hier steht, immer um einen kleiner ist wie hier.
Das steht ja immer an dem Beispiel, hier steht jetzt heiß,
minus eins. Und ihr seht es an dem Beispiel,
weil Index plus eins, minus eins ist dann wieder Index.
So, das ist also jetzt die Verschiebung. Und dann
kann ich das in dem RA im Prinzip einfach einfügen.
Das gleiche Element. Ich glaube, dass diese Methode noch
nicht funktionieren wird, wenn vorher das RAL voll ist.
Das können wir dann vielleicht als nächsten Test für uns
nächste Woche
Okay, schauen wir mal, ob der Testfall jetzt durchläuft.
Das sieht doch ganz gut aus. Und was ich jetzt ganz gerne machen würde,
tut mir leid, dass ich heute ein bisschen überziehe,
ihr seid in zwei Minuten durch.
Ich würde hier gerne mal einen Breakpoint setzen.
Wie mache ich das? Kann ich ja nicht Breakpoints setzen,
doch da ist er. Und will den Test nochmal ausführen.
Und ich will, dass ihr das nochmal grafisch seht.
Also wir sind jetzt hier in dem Breakpoint, hier ist meine Liste.
Hier seht ihr sie. Hier seht ihr, wie es
momentan aussieht. Und wir würden jetzt erwarten,
wenn ich jetzt hier reingehe, ich hoffe mal,
dass ich das so nach und nach durchmache.
Ich mache jetzt mal ein Step-Into, jetzt bin ich hier,
jetzt schiebe ich den Kurt mal so nach unten,
dass ihr Kot und Objekt irgendwie gleichzeitig seht.
Das ist ziemlich schwierig
So. Machen wir ein Step. Wo sind wir jetzt?
Also. Iih hat den Index 4. Das heißt,
wir müssen nicht hier als erstes was reinschreiben, ist klar,
weil wir müssen ja nach unten die Elemente jetzt schieben.
Und was müssen wir an Position 4 schreiben,
das Wasser an Position darstellt? Ich würde also erwarten,
dass das eh jetzt hier reinkopiert wird, wenn ich einen Step mache.
Seht ihr, wie das eh kopiert wurde?
Okay. Ja, jetzt wird das Iih um einen erniedrigt
und wir gehen wieder in die Schleifen rumfahren.
Das passiert jetzt hier nach einem Schritt.
Ihr seht, ihr habt jetzt die Position 3.
Also wollen wir jetzt hier als nächstes was reinschreiben?
Und was wollen wir da reinschreiben, das,
was da drüber steht?
Also das D wird jetzt hier reinkopiert
Okay. Und jetzt wird er ihn nochmal erniedrigen,
wird feststellen, an Position 2 muss ich nichts mehr verschieben,
dann gehe ich mal aus der Schleife raus.
Ja, und jetzt kann an dieser Stelle das C gefahrlos reingeschrieben werden,
weil das D hatten wir ja hier in kopiert und das E hatten wir ja hier in kopiert.
Zack. Ja, und solche Gedanken, wo sind meine Elemente,
welche müssen verschoben werden, welche nicht,
die müsst ihr euch sowohl beim Einfügen als auch beim Entfernen machen und ich bitte euch,
macht euch solche Grafiken auf Papier oder überlegt es euch in BlueJay,
weil ihr werdet durch reines abstraktes Nachdenken niemals auf solche Vorschlafen kommen.
Das ist einfach nicht möglich. Also durch reinen Zufall werdet ihr nicht feststellen,
dass man hier anfangen muss und dass ich größer bin und
dass ich kleiner werden muss und dass hier einer weniger stehen muss,
das muss man sich einfach mal grafisch klar machen.
Okay, danke für eure Geduld, wenn ihr Fragen habt,
kommt einfach zu mir,
bis nächste Woche