最轻量图建模用Map<Integer, List<Integer>>,须用computeIfAbsent初始化List防NPE;带权则用Map<Integer, List<int[]>>;遍历时删边需Iterator或批量remove;小范围连续编号优先用int[][]数组;并发写需节点级锁或CopyOnWriteArrayList。

如何在Java中使用集合实现图结构_邻接表与Map套List的基础图论建模

Map<Integer, List<Integer>> 建邻接表,别直接套 HashMap 就完事

Java 里最轻量的图建模方式就是 MapList,但直接写 new HashMap<>() 容易在增删边时触发 NullPointerException——因为没初始化对应节点的 List

正确做法是每次访问前确保 List 存在:

遍历时别在 for-each 里改 List,否则 ConcurrentModificationException

DFS/BFS 过程中常要动态删边(比如拓扑排序删入度为 0 的节点所连出的边),但直接在 for (int v : graph.get(u)) 里调 remove() 会炸。

根本原因是 ArrayList 的迭代器是 fail-fast 的,而 computeIfAbsent 返回的也是普通 ArrayList

Integer 作键有装箱开销,小范围图优先用 int[] 数组模拟

节点编号若连续且已知上限(比如 1~10000),用 List<Integer>[] graph = new ArrayList[n+1]Map 快不少;但注意 Java 不允许泛型数组,得加 @SuppressWarnings("unchecked")

更干净的做法是用原始类型数组:List<Integer>[] graph → 改成 int[][] graph,每个 graph[u] 是一维数组存邻居,长度固定或用额外 int[] size 记录实际边数。

多线程读写必须加同步,但别锁整个 Map

并发环境下,多个线程同时往不同节点加边,如果只用 ConcurrentHashMap,仍可能因 computeIfAbsent + add 非原子而出错。

例如两个线程同时对节点 ucomputeIfAbsent,可能都创建新 ArrayList,后者覆盖前者,导致丢边。

图建模最麻烦的从来不是怎么存,而是“什么时候该初始化”和“谁负责清理”。尤其是稀疏图里大量节点只出不进,Map 键集会悄悄膨胀,得定期 keySet().removeIf(k -> graph.get(k).isEmpty()) ——这步很多人忘了。

本文转载于:互联网 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。