0

ReportDatabase class

public class ReportDatabase {

    private static final Map<String, Report> reportTypes;
    private static final String[] reportTypeNameVersion = {
            "Payroll Tax Report 1",
            "Capital Gains Tax Report 1",
            "Overall Expenditure Report 1",
            "Dark Costs Report 1",
            "Petty Cash Report 1",
            "Enron Fund Report 1",
            "COVID-19 Report 1",
            "COVID-20 Report 1",
            "Brawndo Investment Report 1",
            "Philanthropy Report 1",
            "Payroll Tax Report 2",
            "Capital Gains Tax Report 2",
            "Overall Expenditure Report 2",
            "Dark Costs Report 2",
            "Petty Cash Report 2",
            "Enron Fund Report 2",
            "COVID-19 Report 2",
            "COVID-20 Report 2",
            "Brawndo Investment Report 2",
            "Philanthropy Report 2"
    };

    static {
        reportTypes = new HashMap<>();

        for (String name: reportTypeNameVersion) {
            reportTypes.put(name, new ReportImpl(name.substring(0, name.length()-1), getReportCommission(), getReportData(), getReportData(), getReportData(), getReportData(), getReportData()));
        }
    }

    /**
     * Note from Tim:
     * The version number is entirely arbitrary and not used anywhere else, so we can't use it as a composite key...
     * There are also more than 2 versions per name in the full system.
     *
     * The recreation/copying here is simulating a networked database connection
     */
    public static Collection<Report> getTestReports() {

        Collection<Report> originals = reportTypes.values();
        List<Report> result = new ArrayList<>();

        for (Report original: originals) {
            result.add(new ReportImpl(original.getReportName(),
                    original.getCommission(),
                    original.getLegalData().clone(),
                    original.getCashFlowData().clone(),
                    original.getMergesData().clone(),
                    original.getTallyingData().clone(),
                    original.getDeductionsData().clone()));
        }

        return result;
    }

    private static double[] getReportData() {

        /*
        Note from Tim:
        If your machine's memory can't handle the whole product data, you could set this lower while you work out
        a way to stop the RAM explosion? We do need to be able to get at the whole product though.
        The database doesn't though...
         */

        double[] result = new double[500000];
        Random random = new Random();

        for (int i = 0; i < result.length; i++) {
            result[i] = random.nextDouble();
        }

        return result;
    }

    private static double getReportCommission() {
        Random random = new Random();

        return 1.0 + 99.0 * random.nextDouble();
    }
}

ReportImpl class:

import au.edu.sydney.cpa.erp.ordering.Report;

public class ReportImpl implements Report {

    private String name;
    private double commissionPerEmployee;
    private double[] legalData;
    private double[] cashFlowData;
    private double[] mergesData;
    private double[] tallyingData;
    private double[] deductionsData;

    public ReportImpl(String name,
                      double commissionPerEmployee,
                      double[] legalData,
                      double[] cashFlowData,
                      double[] mergesData,
                      double[] tallyingData,
                      double[] deductionsData) {
        this.name = name;
        this.commissionPerEmployee = commissionPerEmployee;
        this.legalData = legalData;
        this.cashFlowData = cashFlowData;
        this.mergesData = mergesData;
        this.tallyingData = tallyingData;
        this.deductionsData = deductionsData;
    }

    @Override
    public String getReportName() {
        return name;
    }

    @Override
    public double getCommission() {
        return commissionPerEmployee;
    }

    @Override
    public double[] getLegalData() {
        return legalData;
    }

    @Override
    public double[] getCashFlowData() {
        return cashFlowData;
    }

    @Override
    public double[] getMergesData() {
        return mergesData;
    }

    @Override
    public double[] getTallyingData() {
        return tallyingData;
    }

    @Override
    public double[] getDeductionsData() {
        return deductionsData;
    }

    @Override
    public String toString() {

        return String.format("%s", name);
    }
}

Method in a class, calling the method, getTestReports() from ReportDatabase class

public List<Report> getAllReports() {
        if (null == token) {
            throw new SecurityException();
        }

        return new ArrayList<>(ReportDatabase.getTestReports());
    }

For the above classes, I need to reduce the RAM usage and the rule is, I need to use ReportDatabase.getTestReports(), and I cannot modify anything in ReportDatabase class (only). If I print out the RAM consumption, it is around 450MB. I tried to create a ReportFactory class in order to apply a flyweight pattern, but I think that instantiating ReportDatabase class to use getTestReports directly takes around 400MB RAM consumption since:

There are 20 reports and each report has 6 double arrays. Each array has 500000 double numbers. If we calculate the memory, the double number in java is 8 bytes. And the total memory will be

8⋅500000⋅6⋅20/(1024⋅1024)≈450MB

How should I reduce the RAM usage for the above classes? Can I please get any hints?

  • Why does the double[] result = new double[500000]; array have to have 500_000 positions? Is it a business requirement? – fpezzini May 23 '20 at 07:52
  • Why is ReportImpl using fixed arrays of doubles for its data instead of List which would have a variable size? – fpezzini May 23 '20 at 07:53
  • yes, that is the main problem that the program takes a huge RAM consumption, but I cannot modify anyhting in ReportDatabase class... But I can modify things in other classes such as ReportImpl and also can apply a design pattern like flyweight – helpmefromjava May 23 '20 at 07:54
  • Ok, so you have several double arrays in ReportImpl, however they all contain the exact same data (the result from getReportData() - see the call to the ReportImpl constructor.) You can reduce quite a bit of RAM usage by having just one double array in ReportImpl, since they all have the same data. – fpezzini May 23 '20 at 07:57
  • I think we need to maintain the variables since they are being used in external classes. What I thought is to reduce the size of the double[500000] by creating a caching method which halves the length of double[500000], but the contents of double[250000] and the contents of double[500000] are not the same, so I am not sure if its a correct way of caching arrays... – helpmefromjava May 23 '20 at 08:00
  • Since all the double arrays have exactly the same value, you can have a single double array variable, even if you keep several getters that all return the same double array. – fpezzini May 23 '20 at 08:05
  • You could off course convert the values down to floats if you were certain they'd all fit in a float. Also, if there is a way to identify "empty" values in the double array, you could instead go for a List and only store the "valid, non empty" values. – fpezzini May 23 '20 at 08:08

0 Answers0