简体   繁体   English

使用某些条件在Java中对数组进行排序

[英]Sorting an array in java with certain conditions

I have a class called Athlete which is a sublass of Human. 我有一类叫做“运动员”的类,它是人类的一个副类。 In the class Human I implement the interface comparable and use the method compareTo in order to compare the ages of different athletes. 在“人”类中,我实现了可比较的界面,并使用compareTo方法来比较不同运动员的年龄。 In the athletes class I have an extra field called year which corresponds to the year the athlete started competing. 在运动员课中,我还有一个名为Year的字段,它对应于运动员开始比赛的年份。 In my main method in my program I have an arraylist that I add both Athletes and Humans. 在我程序的主要方法中,我有一个添加运动员和人类的数组列表。 I would like to so that if an athlete is of the same age to sort according to the year the athlete started competing. 我希望,如果运动员的年龄与开始比赛的年份相同。 I use instanceof to check in my class human if the instance is the object is of type Athlete but after that I don't know how to get it to work. 我使用instanceof来检查班级实例中的实例是否是运动员类型的对象,但此后我不知道如何使其工作。

    public int compareTo(Human other)
    {

        if(this instanceof Athlete && other instanceof Athlete && this.age == other.age){
            return ;
        }
        return this.age - other.age;
    }
}

Use polymorphism , instead of the operator instanceof . 使用多态性 ,而不是运算符instanceof

That is: overload the compareTo method in the Athlete class. 即:重载Athlete类中的compareTo方法。

public int compareTo(Athlete other) {
//This method will be invoked if and only if you compare an athlete with another athlete
}

Also, consider that the equals method result should be consistent with the compareTo method results. 另外,请考虑equals方法的结果应与compareTo方法的结果一致。

