【问题标题】:Check if Object exists in ArrayList in Java检查Java中的ArrayList中是否存在Object
【发布时间】:2016-01-24 13:56:47
【问题描述】:

我有以下对象列表:

private List<Object> teamlist = new ArrayList<Object>();

我正在将对象添加到列表中,如下所示:

teamlist.add(new MCWarTeam(args[0], joinkey));

现在列表中的对象没有名字,但是可以通过列表来引用,对吧?在向列表中添加新元素之前,如何检查具有某个属性的对象是否已经存在?这是对象的构造函数:

public MCWarTeam(String teamname, String joinkey){
    this.teamname = teamname;
    this.joinkey = joinkey;
}

我想检查是否已经有一个名为 teamname 的团队。或者,是否有更好的方法来存储对象?之前,我只是使用 HashMap 来添加团队名称和 joinkey,效果很好,但我认为使用 Objects 会更好。

这是事件处理程序的重要代码:

        else if (cmd.getName().equalsIgnoreCase("createTeam")) {
        if (args.length > 0 && args.length < 3) {
            String joinkey = "";
            if (args.length > 1)
                joinkey = args[1];

            String teamname = args[0];

            MCWarTeam newTeam = new MCWarTeam(teamname, joinkey);
            if (!teamlist.containsKey(teamname)) {
                teamlist.put(teamname, newTeam);
                sender.sendMessage("Created new team \"" + teamname + "\" with join key \"" + joinkey + "\" successfully! Teams:");

                sender.sendMessage("All teams:");
                for (String key : teamlist.keySet()) {
                    sender.sendMessage(key);
                }

            } else
                sender.sendMessage("Team already exists!");
            return true;
        }
        return false;
    }

    else if (cmd.getName().equalsIgnoreCase("joinTeam")) {
        if (args.length > 0 && args.length < 3) {
            String joinkey = "";
            if (args.length > 1)
                joinkey = args[1];

            String teamname = args[0];

            if (teamlist.containsKey(teamname)) {
                String teamKey = teamlist.get(teamname).getJoinKey();
                if (joinkey == teamKey) {
                    teamlist.get(teamname).addPlayer(playername);
                    Bukkit.broadcastMessage("MCWar: " + playername + " joined Team \"" + teamname + "\" successfully!");
                } else
                    sender.sendMessage("Join key incorrect!");
            } else {
                sender.sendMessage("Team doesn't exist! Teams:");
                for (String key : teamlist.keySet()) {
                    sender.sendMessage(key);
                }

            }
            return true;
        }
        return false;
    }

基本上,如果它返回 false,用户将收到一条消息,说明他输入的命令的正确用法。

【问题讨论】:

  • 你在 MCWarTeam 中覆盖 equals 吗?如果是这样,只需使用List.contains
  • 您可能还想将列表键入&lt;MCWarTeam&gt; 而不是&lt;Object&gt;。另外我不知道您是如何认为ArrayList 本质上比HashMap 更好的,但我希望您基于真实的东西而不是直觉。
  • specification有那么难吗?

标签: java object arraylist bukkit


【解决方案1】:

Java 的 List&lt;T&gt; 有一个 boolean contains(Object) 方法,在您希望避免重复的情况下非常方便:

if (!teamlist.contains(newTeam)) {
    teamlist.add(newTeam);
} 

MCWarTeam 类必须实现 equals 才能使其工作。覆盖equals 时,还必须覆盖hashCode

@Override
public boolean equals(Object obj) {
    if (!(obj instanceof MCWarTeam))  {
        return false;
    }
    MCWarTeam other = (MCWarTeam)obj;
    return teamname.equals(other.teamname)
        && joinkey.equals(other.joinkey);
}
@Override
public int hashCode() {
    return 31*teamname.hashCode()+joinkey.hashCode();
}

我只是想检查具有相同teamnameObject 是否已经存在,但不关心joinkey

如果joinkey 不是影响相等性的对象状态的一部分,则将其作为字段作为对象的一部分保留通常不是一个好主意。例如,如果joinkey 是临时的,您可以使用它来将团队“连接”到其他事物,则制作HashMap&lt;String,MCWarTeam&gt;,使用joinkey 作为地图的键,并从MCWarTeam 中删除joinkey 应该是好主意。

