How to implement reference data Java/DB - java

I've got a table with some factors that I need to incorporate into a Java program. At first I was thinking of hardcoding the number but it seems like a pain trying to create a data structure that will fit the factors. So I wanted to ask around and see if it would be better to implement this as reference data in a database, a flat file or in java. The number would change every six months and would be used for mathematical computations.
Thoughts?

For slow-changing data like this, I would use an external config file. Based on the structure of your data, it seems that a CSV would work well, and would be easy for a business user to edit using Excel.
If it will change more often, you need to generate the data programmatically, or you want to provide a UI for editing the data, you could move it to a database.

You would have to create a data structure to contain the data regardless of how you store them. But the data structure for this kind of data does not have to be complex. It is just a list of values with attributes. You don't have to store them in a complex table-like structure.
Loading the data from a flat text file would also be quite easy when representing the data as a single list.
public class DataTable {
private List<Entry> table = new ArrayList<Entry>();
public double getValue(Sex sex, MaritalStatus maritalStatus, AgeInterval ageInterval, Type type) {
for (Entry entry : table) {
if (entry.sex == sex && entry.maritalStatus == maritalStatus && entry.ageInterval == ageInterval && entry.type == type) {
return entry.value;
}
}
throw new IllegalArgumentException("Unknown value");
}
public void load(String filename) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(filename)));
String line;
while ((line = reader.readLine()) != null) {
StringTokenizer t = new StringTokenizer(line, ":");
table.add(new Entry(
Sex.valueOf(t.nextToken()),
MaritalStatus.valueOf(t.nextToken()),
AgeInterval.valueOf(t.nextToken()),
Type.valueOf(t.nextToken()),
Double.valueOf(t.nextToken())));
}
} catch (IOException e) {
throw new IllegalStateException("Failed to read the data file", e);
}
}
}
enum Sex {M, F}
enum MaritalStatus {SINGLE, MARRIED}
enum AgeInterval {I16_21, I22_35, I35_55, I55}
enum Type {GD, NGD} // Whatever this is ...
class Entry {
Sex sex;
MaritalStatus maritalStatus;
AgeInterval ageInterval;
Type type;
double value;
Entry(Sex sex, MaritalStatus maritalStatus, AgeInterval ageInterval, Type type, double value) {
this.sex = sex;
this.maritalStatus = maritalStatus;
this.ageInterval = ageInterval;
this.type = type;
this.value = value;
}
}
The data file would look like this:
M:SINGLE:I16_21:GD:1.10
F:SINGLE:I16_21:GD:1.20
...

You could represent it as XML, but that might be a little heavy for such numeric data. But the XML would allow you to be fairly descriptive and self documenting. Then later you could easily parse this into Java(or another language of your choice).
Partial XML example:
<dataset>
<gd>
<16to21>
<single>
<male>1.10</male>
<female>1.20</female>
</single>
<married>
<male>0.90</male>
<female>0.80</female>
</married>
</16to21>
...
</gd>
<ngd>
...
</ngd>

One way that you could break up the fields is gender, age, marital_status, GD_VS_NGD, the data inside the table, and some identifier for the time period that you are using this data for unless you do not need to keep records of the data.

Related

How to specify a class property from a string in java

