如何解决删除地图条目会导致地图条目中的对象引用可选更改
当我从地图检索地图条目时,将其存储在可选项中,然后使用remove(entry.getKey())从地图中删除该条目,然后Optional
突然开始指向下一张地图地图中可用的条目。
让我进一步解释:
我有一堆要排序的注释对象。评论列表应该始终以被接受为答案的评论开始,它应该是列表中的第一个元素。排序方法从映射开始,并在entrySet上使用流来检索第一个注释,该注释的acceptedAnswer
布尔值设置为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,and 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
的条目。
我不明白是什么导致这种奇怪的行为。为什么acceptedAnswerCommentOptional
的值不能简单地变成null
?为什么acceptedAnswerCommentOptional
不能在我从地图上删除引用的评论时保留引用?
在remove
方法被称为acceptedAnswerCommentOptional
触发器旁边的6 -> ....
的解释性调试标签后,您便可以在使用调试模式在intellij IDEA中运行代码时自己看到此行为。 }}到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()
的末尾,我删除了所有CommentDTOs
可以认为是对另一条评论owningCommentId != null
的答复。我这样做是因为现在从拥有注释的replies
列表中引用了这些注释。如果我将其留在原始地图中,则这些答复将出现两次。因此,我删除了它们,但是这导致了意外的行为。我想知道为什么会这样。
解决方法
发生这种情况是因为您使用的地图是TreeMap
。
TreeMap
被实现为红黑树,这是一种自平衡二叉树。
地图的条目用作树的节点。
如果删除一个条目,则树必须重新平衡自身,然后可能会发生该条目用于指向代替它的节点的情况。
由于TreeMap.entrySet()
得到了地图的支持,所以更改会反映在集合中。
所做的更改还取决于要删除的节点,例如,如果它是叶子,则它可能只是与树断开链接,并且条目不受影响。
如果您使用其他地图实现(例如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
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。