当前位置: 首页 > 知识库问答 >
问题:

三合会不来打架?(Java集缺少一项)

文心思
2023-03-14

我有来自asoft和bsoft两家公司的代码。我也不能改变。这是我的情况的一个简化版本,我很确定它有足够的信息来找出问题的原因。

bsoft提供了IGang,它代表一个可以与其他帮派作战的帮派。

package bsoft;

public interface IGang {
    /** @return negative, 0, or positive, respectively
     *          if this gang is weaker than, equal to, or stronger
     *          than the other
     */
    public int compareTo(IGang g);
    public int getStrength();
    public String getName();
    public void attack(IGang g);
    public void weaken(int amount);
}

asoft提供Gangwar,允许IGang进行战斗:

package asoft;
import java.util.*;
import bsoft.*;
/** An `IGang` ordered by identity (name) */
public interface ComparableGang extends IGang, Comparable<IGang> {}

package asoft;
import java.util.*;

public class GangWar {
    public final Set<ComparableGang> gangs = new TreeSet<ComparableGang>();
    public void add(ComparableGang g) {gangs.add(g);}
    public void doBattle() {
        while (gangs.size() > 1) {
          Iterator<ComparableGang> i = gangs.iterator();
          ComparableGang g1 = i.next();
          ComparableGang g2 = i.next();
          System.out.println(g1.getName() + " attacks " + g2.getName());
          g1.attack(g2);
          if (g2.getStrength() == 0) {
              System.out.println(g1.getName() + " smokes " + g2.getName());
              gangs.remove(g2);
          }
          if (g1.getStrength() == 0) {
              System.out.println(g2.getName() + " repels " + g1.getName());
              gangs.remove(g1);
          }
        }
        for (ComparableGang g : gangs) {
            System.out.println(g.getName() + " now controls the turf!");
        }
    }
}

它需要额外的约束,即您提供给它的具有可比性,因此它可以按名称排序或避免重复。每个帮派(以任意顺序,为简单起见,这里使用Set-order)攻击另一个帮派,直到只剩下一个帮派(如果最后两个帮派平手,则没有帮派)。我编写了一个简单的ComparableGang实现来测试它:

import asoft.*;
import bsoft.*;
import java.util.*;

class Gang implements ComparableGang {
    final String name;
    int strength;

    public Gang(String name, int strength) {
        this.name = name;
        this.strength = strength;
    }

    public String getName() {return name;}
    public int getStrength() {return strength;}

    public int compareTo(IGang g) {
        return strength - g.getStrength();
    }

    public void weaken(int amount) {
        if (strength < amount) strength = 0;
        else strength -= amount;
    }

    public void attack(IGang g) {
        int tmp = strength;
        weaken(g.getStrength());
        g.weaken(tmp);

    }

    public boolean equals(Object o) {
      if (!(o instanceof IGang)) return false;
      return name.equals(((IGang)o).getName());
    }
}

class Main {
   public static void main(String[] args) {
       GangWar gw = new GangWar();
       gw.add(new Gang("ballas", 2));
       gw.add(new Gang("grove street", 9));
       gw.add(new Gang("los santos", 8));
       gw.add(new Gang("triads", 9));
       gw.doBattle();
   }
}

正在测试它。。。

$ java Main
ballas attacks los santos
los santos repels ballas
los santos attacks grove street
grove street repels los santos
grove street now controls the turf!

问题是,黑社会没有出现在战斗中。事实上,在doBattle()的开头打印gangs.size()返回3而不是4。为什么啊?怎么修?

共有3个答案

吕森
2023-03-14

帮派。相比之下,方法基于它们的强度,因此由于三和弦格罗夫街具有相同的强度,树集认为它们相等,然后将它们删除。

根据比较组期望如何对它们进行排序,我会说忽略IGang接口对compareTo行为的请求,并将其更改为这个。

public int compareTo(IGang g) {
    return name.compareTo(g.getName());
}
贝财
2023-03-14

TL;博士:使用下面的B)项

来自html" target="_blank">javadoc的Comparable(Comparator也是如此!):

类C的自然排序被认为是与等于一致的当且仅当e1.compareTo(e2)==0对于每个e1具有与e1.equals(e2)相同的布尔值和C类的e2。请注意,null不是任何类的实例,e.compareTo(null)应该抛出一个NullPointerExcture,即使e.equals(null)返回false。

在您的情况下(简化),

  • equals定义为name
  • compareTo定义为强度的比较

这不符合上述条件:

  • 当两个强度s相等,但两个names不同时
  • 当两个names相等,但两个的强度s不同时(可能是应用程序逻辑避免的条件)

如何纠正?

A) 如果您的要求允许您按名称对集合进行排序(与asoft代码中的注释一致):

 // will only return 0 if the strengths are equal AND the names are equal
 public int compareTo(ComparableGang g) {
     return name.compareTo(g.getName());
 }

B) 如果您的需求迫使您按强度(然后是名称)对集合进行排序(与bsoft代码中的注释一致)。

 // will return 0 if & only if the strengths are equal AND the names are equal
 public int compareTo(ComparableGang g) {
     int result = strength - g.getStrength();
     if (result == 0) result =  name.compareTo(g.getName());
     return result;
 }

 // will return true if & only if the strengths are equal AND the names are equal
 public boolean equals(Object o) {
     if (!(o instanceof ComparableGang)) return false;
     ComparableGang gang2 = (ComparableGang)o;
     return name.equals(gang2.getName()) && strength == gang2.getStrength();
 }

 // For this case, if it's illegal to have two gangs of same name but different 
 // strength (it should be illegal!), then app logic must enforce this - the Set 
 // no longer will.

