删除地图条目会导致地图条目中的对象引用可选更改

如何解决删除地图条目会导致地图条目中的对象引用可选更改

当我从地图检索地图条目时,将其存储在可选项中,然后使用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 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-