为什么创建对象的副本仍会更改原始对象的实例变量?

时间:2020-10-23 08:01:56

标签: java queue priority-queue

我有两节课。类Algorithm实现了方法findIntersections(),该方法是sweep line algorithm,用于检查O(nLogN)时间的交集。

它还实现了addSegments函数,该函数根据x坐标将Segment类型(两点)的对象添加到优先级队列。

public class Algorithm {
 
    public PriorityQueue pQueue = new PriortiyQueue();

    //This function adds objects of type Segments to a priority queue
    public void addSegments(List<Segment> segments) {
        pQueue.add(segments);
        //do something
    }

    //This function implements a sweep line algorithm to check for Intersections.
    public void findIntersection() {
        while (!pQueue.isEmpty()) {
            p.poll(); //removes element from Queue
            // do something
        }
    }
}

另一个类Model将数据从CSV文件加载到优先级队列中。这是一个密集的过程,我只想做一次。

另一方面,checkForCollissions被称为数百万次。

  • 我想检查所提供的段与从csv文件添加到优先级队列中的其余段之间是否存在冲突

  • 我不想每次都从头开始将元素添加到优先级队列。这是不可行的。

     public class Model  {
         public Algorithm algoObj = new Algorithm();
         public ArrayList<Segment> algoObj = new ArrayList<>();
         public ArrayList<Segment> segments = new ArrayList<>();
         public ArrayList<Segment> single_segment = new ArrayList<>();
    
         public boolean loadCSV() {
             //read csv file
             while ((strLine = br.readLine()) != null) {
               segments.add(new Segment()); //Add all segments in CSV file to ArrayLisyt
               algo.addSegments(segments);  //Adds 4000 objects of type segment to priority Queue
             }
         }
    
         //This function is called millions of times
         public boolean checkForCollisions(segment_to_check) {
             single_segment.add(segment_to_check);    //Add 1 segment. 
             algoObj.addSegments(single_segment);     //Adds 1 object of type segment to priority Queue
             algoObj.findIntersection();
             single_segment.remove(new Segment()); //Remove above segment to get back to original data
         }
     }
    

TL; DR

我遇到的问题是,在checkForCollisions的第一次调用之后,优先级队列已更改,因为findIntersection()通过轮询队列中的元素来工作,从而改变了队列。

如何防止algoObj.addSegments()创建的优先级队列在函数调用之间进行更改?

这是否需要像here所述进行浅层复制和深层复制?


我尝试在函数的开头创建队列的副本,然后更改副本:

        public boolean checkForCollisions(segment_to_check) {
            Algorithm copy = algoObj;
            single_segment.add(segment_to_check);    //Add 1 segment. 
            copy.addSegments(single_segment);     //Adds 1 object of type segment to priority Queue
            copy.findIntersection();
            single_segment.remove(new Segment()); //Remove above segment to get back to original data
        }
    }

但是,此操作不起作用,因为它仍会更改原始algoObj的优先级队列。

我认为这是一个初学者的问题,原因是我在使用面向对象语言时缺乏适当的理解。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

首先,至关重要的是要知道将现有对象分配给另一个变量不会创建原始对象的副本:

MyObject a = new MyObject();
MyObject b = a; // does NOT create a copy!
// now a and b "point" to the same single instance of MyObject!

关于您实际问题的一些想法:

您的优先级队列只是一个有效的数据结构,用于交叉算法,并且算法正在运行。完成后(因此已找到交集),如您已经写过的,它为空或至少已更改。因此,必须为每次运行的算法重新创建优先级队列。

那你应该做什么:

  1. 将分段从CSV文件加载到您的ArrayList中,但尚未将其传递到优先级队列。

  2. 每次调用findIntersection()之前都要重新填充(或重新创建)优先级队列。最好通过将所有段传递给该方法并从头开始创建新的优先队列来做到这一点:

    public void findIntersection(Collection<Segment> segments) {
        PriorityQueue<Segment> pQueue = new PrioerityQueue<Segment>(segments);
        while (!pQueue.isEmpty()) {
            p.poll(); //removes element from Queue
            // do something
        }
    }
    

提示:正如我在开始时已经写的那样,这不会复制单个句段或句段集合。它只是传递参考。当然,优先级队列必须在构建时创建内部结构,因此,如果路段集合很大,则可能需要一些时间。

如果该解决方案太慢而无法满足您的需求,则您将不得不研究算法。您真的需要经常检查交叉路口吗?如果仅将一个线段添加到列表中,则检查与其他线段的相交就足够了,但如果其他线段彼此相交则不足够。可能您可以将您的细分存储在类似于Bentley-Ottmann算法的二进制搜索树中。每当有新的分段“到达”时,就可以根据搜索树进行检查,这应该是可行的,时间复杂度约为O(log n)。之后,如有必要,可以将该段插入到树中。

或者您可以先添加所有路段,然后仅检查一次交叉点。