【问题标题】:Best Data Structure representing Package Installation and System Dependencies代表包安装和系统依赖的最佳数据结构
【发布时间】:2018-05-31 21:01:40
【问题描述】:

我正在尝试基于面试过程创建一个程序(我选择 Java,但可以是 C/C++ 或 GoLang)来表示/模拟 Linux/Unix 环境中存在的包安装和系统依赖关系。 基本上,我会做以下要求:

1) 维护已安装软件包及其依赖项的记录。
2) 支持显式安装包以响应命令(除非已经安装)。
3) 如果需要安装另一个包,支持隐式安装一个包。
4) 支持响应命令显式删除一个包(如果不需要支持其他包)。
5) 如果不再需要支持另一个组件,则支持隐式删除包。

在安装软件包之前,自动安装它需要的所有软件包。 在删除一个包之前,请确认没有其他包需要它。必须先手动删除依赖包,然后才能删除包。

我想要最好的数据结构(以及我可以检查的链接)的提示,我可以用它来做到这一点。我尝试使用队列列表作为存储依赖项的一种方式,并使用队列来存储已安装的包,但我不确定这是否是最好的方法,例如:

...
ArrayList<Queue<String>> dependencies = new ArrayList<>(capacity);
Queue<String> pkgInstalled = new LinkedList<String>();
...

该进程将捕获用户的条目数据,直到发出 END 命令。 命令语法为:

DEPEND item1 item2 item(n):包 item1 依赖于包 item2(和 item3 或任何;

安装 item1:安装 item1 和 item1 所需的任何其他包。

REMOVE item1:删除 item1,如果可能,删除 item1 所需的包。

LIST:列出所有当前安装的包的名称。

END:当单独在一行中使用时,标记输入的结束。

1) 在每个回显的 INSTALL 或 REMOVE 行之后执行响应所采取的操作,确保以正确的顺序给出操作。
2) 对于 LIST 命令,显示当前安装的组件的名称。
3) 对于 DEPEND 和 END 命令,除了回显之外,不产生任何输出。
4) 对于 DEPEND 命令,每一项只会有一个依赖列表。

【问题讨论】:

  • 明确地说,您的计划是使用 Java 虚拟机管理系统级包和依赖项,而不是使用任何现有的系统包管理解决方案?而且,不看他们中的任何一个是如何做到的?我佩服你的乐观。
  • 实际上并不是一个真正的系统来管理依赖和 pkg 安装。我想缺少包含我正在寻找模拟行为的内容。我选择 Java,但如果您认为另一种语言(如 go 或 C/C++)更好,那么知道会很棒
  • 说到“数据结构”,第一个想到的就是DAG

标签: java c++ c go data-structures


【解决方案1】:

我不知道本练习中Queue(或LinkedList)的值,因为您希望能够随机访问包的依赖项。

建议

Map<String, Set<String>> dependsOn = new HashMap<>();
Map<String, Set<String>> requiredBy = new HashMap<>();

这样,当你删除一个包时,你可以找到它拉入的所有包(dependsOn.get(packageToDelete)),并从它们在requiredBy中的每个条目中删除packageToDelete;如果这使 requiredBy 设置为空,那么该包也可以被删除。

我还建议在添加新根包时使用Set 来添加依赖包。处理它们的顺序并不重要,快速并避免重复更有用。

最初我认为使用唯一的完全限定包名称作为键会更简单——更容易编码和调试。这需要一种基于名称查找包的方法,但这应该已经存在。但是,如果您愿意,可以为您的 Package 类实现 equals()hashcode() 并将它们直接用作 Map 键。

为了阐明这可能是如何工作的,这里有一个例子:

public Set<String> addDependencies(Item pkg, Item... dependencies) {
    Set<String> pkgDependsOn = dependsOn.get(pkg.getFullyQualifiedName());
    if (pkgDependsOn == null) {
        pkgDependsOn = new HashSet<>();
        dependsOn.put(pkg.getFullyQualifiedName(), pkgDependsOn);
    }
    pkgDependsOn.addAll(Stream.of(dependencies).map(dep -> dep.getFullyQualifiedName()).collect(Collectors.toSet()));
    return pkgDependsOn;
}

(我可能改用了Map.merge(),但写出来后我觉得它复杂到让人困惑。)

返回生成的依赖项 Set 可能有点过头了,但我可以想象在某些情况下它可能有用。

如果您确实选择使用包本身作为键,它看起来像这样:

public Set<Item> addDependencies(Item pkg, Item... dependencies) {
    Set<Item> pkgDependsOn = dependsOn.get(pkg);
    if (pkgDependsOn == null) {
        pkgDependsOn = new HashSet<>();
        dependsOn.put(pkg, pkgDependsOn);
    }
    pkgDependsOn.addAll(Arrays.asList(dependencies));
    return pkgDependsOn;
}

我也没有做错误检查(比如如果你让一个包依赖于它自己)、空值检查等等。

【讨论】:

  • 谢谢,@DavidW。如果我为每一行包含列表命令,我正在想象这是如何工作的:DEPEND TELNET TCPIP NETCARD;依赖 TCPIP 网卡;依赖 DNS TCPIP 网卡; DEPEND BROWSER TCPIP HTML 其中 DEPEND 是命令,我可以重复,直到我想存储依赖项。
  • 附加@DavidW,我在考虑队列,因为如果安装了依赖项,我无法删除包。就像我尝试删除 NETCARD 一样,我不能这样做,因为安装了 TELNET 和 TCPIP 的依赖项。
  • @EdsonMartins 检查是否可以删除包应该像验证requiredBy.get(packageToDelete).isEmpty() 一样简单。当您删除它时,如果它留下任何其他包孤儿,您可以将它们添加到 Set 以进行下一步处理 - 同样,订单并不重要,它们已经是孤儿。
  • 很棒的@DavidW,但我的问题是关于字符串键的。应该添加什么作为地图键(Map )?在创建每一行并将其添加为 DEPEND value1 value2 value3 时,你能给我一个简单的例子吗(只是提醒我可以有多个 DEPEND 命令)?
猜你喜欢
  • 2021-01-14
  • 1970-01-01
  • 2011-08-16
  • 2014-04-11
  • 1970-01-01
  • 2014-01-08
  • 2017-01-08
  • 2013-03-08
  • 2018-07-12
相关资源
最近更新 更多