评论1:虽然修改asoft的GangWar类对您来说是一个问题,但如果您可以将上述两种B)方法放入:

 class ComparableGangComparator implements Comparator<ComparableGang> {
 }

然后修改如何Gangwar构造Set:

 public final Set<ComparableGang> gangs = new TreeSet<ComparableGang>(
                                                  new ComparableGangComparator());

这样,你就可以把A)的两个方法留在类组中——让类保持它的“真”相等

评论2:对asoft的矛盾评论

从理论角度来看:如果asoft的评论不是打字错误,那么asoft不仅扩展了bsoft的接口,而且还改变了其中一种方法所需的行为。事实上,这根本不是一个矛盾,而是一种凌驾:asoft的评论“获胜”。

从一个实际的角度来看:你需要祈祷,因为福特是故意这么做的

姬昀
2023-03-14

问题是,三合会不会出现在战斗中。事实上,印刷团伙。doBattle()开头的size()返回3而不是4。为什么?

Triadgrove street的强度均为9。因此,它们在组方面是相等的。比较到(实现可比较)。因此,树集中只允许一个。

如果您不想删除排序顺序重复的项,请不要使用TreeSet...

EDIT:比较帮派接口描述指示了预期的内容:

/** An `IGang` ordered by identity (name) */
public interface ComparableGang extends IGang, Comparable<IGang> {}

您的compareTo方法不是“按身份(名称)”排序,而是按强度排序。老实说,首先它是一个相当愚蠢的接口,因为asoft创建一个公共类GangNameComparator:Comparator的类非常容易

但是,当他们建议您应该实现比较时,您需要按照界面描述的方式执行:

public int compareTo(IGang g) {
    return name.compareTo(g.getName());
}

然而...正如你在评论中注意到的(以及罗布的回答中注意到的),这与通常被称为IGang的描述相矛盾:

public interface IGang {
    /** @return negative, 0, or positive, respectively
     *          if this gang is weaker than, equal to, or stronger
     *          than the other
     */
    public int compareTo(IGang g);
}

实现ComparableGang以满足其自身文档和IGang文档的要求是不可能的。asoft的设计基本上打破了这一点。

任何代码都应该能够使用IGang实现,只知道IGang,并依赖于遵循IGang契约的实现。然而,asoft打破了这一假设,在扩展IGang的接口中要求不同的行为。

只要他们不违反现有的IGang需求,他们在比较港中添加更多的需求是合理的。

请注意,这是C#和Java之间的一个重要区别。在C#中,具有相同签名的两个不同接口中的两个函数可以组合成一个继承它们的接口,并且这两个方法保持不同和可访问性。在Java,这两个方法,因为它们是完全抽象的并且具有相同的签名,被认为是相同的方法,并且实现组合接口的类只有一个这样的方法。所以在Java比较港是无效的,因为它不能有一个满足比较港合同和IGang合同的compareTo()的实现。

 类似资料:
  • 问题内容: 我有两家公司asoft和bsoft的代码。我也不能改变。这是我的情况的简化版本,我敢肯定,它有足够的信息来查找导致问题的原因。 bsoft提供了,它代表可以与其他帮派作战的帮派。 asoft提供了,它允许进行战斗: 它需要您提供给它的s 的附加约束,大概是它可以按名称排序或避免重复。每个帮派(为简单起见,这里使用任意顺序的Set顺序)都会攻击另一个帮派,直到只剩下一个帮派(如果最后两个

  • 一、Java集合类简介: Java集合大致可以分为Set、List、Queue和Map四种体系。 其中Set代表无序、不可重复的集合;List代表有序、重复的集合;而Map则代表具有映射关系的集合。Java 5 又增加了Queue体系集合,代表一种队列集合实现。 Java集合就像一种容器,可以把多个对象(实际上是对象的引用,但习惯上都称对象)“丢进”该容器中。从Java 5 增加了泛型以后,Jav

  • 回顾一下已经了解的数据类型:int/str/bool/list/dict/tuple 还真的不少了. 不过,python是一个发展的语言,没准以后还出别的呢.看官可能有疑问了,出了这么多的数据类型,我也记不住呀,特别是里面还有不少方法. 不要担心记不住,你只要记住爱因斯坦说的就好了. 爱因斯坦在美国演讲,有人问:“你可记得声音的速度是多少?你如何记下许多东西?” 爱因斯坦轻松答道:“声音的速度是多

  • 我试图在EclipseIDE中运行Equinox的区域有向图束。当我在OSGi命令提示符下使用start命令运行相关的bundle时,我收到一个错误,表示无法解析该bundle。原因:缺少约束:导入包:org.osgi.framework.hooks。捆version=“1.0.0”。为了找出框架中的现有版本,我尝试了命令p org.osgi.framework.hooks.bundle。结果显示

  • 我使用Avro模式动态地从C#应用程序生成消息到Kafka集群,使用汇合的Kafka客户机。在编译时不知道数据类型,因此我使用命名空间中的GenericRecord类,如下所述:https://www.confluent.io/blog/decoupling-systems-with-apache-kafka-schema-registry-and-avro/。 但是,我有一个问题--如果模式有一

  • 问题内容: 打开Eclipse时出现问题。在错误日志中显示: 我正在使用Eclipse juno(版本4.2),而我的ADT是version20和Java 1.6。您知道我应该做什么吗? 问题答案: 我认为,当从Eclipse中删除功能并且未正确更新工作空间时,可能会发生这种情况。创建一个新的工作区,导入所有项目,然后删除旧的工作区。