I have an assignment where I have to read a data file containing student names and their GPAs, and put it into a linked list in ascending order. I've been struggling with the Linked List conceptually, and I've gotten to a point where I'm utterly lost. I'm not quite sure how to put the information I'm getting off of the file into a linked list. We can't use any of the built-in collection classes either, which makes it a bit more difficult.
public class Driver {
private LinkedList<Student> myList;
String name;
double doub;
public static void main(String[] args) {
new Driver();
}
public Driver() {
myList = new LinkedList<Student>();
FileInputStream file;
Student x = new Student(name, doub);
int count = 0;
try {
file = new FileInputStream("Student.dat");
BufferedReader read = new BufferedReader(new InputStreamReader(file));
String lineRead;
while ((lineRead = read.readLine()) != null) {
count++;
x = new Student(lineRead,count);
} catch (IOException e) {
e.printStackTrace();
}
I also have the "Student" class here.
public class Student<E> {
private String name;
private double gpa;
private E value;
private Student nextNode;
public Student(String name, double gpa) {
this.name = name;
this.gpa = gpa;
}
public Student(E x) {
value = x;
}
public Student getNext() {
return nextNode;
}
public E getValue() {
return value;
}
public void setValue(E x) {
value = x;
}
public void setNext(Student next) {
nextNode = next;
}
public String getName() {
return this.name;
}
public double getGPA() {
return this.gpa;
}
The data file also reads as
Emily 2.5
Evan 3.8
Lily 4.0
April 1.9
I've done some testing to try and figure out what is going on, mostly just adding methods to see what they put out and working it from there. For example I know that, If I were to put run "System.out.println(x.getGPA());" in the try catch, it would return the following
1.0
2.0
3.0
4.0
Which makes sense because I'm assuming its just reading the count instead of the numbers in the data file. If I were to run "Sysetm.out.println(x.getName());" in the try catch, it would return the following
Emily 2.5
Evan 3.8
Lily 4.0
April 1.9
This is almost correct, but If I run something like "System.out.println(myList);" I would just get
LinkedList#7852e922
LinkedList#7852e922
LinkedList#7852e922
LinkedList#7852e922
And I am 90% sure those are all null. Moving forward from that running "System.out.println(x);" returns this
Student#7852e922
Student#4e25154f
Student#70dea4e
Student#5c647e05
I think I've completely overthought this problem. There are a few other parts to the assignment, but I think if I can at least get this working I'll be set to move forward.
Related
I have a base class named Train:
public abstract class Train {
String serialNo;
float weight;
final String label;
public Train(String serialNo, float weight, String label) {
this.serialNo = serialNo;
this.weight = weight;
this.label = label;
}
public abstract float getCapacity();
}
And I have 2 classes implementing the abstract class(I will not include getters and setters in the code):
public class FreightTrain extends Train implements Cloneable {
private float capacityKg;
Vector<String> freightVector = new Vector<String>();
public FreightTrain(String serialNo, float weight, String label) throws Exception{
super(serialNo, weight, label);
if (weight < 0)
throw new Exception();
}
#Override
public float getCapacity() {
return this.capacityKg;
}
}
And the PassengerTrain class:
public class PassengerTrain extends Train implements Cloneable {
private float seatNo;
Vector<String> passengerId = new Vector<String>();
public PassengerTrain(String serialNo, float weight, String label) throws Exception{
super(serialNo, weight, label);
if (weight < 0)
throw new Exception();
}
#Override
public float getCapacity() {
return this.seatNo;
}
}
Next I have an array list ArrayList<Train> arr; which contains both: the PassengerTrain and FreightTrain. I want to create methods to write the items from arr to a file and read the data from file
Here is my attempt:
public void writeObjectInTextFile(ArrayList<Train> array) {
Formatter f = null;
try {
f = new Formatter(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
for(Train train: array) {
if(train instanceof PassengerTrain) {
String label = train.getLabel();
String serialNo = train.getSerialNo();
float capacity = train.getCapacity();
float weight = train.getWeight();
float seatNo = ((PassengerTrain) train).getSeatNo();
String passId = "";
for(String id:((PassengerTrain) train).getPassengerId()) {
passId += id;
}
f.format("%s %f %s %f %f %s", serialNo, weight, label, capacity, seatNo, passId);
} else if(train instanceof FreightTrain) {
String label = train.getLabel();
String serialNo = train.getSerialNo();
float capacity = train.getCapacity();
float weight = train.getWeight();
float capacityKg = ((FreightTrain) train).getCapacityKg();
String freightVector = "";
for(String freight: ((FreightTrain) train).getFreightVector()) {
freightVector += freight;
}
f.format("%s %f %s %f %f %s", serialNo, weight, label, capacityKg, capacity, freightVector);
}
}
f.close();
}
But I have a problem: I am unable to create a function that will read this data from the file, and return the correct ArrayList with the initial data.
What is the best and fastest way to write the array of 2 different classes deriving from a single class to a file?
And how it could be recreated?
Thank you!
Please don't my question as duplicate. I have searched for similar questions and my question is different from the ones available.
I don't know how to convert back the objects from file to their respective types. What If I have n deriving classes?
Simply have your classes implement Serializable and you'll be ready to write your objects to files (and read them back, of course) whenever you want! This includes arrays of your objects, too.
That's the 10000-foot answer above. Implementing Serializable correctly (in a way that won't come back around to bite you) is a somewhat daunting subject. However, there is plenty of literature out there that can teach you how to do it and how to avoid common pitfalls. I recommend Joshua Bloch's "Effective Java" for this, personally; he's dedicated a whole chapter or two to the subject.
if(train instanceof PassengerTrain) This is bad. You are completely throwing the whole purpose of inheritance and polymorphism away. Your code shouldn't care about what type the trains are.
You should use the Object serialization features of Java. Implement Serializable in your Train class and use ObjectOutputStream to write and ObjectInputStream to read the objects.
Write:
public void writeTrainsToFile(ArrayList<Train> array, String file) {
FileOutputStream fileOutputStream = new FileOutputStream(file);
try(ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)){
objectOutputStream.writeInt(array.size());
for(Train train: array) {
objectOutputStream.writeObject(train);
}
}
}
Read:
public void readTrainsFromFile(ArrayList<Train> array, String file) {
FileInputStream fileInputStream = new FileInputStream(file);
try(ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)){
int trainCount = objectInputStream.readInt();
for (int i = 0; i < trainCount; i++) {
array.add((Train)objectInputStream.readObject());
}
}
}
I'm trying to read a csv and storing the records in an ArrayList.
Since I know the no. of records in the csv file I'm specifying the size i.e. 600 when creating the object.
I want the program to be able to read files of unknown no. of records.
How do I make it dynamic.
Here's the working code for the file with 600 records.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.io.*;
public class BankRecords extends Client{
//Create objects for processing data
//private static int count;
static BankRecords[] obj=new BankRecords[600];
static List<List<String>> array = new ArrayList<List<String>>();
#Override
void readData() {
// TODO Auto-generated method stub
String line=" ";
//int i=0;
//try with resources statement
try(BufferedReader br = new BufferedReader(new FileReader("bank-Detail.csv"))){
while((line=br.readLine()) != null) //read from file
{
array.add(Arrays.asList(line.split(",")));
//check data
//count++;
//System.out.println(array.get(i++));
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
processData();
}
#Override
void processData() {
// TODO Auto-generated method stub
int idx=0;
for(List<String> bankData: array)
{
obj[idx]= new BankRecords();
obj[idx].setId(bankData.get(0));
obj[idx].setAge(Integer.parseInt(bankData.get(1)));
obj[idx].setSex(bankData.get(2));
obj[idx].setRegion(bankData.get(3));
obj[idx].setIncome(Double.parseDouble(bankData.get(4)));
obj[idx].setMarried(bankData.get(5));
obj[idx].setChild(Integer.parseInt(bankData.get(6)));
obj[idx].setCar(bankData.get(7));
obj[idx].setSact(bankData.get(8));
obj[idx].setCact(bankData.get(9));
obj[idx].setMort(bankData.get(10));
obj[idx].setPep(bankData.get(11));
idx++;
//System.out.println(obj[idx].getId());
}
printData();
}
#Override
void printData() {
//Printing First 25 ID, age, sex, region, income and mortgage
System.out.println("ID\t\tAGE\t\tSEX\t\tREGION\t\tINCOME\t\tMORTGAGE\n");
for(int i=0;i<25;i++){
String s=String.format("%s\t\t%s\t\t%s\t\t%-10s\t%8.2f\t%2s", obj[i].getId(),obj[i].getAge(),obj[i].getSex(),obj[i].getRegion(),obj[i].getIncome(),obj[i].getMort());
System.out.println(s);
}
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
public double getIncome() {
return income;
}
public void setIncome(double income) {
this.income = income;
}
public String isMarried() {
return married;
}
public void setMarried(String married) {
this.married = married;
}
public int getChild() {
return child;
}
public void setChild(int child) {
this.child = child;
}
public String getCar() {
return car;
}
public void setCar(String car) {
this.car = car;
}
public String getSact() {
return sact;
}
public void setSact(String sact) {
this.sact = sact;
}
public String getCact() {
return cact;
}
public void setCact(String cact) {
this.cact = cact;
}
public String getMort() {
return mort;
}
public void setMort(String mort) {
this.mort = mort;
}
public String getPep() {
return pep;
}
public void setPep(String pep) {
this.pep = pep;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
BankRecords bnk= new BankRecords();
bnk.readData();
}
}
ArrayList can the elements dynamically, so it is not required to know the size in advance.
However, for the BankRecords array, do not initialize it with 600 initially. Instead do something like this:
static BankRecords[] obj = null;
static List<List<String>> array = new ArrayList<List<String>>();
void processData() {
// TODO Auto-generated method stub
obj=new BankRecords[array.size()];
// TODO do your work here
}
You do not have to know the number of records beforehand in order to use an ArrayList. You can specify a default size in the constructor, however it is smart enough to expand itself if you add more records than this.
You are almost there, but for some strange reasons you are using Lists in places where you already have an array; yet on the other side, you are using an array where a List would be a much better fit.
You can rework your code as follows:
// TODO Auto-generated method stub
HINT: those TODOs are generated by your IDE. The idea is that you delete them as soon as you have some real content instead. Keeping them means leaving garbage in your source code. Anything that doesn't add real value to your source code: remove it. Always. Immediately!
String line=" ";
List<Bankrecord> records = new ArrayList<>();
//int i=0; ... again: unused code --- remove that!
try(BufferedReader br = new BufferedReader(new FileReader("bank-Detail.csv"))){
while((line=br.readLine()) != null) //read from file
{
String[] lineData = line.split(",");
BankRecord recordForNewLine = buildRecordFrom(lineData);
records.add(recordForNewLine);
} ...
And then you could rework your processData into something like:
private BankRecord buildRecordFrom(String[] lineData) {
BankRecord newRecord = new BankRecords();
newRecord.setId(lineData[0];
...
return newRecord;
}
And things that you should really consider changing, too:
Building your bank records by simply assuming that column contains a valid ID, and the next column contains a valid xyz ... is a bad idea.
Instead, you should be validating all your input: you should check that each array you gain from split has **exactly the expected length. And then have to validate that each value from that array has the expected "content"
Then, from a modelling perspective: you have a ton of setters on your Bankrecord class. But that is simply wrong! In real life, when some "record" is created, then its essential properties (such as its ID) can't be changed after creation!
Instead, you should make sure that such properties in your class can't be changed after the object has been created. The way to go here: Builder pattern!
Finally: my code above is meant as "inspiration point" to get you going. Dont blindly copy/paste it; there might be various typos in - just read it until you get what (and why) it is doing (what it is doing)!
Then: I hope you understand that real CSV parsing is much more complicated than splitting around "," (for example: strings in CSV data can contain ',' too; and then your simple split would rip up that string!)
If you are serious about parsing real-world-other-people CSV input, then you better look into using existing libraries to do this for you. Writing a correct CSV parser is hard work (and not very rewarding; since that means re-inventing a complicated wheel for no good reason)!
public class Example {
private static class Courses {
public final String name;
public final Courses[] children;
public Courses(String name, Courses ... children) {
this.name = name;
this.children = children;
}
}
public static void main(String[] args) {
Courses courses =
new Courses("School",
new Courses("Mathematics",
new Courses("Algebra"),
new Courses("Trig"),
new Courses("Calculus"),
new Courses("Calculus 2"),
new Courses("Geometry")),
new Courses("Sciences",
new Courses("Biology"),
new Courses("Chemistry"),
new Courses("Physics"),
new Courses("Business",
new Courses("Finances",
new Courses("Accounting"),
new Courses("Accounting 1"),
new Courses("Accounting 2"),
new Courses("Administration",
new Courses("Economics"),
new Courses("Business Studies"),
new Courses("Administration 1"),
new Courses("Accounting"))),
new Courses("Physical Education"))));
System.out.println(find(courses, "Economics", courses.name));
public static String find(Courses courses, String name, String currentPath) {
if((courses.name).equals(name)){
System.out.println(currentPath);
return currentPath + " / " + name;
}
else{
//System.out.println(currentPath);
for(Courses child:courses.children){
currentPath += " / " + child.name;
find(child, name, currentPath);
}
}
return currentPath + " / " + name;
}
}
So this is the code that I have acquired. I'm trying to determine what the right thinking pattern should be when coding this find courses method. This is an array but I'm thinking of it in like a tree like manner and trying to find the answer. IS that something you guys would do too? I'm trying to find a path like this School / Business / Administration / Economics. But either I'm getting the whole path or it's iterating through the whole thing. Also, what's the approach you guys will take to accomplish this. I wrote a recursive method to achieve this, but its not working out.
Thanks, for your help
CC
Thinking about it as a tree is obviously the way to go, as it is indeed a tree.
It might help you to think what you expect the method to do if the current value of courses was the parent of the node you want. That is in your case, if it's the Administration node. In your current implementation, you will iterate over all the children, never realizing you found the correct child!
As another hint, you would generally in recursion want to do something with the result of the recursive call. In your code, you call find(child, name, currentPath) and then you do nothing with the result!
Hope these hints help you.
Your question contains some opinion-based (sub)questions. These we cannot answer, but we can help with your recursive algorithm.
The if branch of your find method seems alright. It enters that branch when an exact match occurs. The problem is in your else branch, where you need recursion to keep looking in the children courses.
Note that your method declaration returns a String.
public static String find(Courses, String, String)
And note that, when you recursively call this function, you are ignoring its return value, rendering the recursive call useless.
for (Courses child: courses.children) {
currentPath += " / " + child.name;
find(child, name, currentPath); // <-- this return value is being ignored!
}
Start by assigning the return value of the recursive search to some variable, and define a return value for when the search doesn't find the provided name (you will not find Biology under Administration, for instance). I'll assume that an empty String means the search didn't find the course.
String result = find(child, name, currentPath);
if (!result.isEmpty()) {
// it has been found
}
Finally, note that you are changing the value of the currentPath variable on each iteration, by appending to it. That will result in erroneous paths, if the name is found after the first iteration. Assign that temporary path, for that iteration, to another variable.
I would do something like this
Course.java
public class Course {
private Course parentCourse;
private String name;
public Course(String name){
this.setName(name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Course getParentCourse() {
return parentCourse;
}
public void setParentCourse(Course parentCourse) {
this.parentCourse = parentCourse;
}
}
Courses.java
public class Courses {
private List<Course> courses;
public Courses(){
}
public List<Course> getCourses() {
return courses;
}
public void setCourses(List<Course> courses) {
this.courses = courses;
}
}
Main
Courses courses = new Courses();
List listCourses = new ArrayList<Course>();
Course generalMaths = new Course("General Maths");
Course linAlgebra = new Course("Linear Algebra");
linAlgebra.setParentCourse(generalMaths);
listCourses.add(generalMaths);
listCourses.add(linAlgebra);
courses.setCourses(listCourses);
Find path
for(Course course : courses.getCourses()){
StringBuffer coursePath = new StringBuffer();
coursePath.append(course.getName());
while(course.getParentCourse() != null){
course = course.getParentCourse();
coursePath.append(" | "+course.getName());
}
System.out.println(coursePath);
}
I am currently working with XML files, and am searching to have a better way to avoid try/catch blocks in a nice way.
Here is the thing. Let's say I have an XML file.
<A>
<BB>37</BB>
<CC>
<DDD>1</DDD>
</CC>
</A>
In fact, I turn this into an object, which means that I can do
myXml.getA() and so on.
In my code, I search a lot for given elements in this object, which means that I have a lot of lines like
int ddd = myXml.getA().getCC().getDDD();
The thing is that some elements may not be there, and for example another XML element can be like that only :
<A'>
<BB'>37</BB'>
</A'>
So if I try to get ddd, getCC() raises a NullPointerException.
In the end, I end up coding it like that :
int ddd;
try{
ddd = myXml.getA().getCC().getDDD();
}
catch (NullPointerException e){
ddd = 0;
}
This works but the code becomes really ugly.
I am searching for a solution to have something like
int ddd = setInt(myXml.getA().getCC().getDDD(), 0);
0 being the default in case the method raises an exception.
Is there a nice way to do that ?
Up to now, I couldn't find a solution that do not raise errors.
Thx for your help !
EDIT: Just not to get XML related answers.
I showed the xml part for everybody to understand the problem.
In my code, I don't have access to the XML, but only the object that represents it.
To make it short, what I'd really love is some kind of isNull method to test my getters.
This is sort of an annoyance of working with jaxb. in my company, we do enough work with jaxb that it was worth writing an xjc plugin which generated "safe" versions of every getter that were guaranteed to return non-null values for any non-trivial value (immutable instances in the case that a sub-object did not really exist).
Here's an example of what our generated model entities look like:
public class ExampleUser implements Serializable {
private final static long serialVersionUID = 20090127L;
#XmlAttribute
protected String name;
#XmlAttribute
protected String email;
public final static ExampleUser EMPTY_INSTANCE = new ExampleUser() {
private static final long serialVersionUID = 0L;
#Override
public void setName(java.lang.String value) { throw new UnsupportedOperationException(); }
#Override
public void setEmail(java.lang.String value) { throw new UnsupportedOperationException(); }
};
public String getName() {
return name;
}
public void setName(String value) {
this.name = value;
}
public String getEmail() {
return email;
}
public void setEmail(String value) {
this.email = value;
}
}
public class ExampleAccount implements Serializable {
private final static long serialVersionUID = 20090127L;
protected ExampleUser user;
#XmlElement(name = "alias")
protected List<String> aliases;
#XmlAttribute
protected String id;
#XmlAttribute
protected String name;
public final static ExampleAccount EMPTY_INSTANCE = new ExampleAccount() {
private static final long serialVersionUID = 0L;
#Override
public void setUser(com.boomi.platform.api.ExampleUser value) { throw new UnsupportedOperationException(); }
#Override
public List<String> getAliases() { return java.util.Collections.emptyList(); }
#Override
public void setId(java.lang.String value) { throw new UnsupportedOperationException(); }
#Override
public void setName(java.lang.String value) { throw new UnsupportedOperationException(); }
};
public ExampleUser getUser() {
return user;
}
public void setUser(ExampleUser value) {
this.user = value;
}
public List<String> getAliases() {
if (aliases == null) {
aliases = new ArrayList<String>();
}
return this.aliases;
}
public String getId() {
return id;
}
public void setId(String value) {
this.id = value;
}
public String getName() {
return name;
}
public void setName(String value) {
this.name = value;
}
public ExampleUser safeGetUser() {
return (getUser() != null) ? getUser() : ExampleUser.EMPTY_INSTANCE;
}
}
So you could write this code without fear of NPE:
userEmail = account.safeGetUser().getEmail();
You can look at the Null objec pattern.
For example :
public class A {
private C c;
public C getC() {
if (c == null) {
c = new C(0); // the "null object"
}
return c;
}
}
public class C {
private int d;
public C(int d) {
this.d = d;
}
public int getD() {
return d;
}
}
But personnaly, i have a bad feeling with this code :
int ddd = myXml.getA().getCC().getDDD();
It is a strong violation of the law of Demeter. The class invoker have a too large knowledge of A, C and D. This code will be clearly difficult to adapt and maintain.
The two general approaches to this sort of problem are the null object pattern that other answers have already covered, and type safe nulls such as Scala's Option.
http://www.scala-lang.org/api/current/scala/Option.html
There are a few Java versions of Option knocking around.
http://functionaljava.googlecode.com/svn/artifacts/2.20/javadoc/fj/data/Option.html
http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/Optional.html
Type safe nulls can be particular useful when combined with the flatmap.
Use Apache common-beanutils to create your set method. It will use reflection and then you have only a single place to catch the errors.
It would look something like this (haven't coded it so excuse syntax errors).
int getInt(Object root, String beanPattern, int defaultValue)
{
try
{
return PropertyUtils.getNestedProperty(root, beanPattern);
}
catch (Exception e)
{
return 0;
}
}
This would be called like so.
int ddd = getInt(myXml, "A.CC.DDD", 0);
Can't you just write a function which is general enough to be called for each value, and is returning the value or 0.
Something like
myGetSomething(FOO){
try {getFOO} catch ...
}
Then your Code itself looks nice, but the function has basically a try-catch for each call.
Use Xpath query instead of get methods. It will give you an empty list if it cannot find the element path.
List ddds = myXml.query("/AA/BB/CC/DDD");
if (!ddds.empty()) {}
The correct syntax depends on the XML library you use.
Write part of the code in Groovy or Xtend; both support the ?. syntax which returns null of the left hand side of the expression evaluates to null. They also get rid of the useless get so you can write:
myXml.a?.cc?.ddd
The syntax of Xtend is worse when compared to Groovy but it compiles to plain Java, so you just need to add a single JAR with some helper classes to your code to use the result.
I have this code which gets info from an other class but I have to add another line other code for every instance object.
public static int giveShipData(String shipName,int Data){
if(shipName.equals("Player")){i = Player.getData(Data);}
return i;
}
Is it possible to have something like:
public static int giveShipData(String shipName,int Data){
i = shipName.getData(Data);
return i;
}
Sorry if I am using the wrong terminology I am self taught and new.
I think you'd better to reconsider your design. If you have a ship name and ship data I assume you must have a Ship class which looks something like this:
public class Ship {
private String name;
private int data;
public Ship(String name, int data) {
this.name = name;
this.data = data;
}
public String getName() {
return name;
}
public int getData() {
return data;
}
}
Besides this class there should be a class like Shipyard:
public class Shipyard {
private Map<String, Ship> shipsByNameMap = new HashMap<String, Ship>();
public void registerShipByName(String name, Ship ship){
shipsByNameMap.put(name, ship);
}
public Ship getShipByName(String name){
return shipsByNameMap.get(name);
}
}
So, at first you invoke shipyard.getShip("Player") method to get ship object, than you can invoke ship.getData() method to retrieve ship data.
You might be able to do something like this...
int[] ships = new int[3]; // 3 ships
public void populateShips(){
for (int i=0;i<ships.length;i++){
ships[i] = giveShipData(shipname,data);
}
}
public int giveShipData(String shipName,int data){
return shipName.getData(data);
}
This would allow you to create any number of ships, just increase the size of the ships[] array to be however many ships you want.
Is this kinda what you're after?
As per your code "shipName" is a string...and it does not have getData() method. And why are you passing Data to the getData()... You could instead write something like this-
i = ClassObj.getData(shipname);
and in the method getData return the info regarding the "shipname".