因为任何线程都没有内部合理的解释。请给予我确切的理由。1.对于插入顺序,使用单链表就足够了,但为什么不呢?1.在这种情况下,双向链表如何提高性能?1.所有的方法都是从hashmap xpt 4方法继承的,那么hashmap的迭代器不维护顺序,而linkedhashmap维护顺序?
ohfgkhjo1#
你是对的,你只需要维护一个单链表来跟踪插入顺序。但是为了有效地维护一个单链表,你实际上需要一个双向链表。按顺序考虑三个条目
A ---> B ---> C
假设你删除了B。显然,A现在应该指向C。但是,除非您知道B之前的条目,否则您无法有效地说出哪个条目现在应该指向C。要解决这个问题,需要条目指向两个方向。
B
A
C
---> ---> A B C <--- <---
这样,当您删除B时,您可以查看B之前和之后的条目(A和C)并更新,以便A和C相互指向。LinkedHashMap保持插入顺序,而HashMap没有,尽管事实上除了4个方法外,所有方法都是继承的,这是因为它写得非常巧妙。大多数特定于实现的操作都是HashMap.Entry的成员,而不是HashMap。LinkedHashMap具有private static类LinkedHashMap.Entry,它扩展了HashMap的static类HashMap.Entry。例如,当您调用put或remove时,LinkedHashMap的代码可以与HashMap的代码相同,因为是 * 条目本身 * 跟踪之前和之后的信息。例如,下面是我在上面解释的LinkedHashMap.Entry.remove()的完整代码
LinkedHashMap
HashMap
HashMap.Entry
private static
LinkedHashMap.Entry
static
put
remove
LinkedHashMap.Entry.remove()
private void remove() { before.after = after; after.before = before; }
fjnneemd2#
LinkedHashMap基本上为每个条目维护两个指针,即-:之前,之后正如名称所暗示的,指针都用于排序目的,并且用于在插入或删除的情况下调整指针。
t98cgbkg3#
为了维护插入顺序,有双重LinkedList。在任何时间点,您都可以向前移动节点或向后移动节点。但是,如果你有一个LinkedList,如果你的指针移动到最后一个元素,你又需要从初始点开始,你不能移动到前一个节点。
kiayqfof4#
我还想补充的一点是,LinkedHashMap也可以用作LRU缓存,方法是通过其构造函数将其accessOrder布尔值设置为true。现在LinkedHashMap维护两个指针head(LinkedHashMap中最年长的)和tails(LinkedHashMap中最年轻的)。因此请记住,当您将accessOrder设置为true时,它将停止维护插入顺序,并开始表现得像一个适当的LRU缓存。现在,当LRU缓存超过其限制并且我们想要删除最老的条目时,因为它在内部维护DoublyLinkedList,所以删除最老的条目相对较快,因为它维护两个指针(头部和尾部),可以在两个方向上移动,或者我们可以说如果它是用于删除最老条目的Singly LinkedList,则我们需要遍历所有节点,然后删除它。由于有了head和tails指针,它可以很容易地完成,只需删除最后一个节点(最老的一个),通过tails指针的帮助到达那里,并采取最后一个节点的前一个指针,使LinkedHashMap中DoublyLinkedList的倒数第二个节点tail。
head
tails
tail
yqkkidmi5#
其他答案涵盖了为什么目前需要双向链表。Java 21增加了一个额外的原因:一个reversed方法被添加到LinkedHashMap中,它提供了一个反向的Map视图。需要向后链接来有效地向后遍历列表以提供该视图。
reversed
ybzsozfc6#
LinkedHashMap可用于维护插入顺序和访问顺序。LinkedHashMap继承了hashmap在bucket中维护列表的相同功能,所以使用了next引用。为了保持插入顺序,他们使用了双向链表(在引用之前使用**,在引用之后使用**),但这可以通过使用单向链表来完成。同时,它们必须实现访问顺序功能,并且需要频繁地将元素移动到末尾,并且需要频繁删除频繁删除,因此它们使用了双向链表。
6条答案
按热度按时间ohfgkhjo1#
你是对的,你只需要维护一个单链表来跟踪插入顺序。但是为了有效地维护一个单链表,你实际上需要一个双向链表。
按顺序考虑三个条目
假设你删除了
B
。显然,A
现在应该指向C
。但是,除非您知道B
之前的条目,否则您无法有效地说出哪个条目现在应该指向C
。要解决这个问题,需要条目指向两个方向。这样,当您删除
B
时,您可以查看B
之前和之后的条目(A
和C
)并更新,以便A
和C
相互指向。LinkedHashMap
保持插入顺序,而HashMap
没有,尽管事实上除了4个方法外,所有方法都是继承的,这是因为它写得非常巧妙。大多数特定于实现的操作都是HashMap.Entry
的成员,而不是HashMap
。LinkedHashMap
具有private static
类LinkedHashMap.Entry
,它扩展了HashMap
的static
类HashMap.Entry
。例如,当您调用put
或remove
时,LinkedHashMap
的代码可以与HashMap
的代码相同,因为是 * 条目本身 * 跟踪之前和之后的信息。例如,下面是我在上面解释的LinkedHashMap.Entry.remove()
的完整代码fjnneemd2#
LinkedHashMap基本上为每个条目维护两个指针,即-:之前,之后
正如名称所暗示的,指针都用于排序目的,并且用于在插入或删除的情况下调整指针。
t98cgbkg3#
为了维护插入顺序,有双重LinkedList。在任何时间点,您都可以向前移动节点或向后移动节点。但是,如果你有一个LinkedList,如果你的指针移动到最后一个元素,你又需要从初始点开始,你不能移动到前一个节点。
kiayqfof4#
我还想补充的一点是,LinkedHashMap也可以用作LRU缓存,方法是通过其构造函数将其accessOrder布尔值设置为true。
现在LinkedHashMap维护两个指针
head
(LinkedHashMap中最年长的)和tails
(LinkedHashMap中最年轻的)。因此请记住,当您将accessOrder设置为true时,它将停止维护插入顺序,并开始表现得像一个适当的LRU缓存。现在,当LRU缓存超过其限制并且我们想要删除最老的条目时,因为它在内部维护DoublyLinkedList,所以删除最老的条目相对较快,因为它维护两个指针(头部和尾部),可以在两个方向上移动,或者我们可以说如果它是用于删除最老条目的Singly LinkedList,则我们需要遍历所有节点,然后删除它。由于有了
head
和tails
指针,它可以很容易地完成,只需删除最后一个节点(最老的一个),通过tails
指针的帮助到达那里,并采取最后一个节点的前一个指针,使LinkedHashMap中DoublyLinkedList的倒数第二个节点tail
。yqkkidmi5#
其他答案涵盖了为什么目前需要双向链表。Java 21增加了一个额外的原因:一个
reversed
方法被添加到LinkedHashMap
中,它提供了一个反向的Map视图。需要向后链接来有效地向后遍历列表以提供该视图。ybzsozfc6#
LinkedHashMap可用于维护插入顺序和访问顺序。LinkedHashMap继承了hashmap在bucket中维护列表的相同功能,所以使用了next引用。
为了保持插入顺序,他们使用了双向链表(在引用之前使用**,在引用之后使用**),但这可以通过使用单向链表来完成。同时,它们必须实现访问顺序功能,并且需要频繁地将元素移动到末尾,并且需要频繁删除频繁删除,因此它们使用了双向链表。