gpt4 book ai didi

java - 删除 map 条目会导致 map 条目中的对象引用可选更改

转载 作者:行者123 更新时间:2023-12-02 16:30:27 26 4
gpt4 key购买 nike

当我从 map 中检索 map 条目时,将其存储在可选项中,然后使用 remove(entry.getKey()) 从 map 中删除相同的条目,然后 Optional 突然开始指向 map 中可用的下一个 map 条目。

让我进一步解释:

我有一堆要排序的评论对象。评论列表应始终以被接受为答案的评论开头,它应该是列表中的第一个元素。 sort 方法以映射开始,并使用 entrySet 上的流来检索第一个将 acceptedAnswer boolean 值设置为 true 的评论。

Map<Long, CommentDTO> sortedAndLinkedCommentDTOMap = sortCommentsAndLinkCommentRepliesWithOwningComments(commentDTOSet);
Optional<Map.Entry<Long, CommentDTO>> acceptedAnswerCommentOptional = sortedAndLinkedCommentDTOMap.entrySet().stream()
.filter(entry -> entry.getValue().isAcceptedAsAnswer()).findFirst();

假设 Map 包含 3 条评论,其 ID 分别为 3、6 和 11。键始终是评论的 ID,而评论始终是值。标记为答案的评论的 ID 为 6。在这种情况下,将执行以下代码:

if(acceptedAnswerCommentOptional.isPresent()){
Map.Entry<Long, CommentDTO> commentDTOEntry = acceptedAnswerCommentOptional.get();
sortedAndLinkedCommentDTOMap.remove(commentDTOEntry.getKey());
}

commentDTOEntry 使用 acceptedAnswerCommentOptional 的值进行初始化时,它会引用 ID 为 6 的已接受答案。现在,当我从 sortedAndLinkedCommentDTOMap 中删除该条目时 对已接受答案评论的引用不仅从 sortedAndLinkedCommentDTOMap 中删除,而且从 acceptedAnswerCommentOptional 中删除!但是 acceptedAnswerCommentOptional 现在开始指向 sortedAndLinkedCommentDTOMap 的下一个条目,即带有 key 11 的条目,而不是变为 null。

我不明白是什么导致了这种奇怪的行为。为什么 acceptedAnswerCommentOptional 的值不直接变为 null?为什么 acceptedAnswerCommentOptional 无法在我从 map 中删除时保留对已接受答案评论的引用?

当使用 Debug模式在 intellij IDEA 中运行代码时,您可以自己看到这种行为,只要调用 remove 方法 acceptedAnswerCommentOptional 旁边的 commentDTOEntry 的解释性调试标签> 从 6 -> .... 翻转到 11 -> ....

编辑:我根据 WJS 的意愿制作了一个可重现的示例。这是代码:

import java.util.*;

import java.math.BigInteger;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.function.Function;

