Disclaimer: I just learnt how to do this yesterday, and I am desperate
I have 3 classes (Student, ClassSchedule, ClassEnrolment)
public class ClassEnrolment {
ClassSchedule cs;
Student stud;
public ClassEnrolment(ClassSchedule cs, Student stud) {
super();
this.cs = cs;
this.stud = stud;
}
}
-----
public class Student{
private String name;
private String matricNo;
ClassEnrolment classroom;
ClassSchedule schedule;
}
-----
public class ClassSchedule {
String classType;
int index;
int group;
String day;
String time;
String venue;
String remark;
String courseCode;
}
I am trying to read/write a text file (database). I am having issues with 3 lines inside "/////////////////".
I am aware that the attributes declared in ClassEnrolment are not int nor string. How should I do this? How do I bring ClassSchedule and Student as part of StringTokenizer?
The textfile stores index from ClassSchedule and matricNo from Student. I have a feeling I am doing this wrongly.
public static ArrayList readEnrolment(String filename) throws IOException {
ArrayList stringArray = (ArrayList) read(filename);
ArrayList alr = new ArrayList();
for (int i = 0; i < stringArray.size(); i++) {
String st = (String) stringArray.get(i);
StringTokenizer star = new StringTokenizer(st, SEPARATOR);
///////////////////////////////////////////////////
int cs = Integer.parseInt(star.nextToken().trim());
String stud = star.nextToken().trim();
ClassEnrolment enrolment = new ClassEnrolment(cs, stud);
//////////////////////////////////////////////////
alr.add(enrolment);
}
return alr;
}
public static void saveEnrolment(String filename, List al) throws IOException {
List alw = new ArrayList();
for (int i = 0; i < al.size(); i++) {
ClassEnrolment enrolment = (ClassEnrolment) al.get(i);
StringBuilder st = new StringBuilder();
st.append(enrolment.getCs2());
st.append(SEPARATOR);
st.append(enrolment.getStud2());
alw.add(st.toString());
}
write(filename, alw);
}
Your problem here is an issue of Serialization. You could very well declare your classEnrolment as Serializable(do not forget the serialUUID) and let the classic java serialization process to write your objects as bytes in your file.
However, what you are trying here is to conceive a custom serializer for your class. I'd advise you to use generics in the likes of :
public interface Serializer<T> {
byte[] write(T objectToSerialize);
//to build a factory/service around it
boolean canDeserialize(byte[] serializedObject);
T read(byte[] serializedObject);
}
So that your methods read/writeEnrollment will simply be creating a Serializer<ClassEnrollment> and letting it do its job (which will probably create a Serializer<Student> and a Serializer<ClassSchedule>) .
For the matter of the Serialization process, I have to say that the StringTokenizer is a legacy class from JDK 1 whose use is discouraged by its own javadoc :
StringTokenizer is a legacy class that is retained for compatibility reasons although its use is discouraged in new code. It is recommended that anyone seeking this functionality use the split method of String or the java.util.regex package instead.
And if you want to Serialize objects of potentially different classes than just your ClassEnrollment, it would be nice to be able to distinguish the class used. There are many ways to do it, and as one commenter said, json serialization would fare very well.
Related
I'm writting a simple model of a social media where a users friends is represented as an array of Friend objects.
I am getting the titled error with the use of the method toStringFriends used in the main method. Doe anyone know how to fix this problem?
public class Friend {
private String name;
private String password;
private int uid;
private Friend [] friends;
public Friend (String name, String password, int uid, Friend [] friends) {
this.name = name;
this.password = password;
this.uid = uid;
this.friends [0] = friends [0];
}
public Friend [] getFriends () {
return friends;
}
public String toStringFriends (Friend [] friends) {
String s = "";
for (int i = 0; i <= friends.length; i++) {
s = s + (friends [i]).getName();
}
return s;
}
}
public class Main {
public static void main(String[] args) {
Friend [] noFriends = new Friend [0];
Friend [] laurasFriends = new Friend [1];
Friend jack = new Friend ("jack","liverpool",1,noFriends);
laurasFriends [0] = jack;
Friend laura = new Friend ("laura","everton",2,laurasFriends);
String s = toStringFriends(laurasFriends);
System.out.println(s);
System.out.println(toStringFriends(laura.getFriends()));
}
}
You need to call it from laura's instance of the object:
String s = laura.toStringFriends(laurasFriends);
toStringFriends has access to friends and name so I would pass a separator if I passed anything. I would also prefer a StringBuilder to using String concatenation in a loop. And I don't see a getName() in your class, but Friend has access to its' own data. Then <= is going to cause an array index out of bounds exception, because valid array indices are 0 to friends.length - 1. So you can do something like,
public String toStringFriends(String sep) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < friends.length; i++) {
if (i != 0) {
sb.append(sep);
}
sb.append(friends[i].name);
}
return sb.toString();
}
And then change this.friends [0] = friends [0]; to this.friends = friends; in your constructor, and finally call toStringFriends like
String s = laura.toStringFriends(",");
Use it by creating Friend class object-
Friend friend=new Friend(<constructor input
parameters>);
friend.toStringFriends(<function input
parameters>);
Or make toStringFriends() static and call it by using Friend class.
You can't call an instance or non static method without object. You should use some instance object and then you can call via . operator.
Like object.method()
Either make toStringFriends() static or instantiate a Friend object and call that method on it.
I have this class that serves as a container which I will use the instance variable for processing later
class Data{
static int counter= 0;
boolean boolean1;
String string1;
public Data() {
counter++;
}
}
And I have this method that sets the values of Data
public Data setData()
{
Data data = null;
for (int i = 0; i < somecoutnerhere; i++) {
Data = new Data();
Data.boolean1 = some boolean put here;
Data.string1 = "some string to be put here";
}
return ProcessData(Data);
}
I also have this class ProcessData that will make use of Data and will construct the response
private class ProcessData
{
private final Map<String, List<?>> map = new HashMap<String, List<?>>();
int counter;
public ProcessData(Data data)
{
map.put("boolean1", data.boolean1);
map.put("String1", data.string1);
counter = data.counter;
}
public String someMethodToGenerateReturnData(){
// some code here to make use of the Data collected. Will basically use map to construct the return String
}
}
My problem is that I couldn't figure out how can I return all the instance variables created on the for-loop for Data on setData(). Any thoughts?
My problem is that I couldn't figure out how can I return all the instance variables created on the for-loop for Data on setData(). Any thoughts?
According to this your problem is not "returning all instance one variables in one call", as your title states, but rather a question about how returning all Data-Objects created in your for-loop, which is easier.
Your code is erronous though, so I went ahead & corrected it (I hope I didn't mess up). I also renamed a few things.
The changes I made are:
renamed "boolean1" and "string1" to "trueOrFalse" and "string"
added a public, fully parameterized constructor to the Data-class
added a ProcessData-list to the setData()-method, which is filled in the for-loop
(+ a comment)
However, I'd strongly recommend you to check your architecture, and also to learn a bit about naming conventions, or coding conventions in general. Names should point out the purpose or content of the method/variable/class, and "boolean1" isn't really doing that.
Regarding the architecture: The Data-class seems to exist solely for the counter, and you could easily change that, making the Data-class obsolete (unless it's used somewhere else).
Data class:
class Data {
static int counter = 0;
boolean trueOrFalse;
String string;
public Data() {
counter++;
}
public Data(boolean someBoolean, String someString) {
this.trueOrFalse= someBoolean;
this.string = someString;
counter++;
}
}
setData()-Method:
public List<ProcessData> setData() {
List<ProcessData> processedDataList = new ArrayList<ProcessData>();
for (int i = 0; i < someCounterHere; i++) {
processedDataList.add(new ProcessData(new Data(true, "testString"));
// a new Data-object is created (parameters true and "testString")
// a new ProcessData-object is created (parameter is the newly created Data-Object)
// the newly created ProcessData-object is added to the list
}
return processedDataList;
}
ProcessData-class:
private class ProcessData {
private final Map<String, List<?>> map = new HashMap<String, List<?>>();
int counter;
public ProcessData(Data data) {
map.put("trueOrFalse", data.trueOrFalse);
map.put("string", data.string);
counter = data.counter;
}
public String someMethodToGenerateReturnData() {
// some code here to make use of the Data collected. Will basically use map to construct the return String
}
}
I am having issues with objects and classes.
I had to define two classes:
Course: a course has a code, an name and a number of credits
Teacher: a teacher has a first name and last name. He can be asked his full name.
So far so good, I got no issue with them, but I have to do next assignment which I was trying to do in the last 2 days and I could not find a proper answer:
Extend the code of the class teacher. A teacher also has a list of courses he can teach. Add an array of Courses to the code. Also add a function addCourse(Course aCourse) to the code. Courses can also be removed from teachers.
I could do everyting in my way but no clue on how to create the addCourse(Course aCourse) method.
Find below my coding, but it must be according to the method described:
public class Course {
private String courseCode;
private String courseName;
private String numberOfCredits;
public Course(String courseCode, String courseName, String numberOfCredits) {
super();
this.courseCode = courseCode;
this.courseName = courseName;
this.numberOfCredits = numberOfCredits;
}
public void print() {
System.out.println(courseCode + "\t" + courseName + "\t" + numberOfCredits);
}
public static void main(String[] args) {
Course[] courseArray = new Course[4];
System.out.println("Code" + "\t" + "Name" + "\t" + "Credits");
courseArray[0] = new Course("001", "Hist", "3");
courseArray[1] = new Course("002", "Phy", "3");
courseArray[2] = new Course("003", "Math", "3");
courseArray[3] = new Course("004", "Log", "3");
for (int i = 0; i < courseArray.length; i++) {
courseArray[i].print();
}
}
}
Arrays are fixed length collections of objects, so you'll need to decide how big your array should be. Let's call the length of your array MAX_COURSES. A more advanced solution might resize the array when required, but I get the impression this is beyond the scope of your course.
So you need to define the Course[] array as a field of your Teacher class. The syntax of array declarations is quite easy to research, so I won't put that in here. Just make sure your array length is equal to MAX_COURSES.
Now, to add courses to the array, you need to know where to put them. To keep track of the next free position of the array, the easiest thing to do is to declare a field in your class:
private int numCourses = 0;
Now, when you add a new course, insert the course into the index specified by numCourses. Make sure you increment numCourses after you've added the course.
Finally, you ought to test to see if your array is full before you agree to insert a new course into the array, i.e. check if numCourses is smaller than MAX_COURSES. If it's not, you need to throw an exception.
I would recommend using a collection (such as a List) rather than an array. The code would look something like:
public class Teacher {
private final String firstName;
private final String lastName;
private final List<Course> courses = new ArrayList<Course>();
public Teacher(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public void addCourse(Course course) {
courses.add(course);
}
}
Based on that example, you should be able to add the removeCourse method yourself, and any other method you need to operate on the list of courses.
If you want to return the list as an array, you could always convert it, e.g:
public Course[] getCourses() {
return courses.toArray(new Course[courses.size()]);
}
If you really need to use an array for the data structure based on your assignment, something you can try when adding and removing courses, is to construct a list from the array of courses, add or remove a course from that list, the convert the list back to an array of courses.
There's really 3 options here.
Option 1
If you're allowed to use List constructs:
private List<Course> courses = new ArrayList<Course>();
public void addCourse(Course aCourse)
{
if (aCourse == null)
{
return;
}
courses.add(aCourse);
}
Option 2
The uses arrays, but it doesn't scale. Assume that a teacher can only have a maximum of X courses, in my example 10:
// Yes, I stole Duncan's variable names
private final int MAX_COURSES = 10;
private int numCourses = 0;
private Course[] courses = new Course[MAX_COURSES];
public void addCourse(Course aCourse) {
if (aCourse == null)
{
return;
}
if (numCourses >= courses.length)
{
return;
}
courses[numCourses] = aCourse;
numCourses++;
}
Option 3
This is identical to the previous item, but is a bit smarter in that it can resize the array... by creating a new one using the static method Arrays.copyOf
// Yes, I stole Duncan's variable names
private final int MAX_COURSES = 10;
private int numCourses = 0;
private Course[] courses = new Course[MAX_COURSES];
public void addCourse(Course aCourse) {
if (aCourse == null)
{
return;
}
if (numCourses >= courses.length)
{
int size = courses.length * 2;
courses = Arrays.copyOf(courses, size);
}
courses[numCourses] = aCourse;
numCourses++;
}
I have a set of data that look like this.
1:2:3:4:5
6:7:8:9:10
I have manage to use array list to store the information using a delimiter of ":".
However i would like to store the information of their line numbers together in the array list.
class test
{
String items;
String linenumber;
}
Example:
test(1,1)
test(2,1)
test(6,2)
test(7,2)
Here is my current code.
Scanner fileScanner = new Scanner(new File(fname));
fileScanner.useDelimiter("\n");
int counter = 0; String scounter;
String test;
String events;
while(fileScanner.hasNext())
{
events = fileScanner.next();
scounter = Integer.toString(counter);
Base obj = new Base(scounter, events);
baseArrayList.add(obj);
}
fileScanner.close();
I have try using delimiter "\n" and then trying to split out the string and it is not very successful.
Any advice would be appreciated.
public void Base_Seperator()
{
String temp, temp2;
String[] split;
String days, events;
for(int i = 0; i < baseArrayList.size(); i++)
{
temp = baseArrayList.get(i).events;
temp2 = baseArrayList.get(i).days;
split = temp.split(":");
}
}
Despite the code in #Alex's answer that may solve your problem, your attempt is almost close to get what you want/need. Now you only need to create Test instances and store them in a container, usually a List. I'll add the necessary code to start this from your code:
//it is better to return the List instead of declaring it as a static field
public List<Test> Base_Seperator() {
//try to declare variables in the narrower scope
//String temp, temp2;
//String[] split;
//String days, events;
//this variable must be recognized in all the paths of this method
List<Test> testList = new ArrayList<Test>();
for(int i = 0; i < baseArrayList.size(); i++) {
//these variables should only work within the for statement
String temp = baseArrayList.get(i).events;
String temp2 = baseArrayList.get(i).days;
String[] split = temp.split(":");
//you have splitted the String by :
//now you have every element between : as an item stored in split array
//go through each one and create a new Test instance
//first, let's create the lineNumber variable as String
String lineNumber = Integer.toString(i+1);
//using enhanced for to go through these elements
for (String value : split) {
//now, let's create Test instance
Test test = new Test(value, lineNumber);
//store the instance in testList
testList.add(test);
}
}
//now just return the list with the desired values
return testList;
}
Not part of your question, but some advices:
There are plenty other ways to write code to achieve the same solution (take #Alex's answer as an example). I didn't posted any of them because looks like you're in learning phase, so it will be better for you to first achieve what you're looking for with your own effort (and a little of help).
Not sure if you're doing it (or not) but you should not use raw types. This is, you should always provide a generic type when the class/interface needs it. For example, it is better to define a variable as ArrayList<MyClass> myClassList rather than ArrayList myClass so the class become parameterized and the compiler can help you to avoid problems at runtime.
It is better to always program oriented to interfaces/abstract classes. This means, it is better to declare the variables as an interface or abstract class rather than the specific class implementation. This is the case for ArrayList and List:
List<String> stringList = new ArrayList<String>();
//above is better than
ArrayList<String> stringList2 = new ArrayList<String>();
In case you need to use a different implementation of the interface/abstract class, you will have to change the object initialization only (hopefully).
More info:
What is a raw type and why shouldn't we use it?
What does it mean to "program to an interface"?
Looks like you want to store days instead of lineNumber in your Test instances:
//comment this line
//Test test = new Test(value, lineNumber);
//use this one instead
Test test = new Test(value, days);
First of all you don't need to keep line number info in the test object because it can be inferred from the ArrayList that holds them. If you must though, it should be changed to an int. So,
class test
{
ArrayList items<Integer>;
int linenumber;
public test(int line, String[] input){
items=new ArrayList();
linenumber=line;
//populate with the line read by the Scanner
for(int i=0; i<input.lenth; i++)
items.add(Integer.parseInt(input[i]));
}
}
I use an ArrayList inside test because you don't know how many elements you'll be handling. Moving on to the scanner
Scanner fileScanner = new Scanner(new File(fname));
// fileScanner.useDelimiter("\n"); You don't need this!
String tmp[];
int line=0; //number of lines
while(fileScanner.hasNext()) {
line++;
//this returns the entire line, that's why you don't need useDelimeter()
//it also splits it on '.' I'm not sure if that needs to be escaped but
//just to be sure
tmp=fileScanner.nextLine() . split(Pattern.quote("."));
baseArrayList.add(new test(line, tmp));
}
fileScanner.close();
Here I use test to store the objects you read, I'm not sure what Base is supposed to be.
A Java Bean/construct is required that will hold the day and the item together. The following code will read the text file. Each line will be converted to a List where finally the application will populate the List DayItems collection properly.
public class DayItem {
private int day;
private String item;
public int getDay() {
return day;
}
public void setDay(final int day) {
this.day = day;
}
public String getItem() {
return item;
}
public void setItem(final String item) {
this.item = item;
}
}
And main code
public class ReadFile {
private static final List<DayItem> dayItems = new ArrayList<DayItem>();
public static void main(String args[]) throws FileNotFoundException{
final BufferedReader bufferReader = new BufferedReader(new FileReader("items.txt"));
int lineNumber=0;
try
{
String currentLine;
while ((currentLine = bufferReader.readLine()) != null) {
lineNumber++;
List<String> todaysItems = Arrays.asList(currentLine.split(":"));
addItems(todaysItems,lineNumber);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void addItems(final List<String> todaysItems,final int day){
int listSize = todaysItems.size();
for(int i=0;i<listSize;i++){
String item = todaysItems.get(i);
DayItem dayItem = new DayItem();
dayItem.setDay(day);
dayItem.setItem(item);
dayItems.add(dayItem);
}
}
}
Is there a way to serialize java collections in Hadoop?
The Writable interface is for Java primitives only. I have following class attributes.
private String keywords;
private List<Status> tweets;
private long queryTime = 0;
public TweetStatus(String keys, List<Status> tweets, long queryTime){
this.keywords = keys;
this.tweets = tweets;
this.queryTime = queryTime;
}
How I can serialize List object?
The Writable interface is for Java primitives only.
Right. Basically you need to break down your object into a sequence of objects that you can serialize.
So, from first principles, to serialize a list you need to serialize the size of the list and then serialize each element of the list. This way, when you need to deserialize, you know how many elements you need to deserialize.
Something like this should get you on the write (pun!) track:
class TweetStatusWritable implements Writable {
private String keywords;
private List<Status> tweets;
private long queryTime;
// add getters for the above three fields
public void readFields(DataInput in) {
this.keywords = in.readUTF();
int size = in.readInt();
this.tweets = new List<Status>();
for(int i = 0; i < size; i++) {
Status status = // deserialize an instance of Status
tweets.add(status);
}
this.queryTime = in.readLong();
}
public void write(DataOutput out) {
out.writeUTF(this.keywords);
out.writeInt(this.tweets.size());
for(int i = 0; i < this.tweets.size(); i++) {
// serialize tweets[i] onto out
}
out.writeLong(queryTime);
}
public TweetStatusWritable(
String keywords,
List<Status> tweets,
long queryTime
) {
this.keywords = keywords;
this.tweets = tweets;
this.queryTime = queryTime;
}
}
Take a look at ArrayWritable. It lets you serialize an array of instances (all of the same type). You could build one of those from your List
If you have a lot of serialization stuff ahead, you might find Avro useful.