In diesem Artikel werden wir eine persistente und unveränderliche Variante der LinkedList in Java implementieren mit
teilweise strukturelle gemeinsame Nutzung für Zeit- und Raumeffizienzgewinne.
Eine verknüpfte Liste ist eine Datenstruktur, die aus einer Sammlung von Knoten besteht, wobei jeder Knoten einen Wert und einen Verweis auf den nächsten Knoten in der Sequenz enthält. Operationen wie das Hinzufügen eines Elements zum Kopf der Liste oder das Entfernen eines Elements aus dem Kopf sind O(1)-Operationen. Allerdings sind Operationen wie das Hinzufügen eines Elements am Ende der Liste oder das Entfernen eines Elements vom Ende O(n)-Operationen, wobei n die Anzahl der Elemente in der Liste ist.
In der funktionalen Programmierung ist Unveränderlichkeit ein Schlüsselkonzept. Unveränderlichkeit bedeutet, dass, sobald eine Datenstruktur erstellt wurde, sie
kann nicht geändert werden. Stattdessen wird mit den Änderungen eine neue Datenstruktur erstellt und die ursprüngliche bleibt unverändert.
Diese Immobilie bietet uns mehrere Vorteile:
Java-Sammlungen sind jedoch standardmäßig veränderbar, da der Schwerpunkt dieses Artikels auf LinkedList liegt. Die Gründe könnten vielfältig sein
Das reicht von einem nicht nachträglichen Einfall bei der Gestaltung der Kollektionen bis hin zu Leistungsgründen, die inhärenten
unveränderliche Datenstrukturen.
Das JDK stellt unveränderbare Sammlungen bereit, die die Originalsammlungen umhüllen. Sie unterstützen Unveränderlichkeit, sind jedoch weder dauerhaft noch bieten sie eine wirklich typsichere Möglichkeit. Sie sind Hüllen um die Originalkollektionen und sie
löst eine Ausnahme aus, wenn eine Änderungsoperation aufgerufen wird. Dies ist nicht dasselbe wie Unveränderlichkeit, bei der die Datenstruktur überhaupt nicht geändert werden kann und gleichzeitig sichergestellt wird, dass es schwierig ist, zur Laufzeit eine UnsupportedOperationException zu erhalten.
Obwohl die Begriffe „beständig“ und „unveränderlich“ oft synonym verwendet werden, haben sie unterschiedliche Bedeutungen. Während Unveränderlichkeit keine Änderung der Datenstruktur zulässt, ermöglicht Persistenz die gemeinsame Nutzung der Datenstruktur, wenn diese geändert wird. Das bedeutet, dass bei der Änderung einer Datenstruktur, d. h. bei der Erstellung einer neuen Version, Teile der alten Datenstruktur mit der neuen gemeinsam genutzt werden können, was zu Zeit- und Platzeffizienzgewinnen führt. Diese Technik wird Strukturelles Teilen
genanntEs gibt mehrere Möglichkeiten, Persistenz in Datenstrukturen zu erreichen. Die Datenstrukturen reichen von einfach bis komplex, wie die Verwendung ausgeglichener Bäume wie AVL- oder Rot-Schwarz-Bäume, bis hin zu komplexeren wie Fingerbäumen und Radix-basierten ausgeglichenen Bäumen.
In diesem Artikel werden wir eine einfachere Version einer dauerhaften und unveränderlichen LinkedList mit teilweiser struktureller Freigabe implementieren. Der Grund dafür ist, dass LinkedList eine einfache Datenstruktur ist und uns hilft, die Konzepte der Unveränderlichkeit und Persistenz besser zu verstehen. Normalerweise ist die Implementierung komplexerer Datenstrukturen von Natur aus eine herausfordernde Aufgabe.
Im Folgenden werden wir Schritt für Schritt eine persistente und unveränderliche Single LinkedList in Java implementieren.
Für eine vollständige Implementierung und zusätzliche Monaden und Dienstprogramme, die Sie bei Ihrer funktionalen Programmiertour nach Java unterstützen, können Sie sich diese tolle kleine Bibliothek FunctionalUtils ansehen.
Der Name, den wir unserer LinkedList geben werden, ist SeqList und es wird eine generische Klasse sein.
Zunächst müssen wir über die Operationen nachdenken, die wir in unserer Liste unterstützen werden.
Wir können uns eine LinkedList als eine Struktur vorstellen, die aus Knoten besteht, wobei jeder Knoten Folgendes umfasst:
完整的实现可以在这里找到
鉴于列表的最后一个元素是一个空的 LinkedList,并且每个元素都是一个有头和尾的节点,我们可以将我们的 LinkedList 表示为由两个类组成的递归数据结构:
其中 Cons 是一个名为 Construct 的函数式编程术语,其历史可以追溯到 Lisp 编程语言。
鉴于上述,我们可以实现 SeqList 接口如下:
让我们分解一下上面写的内容:
我们需要实施剩余的操作。让我们从删除操作开始:
另外在我们的子类中实现 tail() 方法和其他一些有用的方法:
我们可以从删除方法的实现中检查,我们正在使用递归调用来删除元素
列表。这是函数式编程中的典型模式,我们使用递归来遍历列表并
删除该元素。应注意避免在无限列表的情况下堆栈溢出。未来的改进可能是使用 Java 不支持的尾递归优化,但可以使用蹦床来实现。
最后,让我们实现map和flatMap操作,将我们的List变成Monad:
从 map 和 flatMap 方法的实现中可以看出,我们使用递归调用来遍历列表并将函数应用于每个元素。 flatMap 方法有点复杂,因为它需要函数返回一个新列表,我们需要将其与列表的其余部分连接起来。由于其众所周知的难度和使用高级数据结构的重要性,这两种方法都不使用结构共享。未来的改进将在以后的文章中进行探讨。
让我们看看 SeqList 的一些使用示例。
在本文中,我们用 Java 实现了一个具有部分结构共享的持久且不可变的 LinkedList。我们演示了不变性和持久性的好处以及如何在 Java 中实现它们。我们还展示了如何将 LinkedList 转换为 Monad 以便更轻松地进行函数组合。我们讨论了持久性和不可变数据结构的优点和缺点以及如何在实践中使用它们。
以上是持久且不可变的 Java LinkedList的详细内容。更多信息请关注PHP中文网其他相关文章!