2
\$\begingroup\$

Hey there guys, I have just completed an exercise for uni and I would really like some feedback as I'm pretty new to using java. Areas that I could especially use some input in this instance: readability of the code, the names of the variables, the javaDoc comments, correct use of error statements AND MOST IMPORTANTLY the not3TimesHigher method, which took me ages to do, I'm still not sure whether this is the best way to do it.

A warning before anybody writes anything here: Please do not suggest any overly complex updates to my code, I'm a beginner and I'm required to work within those limitations.

Anyway here is the problem sheet for reference:

A company stores the salaries of its employees in an ArrayList allSalaries, an ArrayList of arrays of type double. Each entry in allSalaries is an array of the 12 salaries of an employee for the 12 months of the year.

Write a class Salaries with the constructor • public Salaries() having no parameters to create an initially empty ArrayList. And write the following methods:

• public void add(double[] employeeSalaries) to add the salaries of one employee to the field variable allSalaries.

• The method public static double average(double[] employeeSalaries) computes the average salary for an employee. Note that any 0 entry should be disregarded, since a 0 means that the employee was not employed in that particular month. For instance, the average of {1000,1000,2000,2000,0,0,0,0,0,0,0,0} should be 1500.0 as the sum of the four non-zero values divided by 4. If all values in the annualSalaries array are zero, the method should throw an IllegalArgumentException.

• The method public ArrayList averageSalaries() generates an ArrayList storing the average salaries of all employees that have at least one non-zero monthly salary. Make use of the method average. Hint: You need to catch possible exceptions thrown by the method average.

• The method public boolean not3TimesHigher() checks whether for each employee with at least one non-zero monthly salary their average salary is not higher than three times the overall average salary of the other employees. That is, you need here the average of the averages.

import java.util.ArrayList;

public class Salaries {
private ArrayList<double[]> allSalaries;

public Salaries() {
    allSalaries = new ArrayList<double[]>();
}

public void add(double[] employeeSalaries) {
    allSalaries.add(employeeSalaries);
}

/** 
 * @param takes an array of doubles; each index of the array represents
 * the earnings of an employee for that particular month.
 *
 * @return average salary of an employee. 
 */
public static double average(double[] employeeSalaries) {
    double totalSalary = 0;
    int totalMonths = 0;

    for (int i=0; i<employeeSalaries.length; i++) {
        if (employeeSalaries[i] > 0) {
            totalSalary += employeeSalaries[i];
            totalMonths++;
        } 
        if (totalSalary == 0) {
            throw new IllegalArgumentException("This chump didn't earn any money!");
        }
    } 
    return totalSalary / totalMonths;
}

/** 
 * Method traverses allSalaries calculating the average salary for each employee
 * and appending it to a newly a instantiated ArrayList. 
 * 
 * @return ArrayList containing average salaries for all employees with at least one
 * monthly salary above 0.
 */
public ArrayList<Double> averageSalaries() {
    ArrayList<Double> averageSalaries = new ArrayList<Double>();

    try {
    for (int i=0; i<allSalaries.size(); i++) {
            double avgEmployeeSalary = average(allSalaries.get(i));
            averageSalaries.add(avgEmployeeSalary);
            }
    } catch (IllegalArgumentException e) {
        System.out.println("Warning, attempted to add employee with zero earnings.");
    }
        return averageSalaries;
        }

/** 
 * Method creates a new instance of averageSalaries which it then traverses,
 * comparing each index (average employee salary) with the total average value
 * of all other indexes * 3. 
 * 
 * @return false if any employee average salary is greater than the average of 
 * all other employee salaries * 3, true otherwise. 
 */
public boolean not3TimesHigher() {
    ArrayList<Double> avgS = averageSalaries();

    for (int i=0; i<avgS.size(); i++) {

        double employee = avgS.get(i);
        avgS.remove(i);

        double[] allOtherEmployees = new double[avgS.size()];

        for (int j=0; j<allOtherEmployees.length; j++) {
            allOtherEmployees[j] = avgS.get(j);
        }  
        if (employee > (average(allOtherEmployees) * 3)) {
            return false;
        }
    } return true;
}

public static void main(String[] args) {
    //double[] bowie = {2456, 1330, 0, 5470};

    double[] paul = {5, 8, 4, 6};
    double[] ringo = {5, 7, 4, 6};
    double[] john = {0, 0, 0, 0};
    double[] george = {3, 7, 9, 8};

    Salaries a = new Salaries();
    //a.add(bowie);
    a.add(paul);
    //a.add(john);
    a.add(ringo);
    a.add(george);

    //System.out.println(Salaries.average(john));

    System.out.println(a.averageSalaries());

    System.out.println(a.not3TimesHigher());
  }
}
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