So I am reading from a file with scanner it has the similar format:
title, name, age
Mr, Matthew, 20
mr, Paul, 30
miss, Anne, 24
CSV^
class person{
String name, title;
int age;
public crimeData(String csv){
String[]list = csv.split(",", -1);
name = list[0];
title = list[1];
age = list[2];
}
}
Console Program
Scanner input = new Scanner(System.in);
System.out.println("Please select what data you want to load:");
String selection = input.next();
int temp = 0;
for(int i=0; i< header.length; i++){
if(header[i].equals(selection)){
temp = i;
break;
}
}
temp will give us the index of the option specified so if it is 2 we will want to access the age property
When my console application runs I prompt them(the user) for the data that they want.
So they may enter "age" So I am lost on how I may take this "age" String and access the person object with it.
The ideal case for the program output should be: 20,30,24 going through each age and printing
I take their input so String input = scanner.nextLine();
Then I loop through my array of person objects to get the index of the input. Once I have this index I then want to access the property of person at the index. So like if my index was 1 I would want to access the property 'name'.
In javascript I could take the string and say person['age'] although java's a whole different story. I have looked into java's "reflection API" although it's a heavy learning curve.
I have looked into java's "reflection API" although it's a heavy learning curve.
Well, Reflection is the way to go. It's widely used in many frameworks.
But perhaps a simpler solution will fit your needs. Use a switch to decide which attribute to return, and encapsulate this in a method of the Person class:
class Person {
private String name, title;
private int age;
public loadData(String csv){
String[] list = csv.split(",");
name = list[0];
title = list[1];
age = Integer.parseInt(list[2]);
}
public Object attribute(String attribute) {
switch (attribute) {
case "name": return this.name;
case "title": return this.title;
case "age": return this.age;
default: throw new RuntimeException("Invalid attribute: " + attribute);
}
}
}
Encapsulating the switch inside the method is in line with OOP principles, since it hides how attributes are stored from other objects, only exposing an interface to query them. Reflection breaks all encapsulation.
Though in general I am not in favor of using Map for holding fields for an object, if the number of properties is large and could even potentially vary across CSV files (e.g., some file has the University a person attended, another does not), then using a Map to hold the properties might be appropriate.
In this case, one would define a simple Person class:
public class Person {
Map<String, String> props = new HashMap<>();
public void addProperty(String propertyName, String value) {
// could add error checking to ensure propertyName not null/emtpy
props.put(propertyName, value);
}
/**
* returns the value of the property; may return null
*/
public String getProperty(String propertyName) {
return props.get(propertyName);
}
}
If it is know that certain attributes/properties will always be loaded, then accessors such as getName() could be added:
public String getName() {
return props.get("name");
}
public int getAge() {
String age = props.get("age");
// or throw exception if missing
return (age != null ? Integer.parseInt(age) : -1);
}
Though note I would expect name to not be a single entry for most datasets, as there typically would be last name, first name, etc. Nonetheless, the pattern for a limited number of commonly expected values is the same. Also, you can adapt so that you could get integer values directly for certain well-known fields.
Then, when you parse the file, you keep the title row that has the attribute definitions. Then for each row that you subsequently read, you create a new Person object, and then add the properties in order.
List<Person> allPersons = new ArrayList<>();
while ( (line = READ_NEXT_LINE) ) {
// NOTE: this is not a safe way to handle CSV files; should really
// use a CSV reader as fields could have embedded commas
attrs[] = line.split(",");
Person p = new Person();
for (int i = 0; i < titleRow.length; ++i) {
p.addProperty(titleRow[i], attrs[i]);
}
allPersons.add(p);
}
You can then get a specific Person by Person myPerson = allPersons.get(index_of_person), and much akin to the way you would have used Javascript, you can do String val = myPerson.getProperty("age").
If you need to search by a given attribute, you can then stream/loop over the allPersons and check of equivalence based upon a given property.
// find all people of a given age
List<Person> peopleAge20 = allPersons.stream()
.filter(p -> p.getAge() == 20)
.collect(Collectors.toList());
System.out.println(peopleAge20);
// summary statics (average age) for all people
IntSummaryStatistics stats =
allPersons.stream().mapToInt(p -> p.getAge()).summaryStatistics();
System.out.printf("Average age: %f\n", stats.getAverage());
Note that this approach does break the idea of a Javabean, but that may or may not be an issue depending upon your requirements.
First thing, we should add a constructor to your Person class.
class Person {
public Person(String name, String title, int age) {
this.name = name;
this.title = title;
this.age = age;
}
}
Now while you read the input you can use a Map as follows. Here after reading each line, we create a Person object and then using that person's age we make an entry in the map with key as age and value as Person.
Map<Integer, Person> mapOfPeople = new HashMap<>();
while (input.hasNextLine()) {
String line[] = input.nextLine().split(",");
Perso person = new Perso(line[1], line[0], Integer.parseInt(line[2].trim()));
mapOfPeople.put(person.getAge(), person);
}
Now to fetch a particular Person by age just do
mapOfPeople.get(20);

Sorting by translated enum

I'm having a following problem: I have to sort entities by enum parameter. The thing is, that enum name is not equivalent to its translated name, for example, the enum values can be:
enum Sample {
Bus, Car, Train
}
However, let's say in my language, Bus corresponds to pks, Car to auto, and Train to ciuchcia, co their order should be:
Car, Train, Bus and not Bus, Car, Train. It's just an example, my problem involves something like 10 different values.
The problem is, I can't get all the data, then perform a sort in Java, because the data is paginated. I tried to solve this problem by doing this in SQL (the data is from database view):
(CASE sample WHEN 'Car' THEN 1 WHEN 'Train' THEN 2 WHEN 'Bus' THEN 3 ELSE 0 END)
I'm sorting by number, and this solution works. However, I feel like this can be done better, and doesn't need to be modified each time I want to add something. Any help would be very appreciated.
If you can hard-code or populate the translation in the enum you can make the enum generate the query.
enum Sample {
Bus("pks"), Car("auto"), Train("ciuchcia");
private final String localName;
Sample(String localName) {
this.localName = localName;
}
private static final List<Sample> inLocalOrder = Arrays.stream(values())
.sorted((a,b) -> a.localName.compareTo(b.localName))
.collect(Collectors.toList());
public static CharSequence inLocalOrder() {
StringBuilder sb = new StringBuilder("(CASE");
int i = 1;
inLocalOrder.stream().forEach(a -> sb.append(" WHEN '"+a.name()+"' THEN "+i));
sb.append(" ELSE 0 END)");
return sb;
}
}
public void test(String[] args) {
System.out.println(Sample.inLocalOrder());
}
prints:
(CASE WHEN 'Car' THEN 1 WHEN 'Train' THEN 2 WHEN 'Bus' THEN 3 ELSE 0 END)
If the translations happen later then a minor adjustment should suffice.
First things first. Why you're using an Enum to sort stuff? I can't agree with this feature, but it's ok if you really need it.
I really appreciate #OldCurmudgeon's answer, but I would have done something different.
Instead of using the property value as the column name, I would use a method to request the correct column name (obviously I'm supposing that you don't have the entities mapped inside your code, so you're using an Enum to sort them). This way:
enum Sample {
Bus, Car, Train;
public String getColumnName() {
// GET IT FROM SOME RESOURCE OR REQUEST IT FROM ANOTHER CONTEXT OR JUST RETURN IT THE WAY IT IS
// YOU CAN USE A RESOURCE FILE TO MAP YOUR COLUMN NAME, SO IF THE ENTITY CHANGES, YOU DON'T NEED
// TO UPDATE YOUR CODE...
return "";
}
public static String getSortSQL(Map<Sample, Integer> samples) {
StringBuilder sb = new StringBuilder("(CASE");
samples.forEach((sample, number) -> addSampleNumberSQL(sb, sample, number));
return sb.append(" ELSE 0 END)").toString();
}
private static StringBuilder addSampleNumberSQL(StringBuilder sb, Sample sample, Integer number) {
return sb.append(" WHEN '").append(sample.getColumnName()).append("' THEN ").append(number);
}
}
As you can see, you can request your column name from any resource you want. But this implementation still weak because if you need to sort by any new column, you will need to add another Enum value. So I would implement something more powerfull, like a properties reader that read each property from the resource and a method that receive an String (mean the property itself) and a number (to sort by the number) so I would do something like this:
public static String getSortSQL(Map<String, Integer> properties) {
StringBuilder sb = new StringBuilder("(CASE");
properties.forEach((prop, number) -> addSampleNumberSQL(sb, prop, number));
return sb.append(" ELSE 0 END)").toString();
}
private static StringBuilder addSampleNumberSQL(StringBuilder sb, String property, Integer number) {
return sb.append(" WHEN '").append(property).append("' THEN ").append(number);
}
Hope it helps you...

Navigation through Objects in Java

My aim is to recreate the structure of XML in custom Objects to operate with it further. Actually, I want to have XML as input and produce LaTeX as output. For this task I have implemented principles of JAXB library. But don't think that this is a good idea, because it is not convenient to retain the needed structure of document as output in TeX.
Here is an example of my custom class:
public class Section {
private String title;
private List<Par> par;
private List<SubSec> subsec;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = "\\section {" + title + "}";
}
public List<Par> getPar() {
if (par == null) {
par = new ArrayList<Par>();
}
return this.par;
}
public List<SubSec> getSubSec() {
if (subsec == null) {
subsec = new ArrayList<SubSec> ();
}
return this.subsec;
}
}
So I have a list of Section class, which have titles, list of paragraphs (Par) and list of subsections (SubSec) (simplify LaTeX article structure). Paragraphs contain text, but subsection can include also list of paragraphs.
After XML input I transfer all Data from it in objects, instances of this Classes.
As example:
List<Section> listSections = new ArrayList<Section>();
// omitting the actions to recreate the structure and set values to Objects
// now, to retrieve and write:
for (int j = 0; j < listSections.size(); j++) {
List<Par> listParText = listSections.get(j).getPar();
writer.write(listSections.get(j).getTitle());
writer.newLine();
for (Par parList : listParText) {
if (parList.getText() != null) {
writer.write(parList.getText());
writer.newLine();
}
}
}
The problem is, that I can't recreate the structure of the document on the stage custom objects -> TeX. Although the structure is preserved on stage XML - custom objects. In Objects model I have, for example:
Section1(title): Par(text), Par(text), Par(text)
Section2(title): Subsection1(title): Par(text), Par(text), Par(text)
Subsection2(title): Par(text), Par(text)
Section3(title): Par(text)
Is there a way to save this order and get value in the same order to write them to file? Get values with getters and setters is NOT a problem to me, problem to retrieve them with proper order.
Update
To clarify the problem, lets suppose every Section contains paragraphs (Par), subsection (SubSec), Tables, Figures in certain order. But obviously Java not allow to make a list like: List<SubSec, Par, Table, Fig>. I can put information there in certain order, but not retrieve. Or can I?
Would it work to make a parent class, say DocumentComponent, of which SubSec, Par, Table, and Fig were all subclasses, and then say that a document is an ordered list of DocumentComponents?

How to easily process CSV file to List<MyClass>

In my application I use a lot of CSV files which I have to read and build a lists based on them. I'd like to discover an easy way to do this. Do you know any easy framework which does it without using number of config files etc?
For instance, I have got a class Person:
public class Person {
String name;
String surname;
double shoeSize;
boolean sex; // true: male, false:female
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public double getShoeSize() {
return shoeSize;
}
public void setShoeSize(double shoeSize) {
this.shoeSize = shoeSize;
}
public boolean isSe) {
return sex;
}
public void setSeboolean sex) {
this.sex = sex;
}
}
For this class, I have prepared CSV file:
name,surname,shoesize,sex
Tom,Tommy,32,true
Anna,Anny,27,false
How can I do it easily?
One of the simplest ways to read and serialize data is by using the Jackson library.
It also has an extension for CSV, you can find the wiki here
Let's say you have a Pojo like this:
#JsonPropertyOrder({ "name", "surname", "shoesize", "gender" })
public class Person {
public String name;
public String surname;
public int shoesize;
public String gender;
}
And a CSV like this:
Tom,Tommy,32,m
Anna,Anny,27,f
Then reading it is done like so:
MappingIterator<Person> personIter = new CsvMapper().readerWithTypedSchemaFor(Person.class).readValues(csvFile);
List<Person> people = personIter.readAll();
This is simple enough for my taste, basically all you need to do is add the column order in your CSV file using the #JsonPropertyOrder annotation and then just read the file using the above 2 lines.
There are lot of good frameworks written in Java to parse a CSV file and form a List of Objects. OpenCSV, JSefa & jCSV are to name a few of them.
For your requirement, I believe jCSV suits the best. Below is the sample code from jCSV which you can make use of easily.
Reader reader = new FileReader("persons.csv");
CSVReader<Person> csvPersonReader = ...;
// read all entries at once
List<Person> persons = csvPersonReader.readAll();
// read each entry individually
Iterator<Person> it = csvPersonReader.iterator();
while (it.hasNext()) {
Person p = it.next();
// ...
}
Moreover, parsing a CSV file and converting it to a List isn't a big deal and it can be achieved without using any framework, as shown below.
br = new BufferedReader(new FileReader(csvFileToRead));
List<Person> personList = new ArrayList<>();
while ((line = br.readLine()) != null) {
// split on comma(',')
String[] personCsv = line.split(splitBy);
// create car object to store values
Person personObj = new Person();
// add values from csv to car object
personObj.setName(personCsv[0]);
personObj.setSurname(personCsv[1]);
personObj.setShoeSize(personCsv[2]);
personObj.setGender(personCsv[3]);
// adding car objects to a list
personList.add(personObj);
}
If the mapping of CSV columns to bean object is complex, repetitive or large in real case scenario, then it can be done easily by using DozerBeanMapper.
Hope this will help you.
Shishir
Not sure if you need to go as far as using an external library (and taking the usually implied performance hit). It's a pretty simple thing to implement. And if nothing else, it always helps to know what's going on behind the scenes in such a library:
public List<Person> readFile(String fileName) throws IOException {
List<Person> result = new ArrayList<Person>();
BufferedReader br = new BufferedReader(new FileReader(new File(fileName)));
try {
// Read first line
String line = br.readLine();
// Make sure file has correct headers
if (line==null) throw new IllegalArgumentException("File is empty");
if (!line.equals("name,surname,shoesize,sex"))
throw new IllegalArgumentException("File has wrong columns: "+line);
// Run through following lines
while ((line = br.readLine()) != null) {
// Break line into entries using comma
String[] items = line.split(",");
try {
// If there are too many entries, throw a dummy exception, if
// there are too few, the same exception will be thrown later
if (items.length>4) throw new ArrayIndexOutOfBoundsException();
// Convert data to person record
Person person = new Person();
person.setName ( items[0] );
person.setSurname ( items[1] );
person.setShoeSize(Double .parseDouble (items[2]));
person.setSex (Boolean.parseBoolean(items[3]));
result.add(person);
} catch (ArrayIndexOutOfBoundsException|NumberFormatException|NullPointerException e) {
// Caught errors indicate a problem with data format -> Print warning and continue
System.out.println("Invalid line: "+ line);
}
}
return result;
} finally {
br.close();
}
}
Note that the catch statement uses Java 7 multi-catch. For older Java versions, either split it into 3 catch blocks or replace ArrayIndexOutOfBoundsException|NumberFormatException|NullPointerException with Exception. The latter is usually discouraged as it masks and ignores all other exceptions as well, but in a simple example like this, the risk is probably not too high.
This answer, unfortunately, is specific to your problem, but given that it is very straight forward, it should be easy to adapt to other situations as well...
Another neat thing you can do is to match line inside the while loop with a regular expression rather than simply splitting it based on a comma. That way you could also implement data validation in one shot (for example only match a sensible number for shoe size).
Note that the above implementation doesn't work if you have names that contain commas which are then enclosed in quotes (like "Jackson, Jr." as a last name). You can cover this case "easily" if you use regular expressions as described above, or by checking the first letter of the last name and if it is a quotation mark, combine item[1] with item[2] and use item[3] and item[4] instead for the shoesize and sex. This special case will likely be covered by most of the external libraries suggested here, so if you're not worried about any dependencies, licensing issues, and performance hits, those might be the easier way out...
opencsv is a good and simple solution. It is a small but powerful library. You can download it from the opencsv website (direct download from sourceforge, use the jar in the deploy directory) or use maven.
The java bean mapping feature makes it really simple because your CSV column names are matching the property names of your class (it ignores the different capitalisation).
How to use it:
Reader reader = // ... reader for the input file
// let it map the csv column headers to properties
CsvToBean<Person> csvPersons = new CsvToBean<Person>();
HeaderColumnNameMappingStrategy<Person> strategy = new HeaderColumnNameMappingStrategy<Person>();
strategy.setType(Person.class);
// parse the file and get a list of persons
List<Person> persons = csvPersons.parse(strategy, reader);
That's all.
I solved this recently by using Immutables and Jackson, and I think it's a great way to go if you're willing to use these libraries.
Immutables and Jackson integrate very well. To take OP's example, all you'd have to do is specify the Immutables class like so (annotations qualified for snippet explicitness):
#org.immutables.value.Value.Immutable
#com.fasterxml.jackson.databind.annotation.JsonDeserialize(as = ImmutablePerson.class)
public interface Person {
String getName();
String getSurname();
double getShoeSize();
boolean getSex();
}
Then, using the Jackson CSV module, you can easily deserialize each row of the CSV into the class Immutables has generated for you:
List<Person> loadPeople(File personsCsvFile) throws IOException {
CsvSchema schema = CsvSchema.emptySchema().withHeader();
MappingIterator<Person> personsIterator = new CsvMapper()
.readerFor(Person.class)
.with(schema)
.readValues(personsCsvFile);
return personsIterator.readAll();
}
Use OpenCSV
Here is a complete example that reads entries and adds them to a List:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
import au.com.bytecode.opencsv.CSVReader;
public class CSVReaderImplementor {
private String fileName;
private CSVReader reader;
private List<String[]> entries;
public CSVReaderImplementor(String fileName) throws IOException, FileNotFoundException {
this.fileName = fileName;
reader = new CSVReader(new FileReader(this.fileName));
entries = reader.readAll();
}
public List getEntries() {
return entries;
}
public static void main(String[] args) throws FileNotFoundException, IOException {
CSVReaderImplementor cri = new CSVReaderImplementor("yourfile.csv");
for(int i = 0; i < 50; i++) {
System.out.println(cri.getEntries().get(i).toString());
}
}
}
A List of type String[] is returned. You can iterate through the String array for each entry in the list and use the values at each index to populate your Bean constructor.
I think SuperCSV + Dozer easy to use and quite robust for java bean CSV serialization
http://supercsv.sourceforge.net/dozer.html