class CommentDTO implements Comparable<CommentDTO> {
private BigInteger id;

private BigInteger owningCommentId;

private BigInteger commenterId;

private Long owningEntityId;
private String commenterName;
private String commenterRole;
private String country;
private String thumbnailImageUrl;

private String content;
private String commentDateVerbalized;
private boolean flagged;
private Integer flagCount;
private boolean deleted;
private boolean liked;
private Integer likeCount;
private String lastEditedOnVerbalized;
private boolean acceptedAsAnswer;
private boolean rightToLeft;

private TreeSet<CommentDTO> replies = new TreeSet<>();

public CommentDTO() {
}

public CommentDTO(boolean acceptedAsAnswer, BigInteger id){
this.acceptedAsAnswer = acceptedAsAnswer;
this.id = id;
}

public CommentDTO(boolean acceptedAsAnswer, BigInteger id, BigInteger owningCommentId){
this.acceptedAsAnswer = acceptedAsAnswer;
this.id = id;
this.owningCommentId = owningCommentId;
}


public BigInteger getId() {
return id;
}

public void setId(BigInteger id) {
this.id = id;
}

public BigInteger getOwningCommentId() {
return owningCommentId;
}

public void setOwningCommentId(BigInteger owningCommentId) {
this.owningCommentId = owningCommentId;
}

public BigInteger getCommenterId() {
return commenterId;
}

public void setCommenterId(BigInteger commenterId) {
this.commenterId = commenterId;
}

public Long getOwningEntityId() {
return owningEntityId;
}

public void setOwningEntityId(Long owningEntityId) {
this.owningEntityId = owningEntityId;
}

public String getCommenterName() {
return commenterName;
}

public void setCommenterName(String commenterName) {
this.commenterName = commenterName;
}

public String getCommenterRole() {
return commenterRole;
}

public void setCommenterRole(String commenterRole) {
this.commenterRole = commenterRole;
}

public String getCountry() {
return country;
}

public void setCountry(String country) {
this.country = country;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

public String getCommentDateVerbalized() {
return commentDateVerbalized;
}

public void setCommentDateVerbalized(String commentDateVerbalized) {
this.commentDateVerbalized = commentDateVerbalized;
}

public boolean isFlagged() {
return flagged;
}

public void setFlagged(boolean flagged) {
this.flagged = flagged;
}

public Integer getFlagCount() {
return flagCount;
}

public void setFlagCount(Integer flagCount) {
this.flagCount = flagCount;
}

public boolean isDeleted() {
return deleted;
}

public void setDeleted(boolean deleted) {
this.deleted = deleted;
}

public boolean isLiked() {
return liked;
}

public void setLiked(boolean liked) {
this.liked = liked;
}

public Integer getLikeCount() {
return likeCount;
}

public void setLikeCount(Integer likeCount) {
this.likeCount = likeCount;
}

public TreeSet<CommentDTO> getReplies() {
return replies;
}

public void setReplies(TreeSet<CommentDTO> replies) {
this.replies = replies;
}

public String getLastEditedOnVerbalized() {
return lastEditedOnVerbalized;
}

public void setLastEditedOnVerbalized(String lastEditedOnVerbalized) {
this.lastEditedOnVerbalized = lastEditedOnVerbalized;
}

public String getThumbnailImageUrl() {
return thumbnailImageUrl;
}

public void setThumbnailImageUrl(String thumbnailImageUrl) {
this.thumbnailImageUrl = thumbnailImageUrl;
}

public boolean isAcceptedAsAnswer() {
return acceptedAsAnswer;
}

public void setAcceptedAsAnswer(boolean acceptedAsAnswer) {
this.acceptedAsAnswer = acceptedAsAnswer;
}

public boolean isRightToLeft() {
return rightToLeft;
}

public void setRightToLeft(boolean rightToLeft) {
this.rightToLeft = rightToLeft;
}

@Override
public int compareTo(CommentDTO o) {
return this.id.compareTo(o.id);
}

@Override
public String toString() {
return "CommentDTO{" +
"id=" + id +
", owningCommentId=" + owningCommentId +
", commenterId=" + commenterId +
", owningEntityId=" + owningEntityId +
", commenterName='" + commenterName + '\'' +
", commenterRole='" + commenterRole + '\'' +
", country='" + country + '\'' +
", thumbnailImageUrl='" + thumbnailImageUrl + '\'' +
", content='" + content + '\'' +
", commentDateVerbalized='" + commentDateVerbalized + '\'' +
", flagged=" + flagged +
", flagCount=" + flagCount +
", deleted=" + deleted +
", liked=" + liked +
", likeCount=" + likeCount +
", lastEditedOnVerbalized='" + lastEditedOnVerbalized + '\'' +
", acceptedAsAnswer=" + acceptedAsAnswer +
", rightToLeft=" + rightToLeft +
", replies=" + replies +
'}';
}
}

public class HelloWorld implements Comparable<HelloWorld> {


private Long id;

private boolean acceptedAsAnswer;


public HelloWorld(){}

public HelloWorld(boolean acceptedAsAnswer, Long id){
this.acceptedAsAnswer = acceptedAsAnswer;
this.id = id;
}

@Override
public String toString() {
return "id= " + id + " acceptedAsAnswer= " + acceptedAsAnswer;


}

public boolean isAcceptedAsAnswer(){
return acceptedAsAnswer;
}

public long getId(){
return id;
}


public static void main(String []args){
HelloWorld helloWorld = new HelloWorld();
helloWorld.doTest();

}

@Override
public int compareTo(HelloWorld o) {
return this.id.compareTo(o.id);
}

public void doTest(){

Set<CommentDTO> commentDTOSet = new HashSet<>();
commentDTOSet.add( new CommentDTO(false, BigInteger.valueOf(3)));
commentDTOSet.add( new CommentDTO(true, BigInteger.valueOf(6)));
commentDTOSet.add( new CommentDTO(false, BigInteger.valueOf(11)));
commentDTOSet.add( new CommentDTO(true, BigInteger.valueOf(7), BigInteger.valueOf(6)));
commentDTOSet.add( new CommentDTO(true, BigInteger.valueOf(8), BigInteger.valueOf(6)));


Map<Long, CommentDTO> sortedAndLinkedCommentDTOMap = sortCommentsAndLinkCommentRepliesWithOwningComments(commentDTOSet);


Optional<Map.Entry<Long, CommentDTO>> acceptedAnswerCommentOptional = sortedAndLinkedCommentDTOMap.entrySet().stream()
.filter(entry -> entry.getValue().isAcceptedAsAnswer()).findFirst();

if(acceptedAnswerCommentOptional.isPresent()){
Map.Entry<Long, CommentDTO> commentDTOEntry = acceptedAnswerCommentOptional.get();
System.out.println(commentDTOEntry.toString());
sortedAndLinkedCommentDTOMap.remove(commentDTOEntry.getKey());
System.out.println(commentDTOEntry.toString());

}
}

private Map<Long, CommentDTO> sortCommentsAndLinkCommentRepliesWithOwningComments(Set<CommentDTO> commentDTOSet){
Map<Long, CommentDTO> commentDTOMap = commentDTOSet.stream()
.collect(Collectors.toMap(comment -> comment.getId().longValueExact(), Function.identity(), (v1,v2) -> v1, TreeMap::new));
commentDTOSet.forEach(commentDTO -> {
BigInteger owningCommentId = commentDTO.getOwningCommentId();
if(owningCommentId != null){
CommentDTO owningCommentDTO = commentDTOMap.get(owningCommentId.longValueExact());
owningCommentDTO.getReplies().add(commentDTO);
}
});
commentDTOMap.values().removeIf(commentDTO -> commentDTO.getOwningCommentId() != null);
return commentDTOMap;
}

}

您可以在此处运行上面的代码:https://www.tutorialspoint.com/compile_java_online.php

编辑 2:示例代码现在重现了我的问题。

编辑 3:这行代码commentDTOMap.values().removeIf(commentDTO -> commentDTO.getOwningCommentId() != null);导致观察到的行为。已接受的答案(id 为 6 的 commentDTO)有 2 个回复。这 2 条评论(ID 为 7 和 8)由 CommentDTO 6“拥有”,并且也被 CommentDTO 6 中的 replies 列表引用。在 sortCommentsAndLinkCommentRepliesWithOwningComments() 的末尾我删除了所有可以被视为对另一条评论 owningCommentId != null 的回复的 CommentDTOs。我这样做是因为这些评论现在是从拥有评论的 replies 列表中引用的。如果我将它们留在原始 map 中,那么这些回复将出现两次。因此我删除了它们,但这会导致意外行为。我想知道为什么会这样。

最佳答案

发生这种情况是因为您使用的 map 是 TreeMap

一个TreeMap被实现为一个红黑树,它是一个自平衡的二叉树。

map 的条目用作树的节点。

如果您删除一个条目,那么树必须重新平衡自身,然后可能会发生该条目用于指向取代它的节点的情况。

TreeMap.entrySet()由 map 支持,变化反射(reflect)在集合中。

更改还取决于您要删除的节点,例如,如果它是一个叶子,那么它可能只是从树上取消链接并且条目不受影响。

如果您使用另一个映射实现,例如 HashMap,那么您将不会得到这种行为。

顺便说一句,这是一个更简单的例子,它甚至不涉及Optional 或自定义类:

Map<Long, String> map = new TreeMap<>();
map.put(1L, "a");
map.put(2L, "b");
map.put(3L, "c");
map.put(4L, "d");
map.put(5L, "e");
map.put(6L, "f");

Map.Entry<Long, String> entry = map.entrySet().stream()
.filter(e -> e.getKey().equals(4L))
.findFirst()
.get();

System.out.println(entry); // prints 4=d
map.remove(entry.getKey());
System.out.println(entry); // prints 5=e

关于java - 删除 map 条目会导致 map 条目中的对象引用可选更改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63585372/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com