Java HashMaps和putAll()方法的问题

时间:2011-03-15 18:21:20

标签: java hashmap

嘿大家, 我正在编写一个程序来比较各种Dijkstra实现的运行时间。我从文本文件中解析相关信息并将其输入哈希表。在我当前的实现中,我有一个'master'哈希表,它包含Node信息,并且该信息被复制到一个临时哈希表中,然后传递给不同的Dijkstra类。

public class Router
{
    public static Map <Integer, Node> nodes = new HashMap<Integer, Node>();
    public static Map <Integer, Node> temp = new HashMap<Integer, Node>();
    public static int target = 2;
    public static int start = 0;

    public static void main(String[] args) throws IOException
    {
        Parser p3 = new Parser(args[0], nodes, p);
        temp.putAll(nodes);

        // Test 1
        DijkstraFib fb = new DijkstraFib(temp, start, target, p);
        temp.clear();
        temp.putAll(nodes);

        //Test 2
        DijkstraBinary d = new DijkstraBinary(temp, start, target, p);
        temp.clear();
        temp.putAll(nodes);

        // Test 3
        Dijkstra b = new Dijkstra(temp, start, target, p);
    }

}

因此,不得不为每个Dijkstra类调用三次解析器,我只想将nodes表中的所有值复制到temp,因为temp在此期间被修改方法执行。前两个测试运行正常,但第三个测试失败。如果我交换测试2和测试3,那么测试2失败;基本上,无论哪个测试最后运行,都会失败。这是我的Dijkstra方法:

void route(Map <Integer, Node> nodes, int source)
{
    Node start = nodes.get(source);
    start.distance = 0;
    unsettledNodes.add(start);

    while(!unsettledNodes.isEmpty())
    {
        Node n = extractMin();
        visited.put(n.name, n);
        relax_neighbours(n, nodes);
    }
}

void relax_neighbours(Node n, Map <Integer, Node> nodes)
{
    for (int i = 0; i < n.outgoing.size(); i++)
    {
        Edge edge = n.outgoing.get(i);
        Node v = nodes.get(edge.node);

        if (isSettled(v))
        {
            continue;
        }

        double shortDist = n.distance + edge.length;
        if (shortDist < v.distance)
        {
            unsettledNodes.remove(v);
            v.distance = shortDist;
            v.previous = n;
            unsettledNodes.add(v);
        }
    }
}

这是我的方法,它打印从目标节点开始的路径:

 void print_path(Map <Integer, Node> visited, int i)
{
    ArrayList<Node> path = new ArrayList<Node>();

    for (Node target = visited.get(i); target != null; target = target.previous)
    {
        path.add(target);
    }
    System.out.println("Simple Dijkstra took " + this.time_taken + " ms");
    System.out.print("Min dist from " + this.source + " to " + this.target + " = " + path.get(0).distance + " : ");

    Collections.reverse(path);

    System.out.print(path.get(0).name);
    p.append(Integer.toString(path.get(0).name));
    for (int k = 1; k < path.size(); k++)
    {
        System.out.print(" -> " + path.get(k).name);
    }
    System.out.println();
}

这是控制台输出:

Edges: 948464
Fibonacci Dijkstra took 340 ms
Min dist from 0 to 2 = 89.0 : 0 -> 195 -> 5523 -> 5504 -> 5870 -> 5835 -> 3076 -> 15319 -> 5588 -> 5911 -> 2

Binary Dijkstra took 302292 ms
Min dist from 0 to 2 = 89.0 : 0 -> 195 -> 5523 -> 5504 -> 5870 -> 5835 -> 3076 -> 15319 -> 5588 -> 5911 -> 2

Simple Dijkstra took 0 ms
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
   at java.util.ArrayList.rangeCheck(ArrayList.java:571)
   at java.util.ArrayList.get(ArrayList.java:349)
   at Dijkstra.print_path(Dijkstra.java:115)
   at Dijkstra.<init>(Dijkstra.java:25)
   at Router.main(Router.java:33)

2 个答案:

答案 0 :(得分:3)

如果没有看到更多的代码,我无法确定,但看起来当你将所有代码放在新地图中时,你会传递相同的对象。也就是说,从调用到调用引用相同的节点,而temp是节点中值的副本。如果您的方法正在修改最短路径算法的路径权重(即,如果Node是可变的),那么您将遇到问题。

一种选择是通过创建新节点并填充所有相关信息为您的节点编写深层复制方法。然后,对于算法的每次新运行,您将获得节点的“新”副本

答案 1 :(得分:2)

异常(java.lang.IndexOutOfBoundsException)表示您尝试获取数组中超出范围的元素。

问题出在方法print_path中的115行:

at Dijkstra.print_path(Dijkstra.java:115)

尝试使用调试器来了解遇到异常时变量的状态。为此,在115行的方法print_path中设置断点,并检查为什么从非现有索引中获取元素。

<强>编辑: 问题是因为当你调用putAll()时你会引用你的主节点,之后你的算法修改节点(实际上在你的节点HashMap 中),这将在下一次测试中传播。