One possible solution is to add a compareTo method in the Athlete class also, something in the lines of (needs rewriting as I haven't been working on Java since a long time ago): 一种可能的解决方案是在Athlete类中也添加一个compareTo方法,其内容如下(需要重写,因为很久以前我一直没有从事Java的工作):

public int compareTo(Athlete other){
    int result = super.compareTo((Human)other);

    if(result == 0){
        return this.year - other.year;
    }

    return result;
}

As a code review, I'd say that the complete code should be something like the following: 作为代码审查,我想说完整的代码应类似于以下内容:

Human.java 人的java

public int compareTo(Human other){
    return age - other.age;
}

Athlete.java Athlete.java

@Override
public int compareTo(Human other){
    if(other instanceof Athlete){
        return compareTo((Athlete)other);
    }

    return super.compareTo(other);
}

public int compareTo(Athlete other){
    int result = super.compareTo((Human)other);

    if(result == 0){
        return this.year - other.year;
    }

    return result;
}

Using your example, you could just compare the year as well: 使用您的示例,您也可以只比较年份:

public int compareTo(Human other)
{

    if(this instanceof Athlete && other instanceof Athlete && this.age == other.age){
        String tYear = ((Athlete)this).getYear();
        String oYear = ((Athlete)other).getYear();
        int tYearInt = 0;
        int oYearInt = 0;
        try {
           tYearInt = Integer.parseInt(tYear);
           oYearInt = Integer.parseInt(oYear);
        } catch (Exception e){
           e.printStackTrace();
        }
        return  tYearInt - oYearInt;
    }
    return this.age - other.age;
}

However, having said that, please consider @Andres answer, anytime you use instanceof, you should question whether your design is wrong. 但是,话虽如此,请随时考虑@Andres答案,无论何时使用instanceof,都应质疑您的设计是否错误。

Like Andres said, use polymorphism. 就像安德列斯(Andres)所说的,使用多态。 Here is how to do that: 这样做的方法如下:

First of all, this instanceof Athlete in the Human class is not good style, because from the perspective of the Human class, Athlete is a subclass and referencing subclasses can lead to problems in certain cases. 首先,在“ Human类中this instanceof Athlete不是好的样式,因为从“ Human类的角度来看,“ Athlete是一个子类,而引用子类在某些情况下会导致问题。 Instead, put another compareTo() method into the Athlete class. 而是,将另一个compareTo()方法放入Athlete类。 If Athlete.compareTo() gets called, you already know that this is of type Athlete and if you want to compare the year field, you only have to check if other instanceof Athlete , which is ok, because now we are in the perspective of the Athlete class and from here, Athlete is not a subclass. 如果Athlete.compareTo()被调用,你已经知道, this是类型的Athlete ,如果你要比较的year场,你只需要检查other instanceof Athlete ,这是确定的,因为现在我们是在透视Athlete类,从这里开始, Athlete不是子类。

That said, in the Athlete class, use this: 就是说,在Athlete课程中,使用以下命令:

public int compareTo(Human other) {
    int humanComp = super.compareTo(other);
    if (humanComp != 0){
        return humanComp;
    }

    if (other instanceof Athlete) {
        return ((Athlete)other).year - this.year;
    }

    return 0;
}

This solution first uses Human.compareTo() (called with super.compareTo(other) ) to check if the Human class already knows how to order our instances this and other . 该解决方案首先使用Human.compareTo() (与super.compareTo(other)一起super.compareTo(other) )来检查Human类是否已经知道如何对thisother实例进行排序。 If not, ie if this call returns 0, we have to go on with comparing more details, in our case the year field. 如果不是,即该调用返回0,则必须继续比较更多详细信息,在本例中为year字段。
Because we used Human.compareTo() , we have to make sure it exists in the Human class and that it works properly: 因为我们使用了Human.compareTo() ,所以我们必须确保它存在于Human类中并且可以正常工作:

public int compareTo(Human other) {
    return this.age - other.age;
}

This one simply compares by age , because that's the only field in the Human class we know we can use for comparison. 这只是按age进行比较,因为这是Human类中我们知道可用于比较的唯一字段。

The documentation for compareTo says: compareTo文档说:

Finally, the implementor must ensure that x.compareTo(y)==0 implies that sgn(x.compareTo(z)) == sgn(y.compareTo(z)) , for all z . 最后,实现者必须确保对于所有zx.compareTo(y)==0意味着sgn(x.compareTo(z)) == sgn(y.compareTo(z))

Your proposed method does not meet this requirement. 您提出的方法不符合此要求。 For example, suppose 例如,假设

x = Athlete, age = 35, start date = 2000
y = Human,   age = 35
z = Athlete, age = 35, start date = 2001

In this example 在这个例子中

x.compareTo(y) == 0   // y is not an athlete
x.compareTo(z) < 0    // Both athletes, different start dates.
y.compareTo(z) == 0   // y is not an athlete

If you do not obey the contract for Comparable , the behaviour of Arrays.sort or Collections.sort is unspecified. 如果您不遵守Comparable的合同,则未指定Arrays.sortCollections.sort的行为。 Ideally you'd get an exception, but the trouble is these sorting methods use different algorithms depending on the size of the array or list, and you are more likely to get an exception thrown for an incorrect compareTo method if the input array or list has a large number of elements . 理想情况下,您会得到一个异常,但是麻烦的是,这些排序方法根据数组或列表的大小使用不同的算法,并且如果输入数组或列表包含以下内容 ,则您更有可能因不正确的compareTo方法而引发异常大量的要素 This means that you should test your compareTo method very carefully using long randomly generated arrays, otherwise you may have subtle, hard-to-detect bugs in your code. 这意味着您应该使用随机生成的长数组非常仔细地测试您的compareTo方法,否则,您的代码中可能会包含难以发现的细微错误。

A correct compareTo method looks something like this: 正确的compareTo方法如下所示:

public int compareTo(Human other) {
    int result = Integer.compare(age, other.age);
    if (result != 0)
        return result;
    if (!(this instanceof Athlete))
        return other instanceof Athlete ? -1 : 0;
    return other instanceof Athlete 
               ? Long.compare(((Athlete) this).startDate(), ((Athlete) other).startDate()) 
               : 1;
}

This method sorts first by age. 该方法首先按年龄排序。 If two humans have the same age they are sorted first by type (with athletes coming first). 如果两个人的年龄相同,则按类型将其排在第一位(运动员排在第一位)。 Athletes with the same age are sorted by start date. 年龄相同的运动员按开始日期排序。 Non-athletes with the same age do not appear in any particular order. 具有相同年龄的非运动员不会以任何特定顺序出现。

Finally, note that it is generally better to use polymorphism rather than instanceof . 最后,请注意,通常最好使用多态性而不是instanceof The problem here is that Human implements Comparable<Human> . 这里的问题是Human implements Comparable<Human> Therefore the compareTo method must accept a Human , not an Athlete . 因此compareTo方法必须接受一个Human而不是一个Athlete Even if you override compareTo in Athlete , the argument must be a Human , and you'd have to use instanceof to check the type of the argument anyway (as in @GentianKasa's answer) or write a completely separate method compareToAthlete(Athlete athlete) and do the following in Athlete 即使您在Athlete重写compareTo ,该参数也必须是Human ,并且无论如何您都必须使用instanceof来检查参数的类型(如@GentianKasa的回答), 或者编写一个完全独立的方法compareToAthlete(Athlete athlete)渗漏compareToAthlete(Athlete athlete)和在Athlete执行以下操作

@Override 
public int compareTo(Human human) {
    return -human.compareToAthlete(this);  // Note the minus sign!
}

compareToAthlete would need two versions as well. compareToAthlete也将需要两个版本。 While this works, it means that the logic of the compareTo method is spread over four methods, making it harder to reason about its correctness. 在此有效的同时,这意味着compareTo方法的逻辑分布在四种方法中,这使得更难于推理其正确性。 In this case, I'd hold my nose and use instanceof . 在这种情况下,我会nose之以鼻并使用instanceof

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM