I'm a little lost (still working with Ron Jeffries's book). Here's a simple class:
public class Model{
private String[] lines;
public void myMethod(){
String[] newLines = new String[lines.length + 2];
for (i = 0, i <= lines.length, i++) {
newLines[i] = lines[i];
}
}
}
I have another class that initializes Model, and an empty array, by setting myModel = new String[0]. When I invoke myModel.myMethod(), I get a subscript out of range error. Looking at the debugger, what I see is that myModel.lines has zero dimensions and zero length. Shouldn't it have a dimension and length of 1? Granted the value of lines[0] is null, but the array itself shouldn't be, should it?
Any thoughts truly appreciated.
Randy
I think your example is probably not the same as your actual code based on your description. I think the problem is that arrays are zero-based and thus an array initialized as:
string[] lines = new string[0];
has no elements.
You need to change your loop so that you check that the index is strictly less than the length of the array. As others have indicated you also need to make sure that the array itself is not null before trying to reference it.
My take on your code:
public class Model{
private String[] lines = new string[0];
public Model( string[] lines ) {
this.lines = lines;
}
public void myMethod(){
int len = 2;
if (lines != null) {
len = len + lines.length;
}
String[] newLines = new String[len];
for (i = 0, i < lines.length, i++) {
newLines[i] = lines[i];
}
}
}
lines will be null, so lines.length will throw an exception.
I believe your other class initializing "Model" won't help since Lines itself is private. In fact, whatever you are doing to Model is probably illegal in at least 30 states.
lines is initalized to null, check for null or initialize it in this way :
private String[] lines = new String[0];
You cannot initialize an instance of Model by setting it equal to a String array. I'm actually surprised that the compiler will let you even try.
If you really want Model to be initializable with an external array, you should make a Constructor for the Model class that will take as an argument the array. Then in the body of your constructor, set the value of lines to that value.
Example:
public class Model {
private String []lines;
public Model(String [] inLines)
{
lines = inLines;
}
}
Usage:
myStringArray = new String[0];
myModel = new Model(myStringArray);
Take a look at my answer here - I think this will get you the background you are looking for on the differences between array initialization in Java and C/C++.
Related
I'm trying to remove null values from an array, and returning them to do some other stuff with the new values. However, I'm confused about how to get the updated array.
This is the null removal code.
String[] removeNull(String[] nullArray) {
int nullCounter = 0;
//checking if any is null
for(int i = 0; i < nullArray.length; i++) {
if(nullArray[i]==null) {
nullCounter++;
}
}
String[] noNulls = new String[nullArray.length-nullCounter];
if(nullCounter>0) {
//make a non null array
for(int i = 0, j = 0; i <noNulls.length; i++) {
if(nullArray[i]!=null) {
noNulls[j] = nullArray[i];
j++;
}
}
}
return noNulls;
}
I'm pretty sure that is already correct (Please correct me if I'm wrong). Then, I called it inside a constructor.
public theBoundary(String[] bounds){
removeNull(bounds);
}
After I called removeNull(bounds), will the value of the new array be stored in the array bounds? Or will it be stored in the array noNull? I can't seem to find where the new values are stored.
Thank you, and please tell me if there are mistakes. I've been going around this for half an hour now.
Note: If possible, please don't give me answers that include importing something else. Vanilla Java would be preferred.
removeNull() returns the array noNulls, created inside the method. Currently, in theBoundary(), you simply call removeNull(bounds), but do not assign it to a variable. The newly created null-free array is created, not assigned, and immediately garbage collected.
If you wish to do something with your non-null-containing array (which I assume you do), do this:
public theBoundary(String[] bounds) {
String[] withoutNulls = removeNull(bounds);
doSomething(withoutNulls); // whatever you need here
}
Note, unless you really have to use an array, consider using a List or even a Stream.
List example:
List<String> list = ... // from somewhere else
list.removeIf(s -> s == null);
doSomething(list);
Stream example:
Stream<String> stream = ... //from somewhere else
stream.filter(s -> s != null);
doSomething(stream);
EDIT
Even if you do really need arrays, the following will also work:
String[] noNulls = (String[]) Arrays.stream(inputArray).filter(Objects::nonNull).toArray();
I don't think there is any need to iterate the array twice!
You can instead use a stream on array and filter the indexes without that are NOT NULL.
Also, you can do this without needing to create the removeNull method, and do this directly in your theBoundary method.
Here is how your code will look like:
String[] arrayWithoutNull = Arrays.stream(bounds).filter(Objects::nonNull).toArray(String[]::new)
I hope this solves your problem.
Do you mean this?
public theBoundary(String[] bounds){
String[] cleanedBounds = removeNull(bounds);
}
You are not doing it inplace so you need to assign it back to a new array
I want to create a 2D Array that creates a mini seating chart of an airplane. So far, I've gotten it to successfully print out something that looks like this:
1A(0) || 1B(0) || 1C(0)
2A(0) || 2B(0) || 2C(0)
3A(0) || 3B(0) || 3C(0)
4A(0) || 4B(0) || 4C(0)
The zeroes represent an empty seat, and the number one is used to represent an occupied seat.
I first created the program with arrays that were class variables for a First Class, but I wanted to make this program usable for an Economy Class section. The only difference between the two sections is the size of the array so I edited my code to look like this:
public class Seating
{
private int FIRSTCLASS= 12;
private int ECONOMYCLASS= 240;
private int occupied, column;
private String[][] seatchart;
private int[][] seatlist;
private String[][] namelist;
private String name;
public String customer;
public Seating(String seatclass)
{
seatclass.toUpperCase();
if (seatclass.equals("FIRSTCLASS"))
{
seatchart= new String[FIRSTCLASS/3][3];
seatlist= new int[FIRSTCLASS/3][3];
namelist= new String[FIRSTCLASS/3][3];
}
else
if (seatclass.equals("ECONOMY"))
{
seatchart= new String[ECONOMYCLASS/3][3];
seatlist= new int[ECONOMYCLASS/3][3];
namelist= new String[ECONOMYCLASS/3][3];
}
}
public void Creation()
{
for (int i=0; i< seatlist.length; i++)
{
for (int j=0; j<seatlist[i].length; j++)
{
seatlist[i][j]= 0 ;
}
}
I get an null pointer exception error around for (int i=0; i< seatlist.length; i++)
How can I fix this error?
Thanks in advance!
The problem is with this line:
seatclass.toUpperCase();
Replace it with:
seatclass = seatclass.toUpperCase();
I think you are creating the class with a string like "firstclass" rather than "FIRSTCLASS" right? Those aren't the same strings and just invoking the toUpperCase method on the string without assigning the result to a variable to then be tested means nothing happens.
Then since none of your if conditions are met, the arrays are not initialized and a null pointer exception is thrown when Completion() is called.
I'm not sure if you are new to java programming, but I wanted to add a few recommendations to your class:
public class Seating {
private static int FIRSTCLASS= 12; // Make these constants static since they pertain to all
private static int ECONOMYCLASS= 240; // instances of your class. That way there is exactly on
// copy of the variables, which is more memory efficient.
private int occupied;
private column; // Okay but Java convention is to declare each member variable on its own line
// increases code readability.
private String[][] seatchart;
private int[][] seatlist;
private String[][] namelist;
private String locSeatClass;
private String name;
public String customer; // Okay but better to leave this private and then provide getter and
// setter methods to provide access to this string. Much easier to track
// down who is changing its value in your code.
public Seating(String seatclass) { // Java convention is to place the opening bracket here not
// on the next line.
// Make sure that seatClass is not null or empty. NOTE: This is a neat trick for
// simultaneously checking for both null and empty strings in one shot. Otherwise, you have
// you have to check for null and then examine the string's length which is more code.
if ("".equalsIgnoreCase(seatClass) {
throw new IllegalArgumentException("Seat class undefined.");
}
// Store the seat class in a member variable for use. Could also be a local variable.
// My original solution is problematic because it changes the original value of the seat
// class that was passed into the constructor (you might want that information).
locSeatClass = seatclass.toUpperCase();
if (locSeatClass.equals("FIRSTCLASS"))
{
seatchart= new String[FIRSTCLASS/3][3];
seatlist= new int[FIRSTCLASS/3][3];
namelist= new String[FIRSTCLASS/3][3];
}
else if (locSeatclass.equals("ECONOMY")) {
seatchart= new String[ECONOMYCLASS/3][3];
seatlist= new int[ECONOMYCLASS/3][3];
namelist= new String[ECONOMYCLASS/3][3];
}
else {
// Throw an exception if someone passes in an unknown seat class string.
throw new IllegalArgumentException("Unknown seat class detected.")
}
}
public void creation() { // NOTE: Java convention is to begin method names with a lower
// case letter.
// This method is unnecessary. Arrays of integers are initialized with an initial value
// of zero by default. However, if you want to make your class reusable, you could change
// change the name of the this method to clear, which would allow you to clear the arrays of
// an existing object.
for (int i=0; i< seatlist.length; i++)
{
for (int j=0; j<seatlist[i].length; j++)
{
seatlist[i][j]= 0 ;
}
}
}
The only way that line of code can generate a NPE is if seatlist is null. Unless you assign null to seatlist somewhere else in your class, the only way it can be null is if the argument that you pass to the Seating constructor does not match either "FIRSTCLASS" or "ECONOMY". Check your call to the constructor. Also, you might want to just use seatclass.equalsIgnoreCase().
You should modify your constructor to at least warn about that eventuality, since it is vital to the proper operation of the class that any instances of Seating have valid seatlist and namelist arrays.
I have a program in java that I wrote to return a table of values. Later on as the functions of this program grew I found that I would like to access a variable within the method that isn't returned but I am not sure the best way to go about it. I know that you cannot return more than one value but how would I go about accessing this variable without a major overhaul?
here is a simplified version of my code:
public class Reader {
public String[][] fluidigmReader(String cllmp) throws IOException {
//read in a file
while ((inpt = br.readLine()) != null) {
if (!inpt.equals("Calls")) {
continue;
}
break;
}
br.readLine();
inpt = br.readLine();
//set up parse parse parameters and parse
prse = inpt.split(dlmcma, -1);
while ((inpt = br.readLine()) != null) {
buffed.add(inpt);
}
int lncnt = 0;
String tbl[][] = new String[buffed.size()][rssnps.size()];
for (int s = 0; s < buffed.size(); s++) {
prse = buffed.get(s).split(dlmcma);
//turns out I want this smpls ArrayList elsewhere
smpls.add(prse[1]);
//making the table to search through
for (int m = 0; m < prse.length; m++) {
tbl[lncnt][m] = prse[m];
}
lncnt++;
}
//but I return just the tbl here
return tbl;
}
Can anyone recommend a way to use smpls in another class without returning it? Is this perhaps when you use a get/set sort of setup?
Sorry if this seems like an obvious question, I am still new to the world of modular programming
Right now you have this tbl variable. Wrap it in a class and add the list to the class.
class TableWrapper {
// default accessing for illustrative purposes -
// setters and getters are a good idea
String[][] table;
List<String> samples;
TableWrapper(String[][] table, List<String> samples) {
this.table = table;
this.samples = samples;
}
}
Then refactor your method to return the wrapper object.
public TableWrapper fluidigmReader(String cllmp) throws IOException {
// your code here
String tbl[][] = new String[buffed.size()][rssnps.size()];
TableWrapper tw = new TableWrapper(tbl,smpls);
// more of your code
return tw;
}
Then later in your code where you were going
String[][] tbl = fluidigmReader(cllmp);
You instead go
TableWrapper tw = fluidigmReader(cllmp);
String[][] tbl = tw.table;
List<String> smpls = tw.samples;
If you had used a dedicated class for the return value (such as the TableWrapper mentioned in another answer), then you could add additional fields there.
That is the good thing about classes - they can be extended. But you cannot extend String[][] in Java.
You can set a field, instead of a local variable, which you can retrieve later with a getter. You want to avoid it unless it is needed, but in this case it is.
You can use class(Inside Reader class) variable for this. But make sure that it's read/write is synchronized
I am getting a null exception error from this segment of code and I am not sure what causing it. The array itemcatalog has being populate for i =0 to 8. I am new to java so any assistance will be greatly appreciated. The error message points to the line of the while statement. Thanks
public class ItemCatalog {
private static ItemCatalog instance = new ItemCatalog();
private Item itemCatalog[] = new Item[9];
private ItemCatalog(){
};
public static synchronized ItemCatalog getInstance() {
return instance;
}
public void populateCatalog()
{
itemCatalog[0] = new Item("bb","Baked Beans",new BigDecimal("0.35"));
itemCatalog[1] = new Item("cf","Cornflakes",new BigDecimal("1.00"));
itemCatalog[2] = new Item("s0","Sugar",new BigDecimal("0.50"));
itemCatalog[3] = new Item("tb","Tea Bags",new BigDecimal("1.15"));
itemCatalog[4] = new Item("ic","Instant Coffee",new BigDecimal("2.50"));
itemCatalog[5] = new Item("b0","Bread",new BigDecimal("0.50"));
itemCatalog[6] = new Item("s0","Sausages",new BigDecimal("1.30"));
itemCatalog[7] = new Item("e0","Eggs",new BigDecimal("0.75"));
itemCatalog[8] = new Item("m0","Milk",new BigDecimal("0.65"));
}
public BigDecimal getPrice(String itemCode)
{
int i = 0;
while (!itemCode.equals(itemCatalog[i].getItemCode()))
{
i++;
}
BigDecimal itemPrice = itemCatalog[i].getItemprice();
return itemPrice;
}
}
I solved the issue. I was populating the catalog in the main class which was giving the null exception error. I instantiate it in the jframe instead and it works. The follow code solved the issue, but is this the best place to populate the catalog?
private void saleButtonActionPerformed(java.awt.event.ActionEvent evt) {
String itemCode = this.itemCodeinput.getText();
int itemQuantity =Integer.parseInt(this.itemQuantityinput.getText());
ItemCatalog catalog = ItemCatalog.getInstance();
catalog.populateCatalog();
BigDecimal price = catalog.getPrice(itemCode);
itemCostoutput.setText(price.toString());
}
If your itemCode doesn't match any entries in your itemCatalog, then eventually
while (!itemCode.equals(itemCatalog[i].getItemCode()))
{
i++;
}
will increment i to 11, in which case itemCatalog[11] is either empty or out of bounds.
If addition, you should use a for loop to iterate through the itemCatalog:
for (int i = 0; i < itemCatalog.length; i++) {
if (itemCode.equals(itemCatalog[i].getItemCode()) {
return (BigDecimal) itemCatalog[i].getItemprice();
}
}
return null // you can change this from null to a flag
// value for not finding the item.
From the comments, it's clear the design isn't sound.
Here's a possible solution :
public BigDecimal getPrice(String itemCode) {
for (int i=0; i<itemCatalog.length; i++) { // not going outside the array
if (itemCatalog[i].getItemCode().equals(itemCode)) { // inversing the test to avoid npe if itemCode is null
return itemCatalog[i].getItemprice();
}
}
return null; // default value
}
This supposes your array is correctly filled with itemCatalogs having an itemCode.
How do you end your loop?
Seems that the loop will keep going until i is 10. Then your will have exceeded the limit.
Unless this is a uni assignment where you have to use arrays, I'd also suggest using a map, rather than an array. This way your lookup will be the same time, whether your collection has 100,000 entries or 10.
You will also reduce risk of NPE or ArrayOutOfBounds exception
See http://docs.oracle.com/javase/1.4.2/docs/api/java/util/HashMap.html
When adding the object use the item code as the key. Then lookup by the key.
The cost of using a map is increased memory usage.
I would like to create an initialisation method for a Java class that accepts 3 parameters:
Employee[] method( String[] employeeNames, Integer[] employeeAges, float[] employeeSalaries )
{
Employee myEmployees[] = new Employee[SIZE]; // I don't know what size is
for ( int count = 0; count < SIZE; count++)
{
myEmployees[count] = new Employee( employeeNames[count], employeeAges[count], employeeSalaries[count] );
}
return myEmployees;
}
You may notice that this code is wrong. The SIZE variable is not defined. My problem is that I would like to pass in 3 arrays, but I would like to know if I can ensure that the three arrays are ALL of the same array size. This way the for loop will not fail, as the constructor in the for loop uses all the parameters of the arrays.
Perhaps Java has a different feature that can enforce a solution to my problem. I could accept another parameter called SIZE which will be used in the for loop, but that doesn't solve my problem if parameters 1 and 2 are of size 10 and the 3rd parameter is an array of size 9.
How can I enforce that the 3 arguments are all arrays that contain the exact same number of elements? Using an extra parameter that specifies the array sizes isn't very elegant and kind of dirty. It also doesn't solve the problem the array parameters contain different sized arrays.
You can't enforce that at compile-time. You basically have to check it at execution time, and throw an exception if the constraint isn't met:
Employee[] method(String[] employeeNames,
Integer[] employeeAges,
float[] employeeSalaries)
{
if (employeeNames == null
|| employeeAges == null
|| employeeSalaries == null)
{
throw new NullPointerException();
}
int size = employeeNames.length;
if (employeesAges.length != size || employeeSalaries.length != size)
{
throw new IllegalArgumentException
("Names/ages/salaries must be the same size");
}
...
}
Since the arrays being passed in aren't generated until runtime, it is not possible to prevent the method call from completing depending upon the characteristics of the array being passed in as a compile-time check.
As Jon Skeet has mentioned, the only way to indicate a problem is to throw an IllegalArgumentException or the like at runtime to stop the processing when the method is called with the wrong parameters.
In any case, the documentation should clearly note the expectations and the "contract" for using the method -- passing in of three arrays which have the same lengths. It would probably be a good idea to note this in the Javadocs for the method.
A way to skirt around the problem is to create a builder, e.g., EmployeeArrayBuilder,
public class EmployeeArrayBuilder {
private Integer arraySize = null;
private String[] employeeNames;
public EmployeeArrayBuilder addName(String[] employeeNames) {
if (arraySize == null) {
arraySize = employeeNames.length;
} else if (arraySize != employeeNames.length) {
throw new IllegalArgumentException("employeeNames needs to be " + arraySize + " in length");
}
this.employeeNames = employeeNames;
return this;
}
public EmployeeArrayBuilder addSalaries(float[] employeeSalaries) {/* similar to above */}
public EmployeeArrayBuilder addAges(Integer[] employeeAges) {/* similar */}
public Employee[] build() {
// here, you can do what you needed to do in the constructor in question, and be sure that the members are correctly sized.
Employee myEmployees[] = new Employee[arraySize ];// dont know what size is
for ( int count = 0; count < arraySize ; count++) {
myEmployees[count] = new Employee( employeeNames[count], employeeAges[count], employeeSalaries[count] );
}
return myEmployees;
}
}