0

I'm trying to answer this question about parsing data and sorting into bins. I think I almost have it solved but can't seem to get it to work properly.

Here's my code:

package soBins;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Bins {

    public static void main(String[] args) {

        String rangeIn = "100 101 3.45\n101 102 4.23\n103 104 2.40\n199 200 6.89";

        String dataIn = "xx 108.45\nxx 122.00\nyy 124.78\nxx 156.93\nzz 101.5\nxx 103.5\nzz 101.25";

        Scanner rangeScanner = new Scanner(rangeIn);
        Scanner dataScanner = new Scanner(dataIn);  

        ArrayList<Bin> bins = new ArrayList<Bin>();
        while (rangeScanner.hasNextLine()) {
            String line = rangeScanner.nextLine();
            String[] tokens = line.split(" ");
            int min = Integer.parseInt(tokens[0]);
            int max = Integer.parseInt(tokens[1]);
//          System.out.println("Creating new bin, min "+ min + ", max "+ max);
            bins.add(new Bin(min,max));
        }

        Map<String,ArrayList<Bin>> namedBins = new HashMap<String,ArrayList<Bin>>();
        while (dataScanner.hasNextLine()) {
            String line = dataScanner.nextLine();
            String[] tokens = line.split(" ");
            String name = tokens[0]; // name is first token on line
            float data = Float.parseFloat(tokens[1]); // data is second token on line

            if (!namedBins.containsKey(name)) {
                // Shouldn't this create a new copy of the ArrayList of bins??
                namedBins.put(name, new ArrayList<Bin>(bins));
            }
            for (Bin b : namedBins.get(name)) {
                if (b.isInRange(data)) {
                    System.out.println("adding "+ data + " to bin in "+ name);
                    b.addData(data);

                }
            }
        }   

        System.out.println("All bins and data contents:");
        for (String dataName : namedBins.keySet()) { // print all values and bin ranges
            for (Bin range : namedBins.get(dataName)) {
                System.out.println(dataName + ", min " + range.getMin() + ", max " + range.getMax()
                        + ", data is " + range.getData());              
            }
        }
    }
}

And my Bin class:

package soBins;

import java.util.ArrayList;

public class Bin {

    int min = 0;
    int max = 0;
    ArrayList<Float> values = new ArrayList<Float>();

    Bin(int min,int max) {
        this.min = min;
        this.max = max;
    }

    public boolean isInRange(float data) {
        return (min < data) && (data < max);
    }

    public void addData(float data) {
        values.add(data);
    }

    public int getMin() {
        return min;
    }
    public void setMin(int min) {
        this.min = min;
    }
    public int getMax() {
        return max;
    }
    public void setMax(int max) {
        this.max = max;
    }

    public ArrayList<Float> getData() {
        return values;
    }   

}

The output seems to be adding the data to every dataset, not just the one that it's meant to.

The output is:

adding 101.5 to bin in zz
adding 103.5 to bin in xx
adding 101.25 to bin in zz
All bins and data contents:
zz, min 100, max 101, data is []
zz, min 101, max 102, data is [101.5, 101.25]
zz, min 103, max 104, data is [103.5]
zz, min 199, max 200, data is []
yy, min 100, max 101, data is []
yy, min 101, max 102, data is [101.5, 101.25]
yy, min 103, max 104, data is [103.5]
yy, min 199, max 200, data is []
xx, min 100, max 101, data is []
xx, min 101, max 102, data is [101.5, 101.25]
xx, min 103, max 104, data is [103.5]
xx, min 199, max 200, data is []

Why is it adding to all groups, and not just to the intended one?

Eran
  • 387,369
  • 54
  • 702
  • 768
localhost
  • 1,253
  • 4
  • 18
  • 29

1 Answers1

2

You only create the Bin instances once and put them in the bins list prior to initializing the Map :

ArrayList<Bin> bins = new ArrayList<Bin>();
while (rangeScanner.hasNextLine()) {
    ....
    bins.add(new Bin(min,max));
}

and you use that same bins instance to initialize the value of all the keys of the Map :

namedBins.put(name, new ArrayList<Bin>(bins));

While this statement creates a new ArrayList for each Map entry, it puts in it the same Bin instances from the original bins List. This means that the Bin instances in each Bin list in the Map are the same, so when you modify those Bin instances, you change all the Map entries.

In order to have unique Bin instances in each Map entry, you must have a copy constructor or clone method in the Bin class. Then you would do something like this :

if (!namedBins.containsKey(name)) {
    List<Bin> newBins = new ArrayList<Bin>();
    newdBins.put(name, new newBins);
    for (Bin bin : bins)
        newBins.add (new Bin(bin)); // using a copy constructor            
}
Eran
  • 387,369
  • 54
  • 702
  • 768