Thanks for sharing your code.

I think you did a good job on this exercise. Therefore I have only a few points to mention:

Correctness

I think your method averageSalaries() is not correct, since it returns after the first exception is caught and no other input rows are processed. As I understand the exercise the "unpayed" entries should be ignored and all other entries should be processed. This means, the code should be like this:

public ArrayList<Double> averageSalaries() {
    ArrayList<Double> averageSalaries = new ArrayList<Double>();

    for (int i=0; i<allSalaries.size(); i++) {
        try {
            double avgEmployeeSalary = average(allSalaries.get(i));
            averageSalaries.add(avgEmployeeSalary);
        } catch (IllegalArgumentException e) {
            System.out.println("Warning, attempted to add employee with zero earnings.");
        }
    }
    return averageSalaries;
}

Readability

Code Format

Use an Integrated Development Environment (like eclipse, intelliJ NetBeans or alike (if you don't do already) and use it's auto formatter feature.

Naming

Make your names as specific as possible.

In your method average() you have a variable totalMonths. By this name I'd expect the total number of month processed, but it contains the count of moth with non zero payment. In my view monthsWithPayment would be a better name.

avoid single letter names.

In your main() method the variable to refer to the Salaries object is named a. Finding good names is the hardest part in programming. So you should not give away a chance to practice good naming even it it is only a test methods for your exercise solution.

for loop

At many if not at all places the "for each" form of the for loop would improve readability:

for (double[] emploeeMonthlyPayments : allSalaries) {
    try {
        double avgEmployeeSalary = average(emploeeMonthlyPayments);
        averageSalaries.add(avgEmployeeSalary);
         // ...

Off Topic

I think this exercise is not a good one:

naming

The exercise requests you to write a method named not3TimesHigher returning a boolean value. In my view this name is bad in thee ways:

  • according to the (Java Code Conventions)[ https://www.oracle.com/technetwork/java/codeconventions-135099.html] names of variables of type boolean and methods returning a boolean should start with is, has, can or alike.
  • names of variables of type boolean and methods returning a boolean should express positive conditions.

  • when using digits in names they should be the last part in the name

Therefore name of this method had better been isLessThanAverageTimes3()

Referring to the same (Java Code Conventions)[ https://www.oracle.com/technetwork/java/codeconventions-135099.html] names of methods should start with a verb. But the method names requested in this exercise (average(), averageSalaries()) are nouns. They should better be calculateAverage(), calculateAverageSalaries().

use of the static key word

The use of the static key word has more drawbacks than advantages. It should be used with care and for a good reason. I cannot see such a "good reason" for the use of the static key word in the method average().

\$\endgroup\$
9
  • \$\begingroup\$ Small note, "average" can be used as a verb as well as a noun. \$\endgroup\$
    – cbojar
    Oct 27, 2019 at 1:09
  • \$\begingroup\$ @cbojar in that case the readability should be increased by making the identifier name unambiguous. Also: for me as a non native english speaker to average would mean: make all the same average value and thus imply a state change in the Salleries object. \$\endgroup\$ Oct 27, 2019 at 11:15
  • 1
    \$\begingroup\$ Thanks @Timothy Truckle, I really appreciate it. You make some really good points about the not3TimesHigher exercise, I think the worksheet itself was rushed, the version I posted was actually updated from a previous worksheet with more errors. \$\endgroup\$
    – user207830
    Oct 27, 2019 at 14:44
  • \$\begingroup\$ @TimothyTruckle I think it really depends on the team. If they frequently refer to averaging the salaries, using average as a verb method name is fine. If instead they talk about calculating an average, then calculateAverage is more appropriate, even if more verbose. \$\endgroup\$
    – cbojar
    Oct 27, 2019 at 18:45
  • \$\begingroup\$ @cbojar what about new team members who are not (yet) familiar with your use of the wording? \$\endgroup\$ Oct 27, 2019 at 20:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy