5

In a Java program, I have a class WaterBody in which one of the attributes is a double for electric power output. How can I find the maximum electric power output value in an array of WaterBody instances?

Here's my prototype:

public static WaterBody mostPowerful(WaterBody[] waterBodies) {

}

Attribute is electricPower and I have the getter method getElectricPower

Thanks in advance.

rjpedrosa
  • 652
  • 2
  • 7
  • 10
  • 1
    http://docs.oracle.com/javase/6/docs/api/java/util/Collections.html#max(java.util.Collection, java.util.Comparator) – Steve Kuo Feb 08 '16 at 23:34
  • Homework? Declare a variable `maxval` to store the object with the maximum value, iterate over the array with foreach, call `getElectricPower()` on each object and compare its value with the value from the object in your variable. If it is greater it is your new maximum object (assign it to `maxval`). Try to code it yourself, it can be fun! Show your code if it doesn't work out immediately – Ctx Feb 08 '16 at 23:37

3 Answers3

10

If you're using Java 8, you can write this as a one-liner:

public static WaterBody mostPowerful(WaterBody[] waterBodies) {
    return Arrays.stream(waterBodies)
            .max(Comparator.comparingDouble(WaterBody::getElectricPower))
            .orElseThrow(NoSuchElementException::new);
}

The orElseThrow(NoSuchElementException::new) causes a NoSuchElementException to be thrown if the incoming array is empty (and thus there is no maximum value). If you want to return null instead, use orElse(null).

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • 1
    uh, look at that fancy stream ;) definitely the nicest solution if performance is not a big issue! – Neuron Feb 09 '16 at 00:21
  • @Neuron To test your claim of "if performance is not a big issue", I decided to write [a little program](https://gist.github.com/cky/ac45ccc5e1548b86075e) to test the various solution approaches. There are some surprising results in there.... – C. K. Young Feb 09 '16 at 01:32
  • On my computer, from fastest to slowest: nullable (2.4 ms), collections (3.1 ms), stream (4.6 ms), optional double (5.7 ms), optional (15.8 ms). The optional double and optional results actually surprised me a lot. – C. K. Young Feb 09 '16 at 01:53
  • Worst-case (elements in ascending order) performance on my computer: nullable (2.2 ms), collections (3.2 ms), stream (4.6 ms), optional double (8.5 ms), optional (17.6 ms). This appreciably slowed down the optional double and optional results! – C. K. Young Feb 09 '16 at 02:02
4

I am afraid you'll have to do a linear search:

public static WaterBody mostPowerful(WaterBody[] waterBodies) {
    double maxValue = -1;
    int indexOfMaxValue = -1;
    for(int i = 0; i < waterBodies.length; i++) {
        if(waterBodies[i].getElectricPower() > maxValue) {
            indexOfMaxValue = i;
        }
    }
    return waterBodies[indexOfMaxValue];
}

A more elegant solution can be achieved by using a Comparator. It defines an Interface, which tells if one object is "bigger" or "smaller" than another. You can define a Comparator like that:

Comparator<String> cmp = new Comparator<WaterBody>() {
    @Override
    public int compare(WaterBody o1, WaterBody o2) {
        return Double.compare(o1.getElectricPower(), o2.getElectricPower());
    }
};

If you'll need do operations like these often, it is advised to have a collection which always keeps it's content sorted, like a TreeSet It would be a lot smarter to use a collection, which keeps it's content sorted.

TreeSet<WaterBody> waterBodiesTree = new TreeSet<>(cmp);
Collections.addAll(waterBodiesTree, waterBodies);
WaterBody smallest = waterBodiesTree.first();
WaterBody biggest = waterBodiesTree.last();

If you only need to sort your array, the first example I used turns into a one-liner (using the same Comparator as defined earlier):

public static WaterBody mostPowerful(WaterBody[] waterBodies) {
    return Collections.max(waterbodies, cmp);
}
Neuron
  • 5,141
  • 5
  • 38
  • 59
  • Manual searching is not necessary. You can use `Collections.max` as suggested by Steve Kuo, or in Java 8, use `Arrays.stream` to convert the array into a stream, then call `Stream.max`. – C. K. Young Feb 08 '16 at 23:44
  • Apart from the typo on "waterbodies" on the if statement, that does the trick. Will look about collections though. Thanks! :) – rjpedrosa Feb 08 '16 at 23:45
  • @Chris Jester-Young I will add it to my post, but keep in mind that knowing how to do the very basics (like finding the maximum in a list) should come before learning how to let everything be handled by built in code. At university we learned to do these things the hard way first and I guess for a good reason. But I'll add it to the answer for curious readers – Neuron Feb 08 '16 at 23:50
  • @Neuron On the contrary, in professional code (which I always assume questions on SO are about, unless the OP says otherwise) it's important to use library code rather than reinventing the wheel. :-) – C. K. Young Feb 08 '16 at 23:51
  • @Chris Jester-Young I guess I pretty much overachieved all needs now :P – Neuron Feb 09 '16 at 00:03
  • @Neuron I've just added a Java 8 solution. Between your answer and mine, I think we have all bases covered. :-P – C. K. Young Feb 09 '16 at 00:18
2

You can define a comparator in the WaterBody class

class WaterBody {
  :
  public static final Comparator<WaterBody> POWER_COMPARATOR =
    new Comparator<WaterBody>() {
      public int compare(WaterBody dev1, WaterBody dev2) {
        return Double.compare(dev1.getPower(), dev2.getPower());
      }
  };  
}

and use it as below

 WaterBody device = Collections.max(devices, WaterBody.POWER_COMPARATOR);
MaxZoom
  • 7,619
  • 5
  • 28
  • 44
  • `Double.compare(dev1.getElectricPower(), dev2.getElectricPower())` is still a more elegant solution. Not only is it easier to read, it also doesn't create two new objects for the stack. – Neuron Feb 09 '16 at 00:11
  • Thank you for suggestion, included. – MaxZoom Feb 09 '16 at 00:13