【讨论】:

  • joinkey 类似于用户必须输入才能成为团队成员的密码,但我知道这个名称可能会产生误导。你能向我解释一下MCWarTeam other = (MCWarTeam)obj; return teamname.equals(other.teamname); 是如何查看列表中是否已经存在同名对象的吗?
  • @MartinHoffmann teamlist.contains(newTeam) 方法在后台调用 equals(Object) 方法,将 newTeam 与列表中已有的进行比较。
  • 如何从teamname 获取字符串joinkey?我尝试了以下方法:String teamKey = teamlist.get(teamlist.indexOf(teamname)).getJoinKey(); 并尝试在indexOf() 中使用具有相同名称的测试 MCWar 对象代替 teamname,但这也不起作用,我是否也必须 @Overwrite indexOf() ?
【解决方案2】:

根据描述和您对其他答案的 cmets,最好不要使用 List,而是将您的数据存储在 Map&lt;String, MCWarTeam&gt; 中,它将团队名称映射到 MCWarTeam 对象:

private Map<String, MCWarTeam> teams = new HashMap<>();

你可以添加一个团队,检查是否已经存在同名的团队,像这样:

String teamName = args[0];

if (!teams.containsKey(teamName)) {
    teams.put(teamName, new MCWarTeam(teamName, joinKey));
} else {
    // do what you want when the team name was already in the map
}

根据团队名称检索 MCWarTeam 对象,例如访问joinKey 属性很简单:

String joinKey = teams.get(teamName).getJoinKey();

注意,使用这种方法,你不应该在MCWarTeam中实现equalshashCode,因为you aren't gonna need it;由于您的地图键是团队名称,containsKey 对已经具有明确定义的 equalshashCode 语义的 String 对象进行操作。

【讨论】:

  • 这似乎是我见过的最好的方法。我在家的时候试试,谢谢
  • 我在这里遇到了一个奇怪的错误。在一个函数中,我使用teamlist.put(teamname, newTeam); 添加团队。现在,每次我通过teamlist.containsKey(teamname) 检查条目是否存在时,它都会显示为空。当我让它通过一个循环运行时没有结果显示,但是当我在添加一个元素后运行完全相同的循环时,所有添加的元素都会显示出来。编辑:这是我互相调用 /createteam 和 /jointeam prntscr.com/8vpdnk
  • 如果没有看到代码,很难说那里可能发生什么。您能否编辑您的问题并将代码发布到您添加团队/检查条目的位置?
  • 我做到了,谢谢。上面定义了 HashMap 团队列表。
  • 我注意到的一件事是您将连接键 Strings 与 == 进行比较;你应该改用equals。您可以尝试将比较更改为joinkey.equals(teamKey),看看它是否有任何效果。除此之外,仍然很难说您的实际问题是什么。我不熟悉 Bukkit 或其事件处理框架,但如果事件被异步处理,这里可能还会出现一些计时问题。发布更多代码将有助于跟踪问题。另外,请在所有ifs 和elses 中使用大括号({ })(即使是包含单个语句的)。
【解决方案3】:

为了在 ArrayList 中搜索 MCWarTeam 实例,您首先必须覆盖 equals 以定义两个 MCWarTeam 实例彼此相等的含义。然后可以使用indexOf(team)contains来判断一个实例是否在List中。

但是,这样的搜索需要线性时间,因此 HashSet 可能更适合您的需求(为此,您需要同时覆盖 equalshashCode,并且您将能够在恒定时间内查找对象是否在Set 中)。

【讨论】:

    【解决方案4】:

    如果你正确地实现了MCWarTeamequals方法,那么contains应该会告诉你对象是否存在。

     boolean exists = teamlist.contains(member);
    

    正如@Eran 提到的,HashSet 会为您提供O(1) 查找,其中list containsO(n),唯一的问题是HashSet 不允许重复。 是的,使用实际类型而不是Object

    List<MCWarTeam> teamlist = new ArrayList<>();
    

    【讨论】:

    • Set 对于 OP 来说可能是不切实际的,因为即使 contains 表现良好,检索元素的唯一方法是通过迭代;没有get 方法。出于这个原因,Map&lt;String,MCWarTeam&gt; 可能是最好的选择。
    猜你喜欢
    • 2011-05-23
    • 2016-02-27
    • 2018-11-16
    • 1970-01-01
    • 2020-11-28
    • 1970-01-01
    • 1970-01-01
    • 2020-04-22
    相关资源
    最近更新 更多