Your code samples show a lot of confusion.
You are correct that, in Java, a file can be accessed in a manner similar to an array of byte
. As in your code sample, you can even read an entire file into a byte []
. But, when a file is treated as an array of byte
, it's up to the programmer to provide code that assembles the individual byte into various fields, whether primitives or Objects. That can be cumbersome.
Treating a file as an array of byte
isn't needed when the data is in a text file. Java offers several APIs that make it easier to process data in text files. FileInputStream
is NOT one of them. But, BufferedReader
is.
Why do you have a variable maze
in your code? Along with the 2D array of byte
, this makes it look like you took existing code, perhaps for a maze, and inappropriately attempted to adapt it to an unrelated problem.
If you are going to treat the data as a 2D array, it should be an array of String
. Each row in the array represents one row in the table / source data. Each element in a row represents one element. Think about it: You can't hold all 10 characters of a tariff code in one byte. If you use an array of byte
, it would have to be a 3D array. And that would be worse. Again, you would have to provide code to build fields from 'bye []`.
In the second code sample, you found a way, using BufferedReader
to read the file in as String
for each line. But, the code following goes backward: It converts the result into a char[]
. What you want to do is parse line
to extract the values of the fields it represents.
Java has a lot of tools you can use. One is the split
method of String
:
while ((line = br.readLine() ) != null ){
String [] = line.split (" ");
line.split(" ")
will be based on whitespace, as you wish. You could put the results into a 2D array you had in mind in the first place. But, you still have to parse to get numeric data so you can do the arithmetic.
To parse numeric data from a String
, you can use methods that take String
and return the type of value you want in these APIs:
When updating the array, you would need to convert updated numeric values back to String
. Example: foo [i][1] = Integer.toString (totalQuantity);
If the data is kept in a 2D String
array, the code might work like this:
- Read a line
- Parse the numeric values
- Search the array
- If a match is found, do the following:
-
- Parse the numeric fields in the matching row
-
-
- If a match is not found, add the new data as a new row in the array.
- Repeat as long as there are lines to be read.
You could reduce the amount of parsing by keeping separate arrays:
String [] name;
String [] tariffCode;
float [] weight;
int [] quanity;
BigDecimal [] price;
Now, you have 5 tightly coupled arrays, also known as parallel arrays. That is regarded as poor design, especially in an Object-Oriented Programming language, such as Java. It is better to create a class, and use Objects.
The class:
import java.math.BigDecimal;
import java.util.Objects;
import java.util.Scanner;
public class Product {
private String name;
private int quantity;
private float weight;
private BigDecimal price;
private String tariffCode;
public Product(String name, int qty, float wght, BigDecimal price,
String tariffCode) {
this.name = name;
this.quantity = qty;
this.weight = wght;
this.price = price;
this.tariffCode = tariffCode;
}
// generated by IDE
@Override
public int hashCode() {
int hash = 5;
hash = 47 * hash + Objects.hashCode(this.tariffCode);
return hash;
}
// generated by IDE
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Product other = (Product) obj;
return Objects.equals(this.tariffCode, other.tariffCode);
}
// this method allows a Product Object to represent
// inventory totals
public Product add (Product detail) {
this.quantity += detail.quantity;
this.weight += detail.weight;
this.price = price.add (detail.price);
return this;
}
}
You can add the getters and setters you need. I omitted the default constructor, i.e., public Product ()
. It would be a good idea to add it.
Note the class
has a method public boolean equals (Object obj)
. That will be needed for searches. Code like if (inventory [i].tariffCode == target.tariffCode)
won't work. See this question .
It also has a public int hashCode ()
method. hashCode
should always be overridden when equals
is. In fact, it will be needed for a version of this answer.
This class
allows use of one array instead of 5:
Product [] inventory;
When you instantiate arrays, you want to know how many lines there will be in the file you are reading. How do you know that, unless you already read the file?
One way to avoid reading the file twice is to set some maximum value.
Here is what we have so far, using the Product
class above:
public static void productSum1(String fileName) {
try {
FileReader dataFile = new FileReader(fileName);
BufferedReader dataSource = new BufferedReader(dataFile);
final int MAX_SIZE = 30_000;
Product [] inventory = new Product [MAX_SIZE];
int actualSize = 0;
String line;
String [] lineData;
Product lineItem;
while ((line = dataSource.readLine() ) != null ) {
lineData = line.split(" ");
lineItem = new Product ( lineData [0]
, Integer.parseInt(lineData[1])
, Float.parseFloat(lineData[2])
, new BigDecimal (lineData[3])
, lineData [4]
);
actualSize = update (inventory, actualSize, lineItem);
}
}
// catch blocks
}
public static int update (Product [] invent, int size, Product item) {
// code to search array and update
return size;
}
The code to search and update the array is left to you.
If the update
method does not find a matching item in the inventory totals, it will add it to the array. This makes it necessary to update actualSize
.
Note: Java is pass by value. Updating the local variable size
in the update
method will not result in an update of actualSize
in the calling method. For this reason, update
returns size
.
One of the comments noted the code sample has import java.util.Scanner;
but the code didn't use a Scanner
. Scanner
is a popular tool for extracting data from text sources. You could use a Scanner
to wrap the file, and let Scanner
methods parse the data for you.
You could have a constructor in the Product
class like this:
public Product (Scanner dataSource) {
this.name = dataSource.next();
this.quantity = dataSource.nextInt ();
this.weight = dataSource.nextFloat();
this.price = dataSource.nextBigDecimal();
this.tariffCode = dataSource.next ();
}
The code that reads the file
public static void productSum1(String fileName) {
try {
FileReader dataFile = new FileReader(fileName);
Scanner dataSource = new Scanner (dataFile);
final int MAX_SIZE = 30_000;
Product [] inventory = new Product [MAX_SIZE];
int actualSize = 0;
Product lineItem;
while (dataSource.hasNext()) {
lineItem = new Product (dataSource );
actualSize = update (inventory, actualSize, lineItem);
}
// rest of code omitted
Instead of an array, consider using a Collection
. Like an array, a Collection
can hold a number of related elements. But, a Collection
is more flexible than an array. A popular type of Collection
is an ArrayList
. Note that an ArrayList
and other Collection
Objects can change their own size.
Another popular Collection
is a HashMap
. When an search by key for a specific element is done on a HashMap
, it is almost always done in O(1) time.
Add this method to class Product
:
public String getKey () { return tariffCode; }
The code to update the inventory:
public static void productSum2(String fileName) {
try {
FileReader dataFile = new FileReader(fileName);
Scanner dataSource = new Scanner(dataFile);
Map<String, Product> inventory = new HashMap<>();
Product lineItem;
while (dataSource.hasNext()) {
lineItem = new Product(dataSource);
update(inventory, lineItem);
}
} // insert catch blocks here
}
public static void update (Map<String, Product> inventory, Product item) {
Product total = inventory.get (item.getKey());
if (total == null) {
inventory.put (item.getKey(), item);
} else {
inventory.put (total.getKey (), total.add(item));
}
}
You might not be familiar with HashMap
, so I did include the code for the update
method.
Note that HashMap
relies on a proper implementation of .equals
and .hashCode
in the Objects it stores and retrieves.
I omitted code to output the results to either a file or a console. If you used the HashMap
, you might find the .values()
method useful.
Notes:
- I did not attempt to run the code samples in this answer.
- This is not robust. It cannot handle errors in the input file.
- If you find errors in this answer, you may leave a comment or edit this answer.