Java Input Output Logic

First of all, id like to thank this fourm, as I am finding myself quickly improving through all the material on this forum and all the help different members have been giving me. So this is just a big thank you for all of that. As for my question, I've been experimenting around with input out and wanted to see if this logic would work. I am trying to get the appropriate things in their appropriate array, and wanted to see if this logic would do it. Currently (and for a while) I wont be in a place where I can access any Virtual IDE effectively so all this was kinda done on the fly using notepad, word etc. *So don't be to hard on my syntax. What I am mostly concerned about is the logic (if it would work) and to a lesser mistake any major mistakes in code.*
Thanks alot.
So basically, the text file goes like this. Title, one line of space, then name, age and wage and the separator is the #. Then right below that, name, age and wage the separator bring # etc etc.
(pretend there was no line spaces between Bobby, Sandy, Roger, Eric and David..so pretend in the txt file they are right under each other, but there is a gap in between information and bobby.
Information
Bobby#24#5.75
Sandy #19#10.22
Roger #27#6.73
Eric#31#8.99
David#12#3.50**
Here is the logic i've come up with.
public class Practice {
public static void main(String[] args) throws Exception {
String Name [] = new String [5];
int Age [] = new int [5] ;
double Wage [] = new double [5];
String Blank [] = new String [5];
FileReader inputfile = new FileReader (new File(info.txt));
BufferedReader InputBuffer = new BufferedReader (inputfile);
String Title = InputBuffer.readline (); // to get the title
int count = 0;
while (InputBuffer.readline() = null) { // this while loop grabs the blank under the title
Blank [count] = count;
}
int i = 0;
while (InputBuffer.readline() !=null) {
String Getter = InputBuffer.readline (); // reads line
String splitup= Getter.split(#); // splits it
Name [i] = splitup[i]; // puts name in this array
Age [i] = splitup([i] + 1); // age in this array
Wage [i] = splitup([i] + 2); // wage in this array
}
InputBuffer.close();
}
}
Would this logic work for storing the title in the title String, the Blank line under the Blank Array, the name under the name array, age under the age array and the wage under the wage array??
Thanks alot.
P.S: Mostly concerned about the last while loop, I want to know if it will put the name in the name array, the age in the age array and the wage in the wage array.
First of all, you only need one while-loop. I don't understand why you have two, especially since the conditional in the first is nonsensical ( InputBuffer.readline() = null ).
Your loop would look something like this:
boolean isTitleParsed = false;
String title;
String line;
while ( (line = inputBuffer.readLine()) != null ) {
if (!isTitleParsed) {
// this is the title
title = line;
isTitleParsed = true;
} else if (line.isEmpty()) {
// this is a blank line; logic for dealing with blank lines here
...
} else {
// this is actual person data
String[] personData = line.split("#");
if (personData != null && personData.length == 3) {
String name = personData[0];
String age = personData[1];
String wage = personData[2];
...
}
...
}
}
Secondly, I think using arrays is entirely the wrong way to go. Like #AVD mentioned in his comment on the OP, List<T> and a POJO is probably a much better solution -- and much more extensible.
And finally: no, as you've written it, your second loop will not successfully save the name, age, and wage to the arrays. You never increment i and the syntax splitup([i] + 1) is just wrong. (You probably meant splitup[i+1].)
Using Arrays
If you're really stuck on using arrays to save your data, you'd have to do in something like this:
String[] names = new String[5];
String[] ages = new String[5];
String[] wages = new String[5];
...
int index = 0;
while ( (line = inputBuffer.readLine()) != null && index < 5) {
if (!isTitleParsed) {
...
} else if (line.isEmpty()) {
...
} else {
// this is actual person data
String[] personData = line.split("#");
if (personData != null && personData.length == 3) {
String name = personData[0];
String age = personData[1];
String wage = personData[2];
names[index] = name;
ages[index] = age;
wages[index] = wage;
index++;
} else {
System.err.println("Line " + line + " is malformed and was not saved.");
}
...
}
}
Notice that index is instantiated at 0, but is incremented every time we save something to the arrays. This way names[0] will hold the first name, names[1] will hold the second, and so on.
Notice also that we save a given record's name, age, and wage all at the same index. So we could expect names[0] to hold "Bobby", ages[0] to hold "24", and wages[0] to hold "5.75" -- all of which are related to the same record.
Finally, the condition in the while loop has been amended to be (line = inputBuffer.readLine()) != null && index < 5. This means we'll keep looping through the lines of the file until we either run out of lines (the file ends) or our index becomes greater than 5, which is the size at which we instantiated the array. This is one reason why arrays are such a bad structure to hold this data: you have to know exactly how many records you have in your file, and you may end up not filling them all the way (you allocated too much space) or not saving some records because you have no more room to store them.
Using POJOs
A much better way to save the data would be to use a POJO -- a Plain Old Java Object. This kind of object is pretty much a "data holder" object.
In your case, it would be something like this:
public class PersonData {
private String name;
private String wage;
private String age;
public PersonData() {
this(null, null, null);
}
public PersonData(String name, String wage, String age) {
this.name = name;
this.wage = wage;
this.age = age;
}
// ... getters and setters here
}
In your code, you'd replace your arrays with a List structure of PersonData objects:
List<PersonData> records = new ArrayList<PersonData>();
And in your while loop, you'd save into these objects instead of into the arrays:
// in the else in the while loop:
String[] data = line.split("#");
if (data != null && data.length == 3) {
PersonData record = new PersonData(data[0], data[1], data[2]);
records.add(record);
} else {
// error handling for malformed line
}
Now if you wanted to get data for a particular record, you'd just need to extract the PersonData object from your records list and query it:
// assuming the first record we scraped was "Bobby#24#5.75"
PersonData person = records.get(0);
person.getName(); // returns "Bobby"
person.getAge(); // returns 24
person.getWage(); // returns 5.75
Since we're using a List and not an array, we don't have to worry about knowing exactly how many records there are in the file, and we don't run the risk of losing information because we don't have anywhere to store it.
This way we can also know for certain that a name, age, and wage are all related to the same record, whereas before we were just hoping that, say, all records at index 0 in the arrays were related to the same person.
Also, if you add additional data to the records -- for example, name#age#wage#favorite food -- all you have to do is add a new field to the PersonData object and add a line in your parsing method to add that data to the object. If you were using arrays, you'd need to add a new array, and so on.
It's also much easier to create logic if, say, you have a row that only has a name or that missing a wage, and so on -- so that you're actually able to save the data in some meaningful fashion.
If you want to make good progress in Java or any OOP Language for that matter you should always approach a problem in a Object Oriented Manner.
For the problem at hand you should always consider a class to store the Person Info rather than using associative arrays.
class PersonInfo {
PersonInfo(String name,int age,float wage) {
this.name = name;
this.age = age;
this.wage = wage;
}
String name;
int age;
float wage;
}
The code is more or less the same from above...but it should give a List of PeopleInfo as output.
List<PersonInfo> peopleInfo = new ArrayList<String>();
boolean isTitleParsed = false;
while ( (line = inputBuffer.readLine()) != null ) {
if (!isTitleParsed) {
// this is the title
title = line;
isTitleParsed = true;
continue;
} else if (line.isEmpty()) {
// this is a blank line; logic for dealing with blank lines here
} else {
String[] personData = line.split("#");
if (personData != null && personData.length == 3) {
peopleInfo.add(new PersonInfo(personData[0],personData[1],personData[2]));
}
}

Categories