为什么我的ArrayList填充了重复的节点值?

时间:2014-02-16 10:42:39

标签: java arraylist

对于节点5,它应该简单地为5 = [6],为什么有3个不同的值。另外,我知道,这个当前的代码是错误的大时间,因为当我添加像2,1和2,3这样的边时它甚至会做2 = [3,3]。它只是用旧的值替换旧值并复制它。这是带有结果的图像。

Graph.java

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;


public class Graph {

    HashMap <Integer, ArrayList<Integer>> adj =new HashMap<Integer, ArrayList<Integer>>();

    public void addEdge(int source, int destination) {

        ArrayList<Integer> list = new ArrayList<Integer>();
        adj.put(source,list);
        list.add(destination);

        for(Entry<Integer, ArrayList<Integer>> entry: adj.entrySet()) {
            int existing = entry.getKey();
            System.out.println("Existing: "+existing);
            System.out.println("Source: " +source);
            if (source != existing) {
                //adj.get(2).add(9);
                //System.out.println(source +": "+ destination);
                adj.get(source).add(destination); 

            }

        }


    }


    public void print() {
        System.out.println(adj);
    }


}

AdjacencyList.java

import java.util.Scanner;



public class AdjacencyList {

    public static void main(String[] args) {

        Scanner in = new Scanner (System.in);
        System.out.println("Enter nodes below like (u,v):");

        Graph g = new Graph();

        for (int i=0; i<3; i++)  {

            String nodes = in.nextLine();
            String[] data = nodes.split(",");

            String u    = data[0]; 
            String v    = data[1];
            int inNode  = Integer.parseInt(u);
            int outNode =Integer.parseInt(v);

            g.addEdge(inNode,outNode);
            System.out.println("Added to list");

        }


          g.print();

    }

}

1 个答案:

答案 0 :(得分:1)

据我所知,您正在更新Map source -> List<destination>

您添加了一个新的source节点,并将其作为sources映射到所有现有destination

所以,例如我有

1 -> [2,3]
2 -> [1,3]
3 -> [1,2]

如果我通过调用addEdge(4, 5)添加新映射,我最终应该

1 -> [2,3,5]
2 -> [1,3,5]
3 -> [1,2,5]
4 -> [5]

这是对的吗?

在这种情况下,您的代码存在缺陷。首先请注意:

adj.get(source).add(destination); 

始终destination添加到属于源节点的List<destination>。所以当你在上面的例子中调用addEdge(4, 5)时(为了简单起见假设迭代):

  1. entry.key()11 != 4,将5添加到adj.get(4)
  2. entry.key()22 != 4,将5添加到adj.get(4)
  3. entry.key()33 != 4,将5添加到adj.get(4)
  4. entry.key()44 == 4,noop。
  5. 所以你最终得到:

    1 -> [2,3]
    2 -> [1,3]
    3 -> [1,2]
    4 -> [5,5,5]
    

    要解决这个问题,我需要知道您对给定初始状态和输入的预期输出是什么 - 即我需要算法。猜测我会说你想这样做:

    Map<Integer, List<Integer>> adj = new HashMap<>();
    
    public void addEdge(int source, int destination) {
        List<Integer> list = new ArrayList<>();
        adj.put(source, list);
        list.add(destination);
        for (final Entry<Integer, List<Integer>> entry : adj.entrySet()) {
            if (source != entry.getKey()) {
                entry.getValue().add(destination);
            }
        }
    }
    

    即。

    1. entry.key()11 != 4,将5添加到adj.get(1)
    2. entry.key()22 != 4,将5添加到adj.get(2)
    3. entry.key()33 != 4,将5添加到adj.get(3)
    4. entry.key()44 == 4,noop。
    5. 我还将代码更改为程序到interface ,并使用Java 7菱形语法来提高可读性。

      在这种情况下,您需要完成检查的唯一原因是您在循环之前将put destination放入您创建的新List中。我可以建议:

      public void addEdge(int source, int destination) {
          if (adj.get(source) == null) {
              adj.put(source, new ArrayList<>());
      
          }
          for (final Entry<Integer, List<Integer>> entry : adj.entrySet()) {
              entry.getValue().add(destination);
          }
      }
      

      即。我们检查source的映射是否为null,如果是,我们将new ArrayList添加到adj。然后,我们遍历所有映射,并将目标添加到每个映射。结果将与上述相同。

      以下是新代码的一些输出:

      public static void main(final String[] args) throws Exception {
          System.out.println(adj);
          addEdge(1, 2);
          System.out.println(adj);
          addEdge(3, 7);
          System.out.println(adj);
          addEdge(4, 2);        
          System.out.println(adj);
      }
      

      输出:

      {}
      {1=[2]}
      {1=[2, 7], 3=[7]}
      {1=[2, 7, 2], 3=[7, 2], 4=[2]}
      

      所以可能还有另一个错误 - 即如果我将相同的目的地放在两次,我会以这种方式复制。如果这不是您想要的,则需要进行小的更改:

      Map<Integer, Collection<Integer>> adj = new HashMap<>();
      
      public void addEdge(int source, int destination) {
          if (adj.get(source) == null) {
              adj.put(source, new LinkedHashSet<>());
      
          }
          for (final Entry<Integer, Collection<Integer>> entry : adj.entrySet()) {
              entry.getValue().add(destination);
          }
      }
      

      更改Map以保留常规Collection并添加LinkedHashSet而不是ArrayListLinkedHashSet维护订单(Linked)并且不允许重复元素(Set)。

      输出现在是:

      {}
      {1=[2]}
      {1=[2, 7], 3=[7]}
      {1=[2, 7], 3=[7, 2], 4=[2]}
      

      修改

      继OP的评论

      输入以下节点,如(u,v):2,1添加到列表2,3添加到列表5,6添加到列表{2 = [3,6],5 = [6]} < / em>的

      这似乎更简单。 OP希望维护一个简单的映射 - 并且只更新相关的source -> destination并且不允许重复的目标。代码是:

      Map<Integer, Collection<Integer>> adj = new HashMap<>();
      
      public void addEdge(int source, int destination) {
          //see if adj contains source already
          Collection<Integer> destinations = adj.get(source);
          //if not add in a new LinkedHashSet
          if (destinations == null) {
              destinations = new LinkedHashSet<>();
              adj.put(source, destinations);
          }
          //Set returns false if item exists
          if (!destinations.add(destination)) {
              System.out.println("This mapping already exists.");
          }
      }
      

      输出:

      {}
      {1=[2]}
      {1=[2], 3=[7]}
      {1=[2], 3=[7], 4=[2]}
      {1=[2], 3=[7], 4=[2, 3]}
      This mapping already exists.
      {1=[2], 3=[7], 4=[2, 3]}