Due date: Thursday, March 12 @ 11:59PM
This assignment will require you to build StringBuilder class and its corresponding methods from scratch. You will also be expected to test your code extensively by writing your own test cases.
There are a number of ways to get started on development. The following is the recommended way to ensure that your code will compile during grading.
ssh cs8bwi20__@ieng6.ucsd.edu
cp -r ~/../public/pa9 ~/
(this will copy the entire starter files directory to your home directory)ls ~
to verify that you have copied the pa9
directory over.git clone https://github.com/CaoAssignments/cse8b-wi20-pa9-MyStringBuilder-starter.git
cd cse8b-wi20-pa9-MyStringBuilder-starter
mv cse8b-wi20-pa9-MyStringBuilder-starter pa9
vim MyStringBuilder.java
or gvim MyStringBuilder.java
javac
command.
javac file1.java file2.java etc...
javac MyStringBuilder.java
java
command passing in the name of the class with the main method that you want to run.
java nameOfClass
java MyStringBuilder
Strings are implemented differently across different programming languages. In Java, Strings are actual Objects! Furthermore, we have multiple classes which implement sequences of characters in varying ways (char arrays, ArrayLists of Characters, Strings, StringBuilder). These all have similar capabilities with different implementations internally. Here, we now explore one internal implementation by writing a simple string data structure. While it is similar to the StringBuilder class, the internal workings of our StringBuilder will be one we have not seen before. We will call it MyStringBuilder to differentiate it from the default Java implementation.
The MyStringBuilder will be used to construct, store, and modify characters in a specific order. Before we talk about how our MyStringBuilder will be implemented, let’s pretend we were using a char array (we won’t use an actual char array in this assignment). In memory, elements of an array are stored next to each other like this:
Suppose we made a mistake with “CSE” and we meant to spell “CASE”. The char array would have {‘C’, ‘S’, ‘E’}. To fix our mistake, we would have to (1) create a new array, (2) move ‘S’ and ‘E’ back, and then (3) assign the letter ‘A’ to position 1.
Create a new array:
& 3. Copy items and add A:
This is inefficient and slow. If the array we are copying is very long, this could take a very long time! In MyStringBuilder, we will instead use references to link different letters together. CSE will be represented as C → S → E
. Let’s think about how we could insert the letter ‘A’ into our MyStringBuilder now.
Nice! We didn’t have to remake our entire MyStringBuilder and copy all of its contents!
But, char is a primitive type and not an Object-subclassed type. Primitive types don’t have the ability to point to each other. To solve this issue, we will contain the char inside a class whose sole purpose is to hold chars and references to the next char in the sequence. We will call this class a node.
Note: The diagrams in this writeup are not accurate representations, they are just for you to get a better sense of what is happening - we encourage you to draw your own accurate diagrams/memory models to visualize the logic behind the algorithms. For the diagrams,
let start
be the reference to the first CharNode, end
be the reference to the last CharNode,
length
is the length of String, next
is the next reference in CharNode,
data
is the character in CharNode.
This MyStringBuilder has the word “agc”. Notice that the chars are not actually inside MyStringBuilder - MyStringBuilder has a reference i.e. start
which points to the first node, which contains information of a char ‘a’. The first node has a reference next
that points to another node, which contains information of another char ‘g’.
Note that data
and next
are simply instance variables of type CharNode
. We can use these to join all of the CharNodes together.
Upon finishing the implementation of MyStringBuilder, the data structure should be able to store infinitely many characters (theoretically).
Notice that MyStringBuilder has a reference end
that points to the last node.
You might need to iterate through your MyStringBuilder in order to add and delete characters. To “iterate” through a MyStringBuilder, you need a way to keep track of where your current position. This will be done using an additional CharNode reference (i.e. the currNode
reference in the figure above). Notice the two references pointing to the first CharNode.
Use of libraries and other data structures of any kind in this part to store your MyStringBuilder content will result in a zero. For example, you can’t use a Java StringBuilder to make your MyStringBuilder. Don’t use character arrays, Strings, etc. You are allowed to use String's charAt()
, substring()
, and length()
instance methods (you shouldn't need anything else anyway). If you're using CharNodes as specified in the writeup, you're fine. All code must be from scratch and the logic must use only references, object instantiation, and object initialization.
In this PA, you will be required to handle different exceptions using try and catch blocks in your methods. An exception is characterized as anything that disrupts the normal flow of the method. Some examples of different exceptions include NullPointerException, ArithmeticException and IndexOutOfBoundsException. You should enclose the part of your code that might cause the error in your try block. In your catch block, make sure you are catching the appropriate exception. You should then throw this exception using the throw keyword. Let's take a method attempting to divide by 0 as an example. To handle the exception in the method, you would do the following:
public static void divideByZero() throws ArithmeticException { try { System.out.println (39/0); } catch (ArithmeticException e) { System.out.println ("Exception caught!"); throw e; } }
In the starter code, you are provided with four files: MyException.java, MSBOutofBoundsException.java, BadInputException.java and Constants.java. Make sure you read through each of the files before starting the assignment.
This class extends Throwable
, which is the superclass of all errors and exceptions. Any MyException object can be thrown using the throw
keyword, similar to what was shown in the example above.
The constructor takes in two String objects, the first specifying where the MyException object is getting thrown from (which method is causing the error), and the second specifying a message used to describe the error.
This class extends MyException.java. It should be thrown when your MSB object faces an out of bounds error.
This class also extends MyException.java. It should be thrown when the input into the MSB object is of an unintended type (such as a null object).
You will notice that the constructors for these exceptions takes a from
String and a message
String. Use the constants provided
in Constants.java for the from
argument and use whatever will help you debug as the message
argument.
Constant | Method Signature (described in further sections) |
---|---|
FROM_CONSTRUCTOR | MyStringBuilder(String str) [constructor] |
FROM_DEEPCOPY_CONSTRUCTOR | MyStringBuilder(MyStringBuilder param) [constructor] |
FROM_APPEND_STR | append(String) |
FROM_INSERT_CHAR | insert(char, int) |
FROM_INSERT_STR | insert(String, int) |
FROM_FIND_INDEX | findIndex(int) |
FROM_REMOVE | remove(int) |
FROM_DELETE_STARTINDEX | delete(int) |
FROM_DELETE_STARTINDEX_ENDINDEX | delete(int, int) |
FROM_SUBSTRING_STARTINDEX | substring(int) |
FROM_SUBSTRING_STARTINDEX_ENDINDEX | substring(int, int) |
FROM_CONCAT | concat(MyStringBuilder) |
public class CharNode
This class/file should contain:
data
, andnext
.next
reference as null.getData()
, getNext()
, setData()
, setNext()
.
Please make sure that the getters and setters are named as above.The method signatures for getters and setters are as follows:
public char getData()
This should return data
.
public CharNode getNext()
This should return next
.
Setters are usually void return types but here the setters are returning the objects for the purpose of testing.
public CharNode setData(char newData)
This should update the data
to newData and return the CharNode object.
public CharNode setNext(CharNode newNext)
This should update the next
to newNext and return the CharNode object.
public class MyStringBuilder
Our custom MyStringBuilder class will have
protected
instance variables:
the first constructor takes a char input and should create a MyStringBuilder with the single CharNode representing the input.
the second constructor creates a MyStringBuilder from a String. If the String is null, you should throw a BadInputException
. If the String is empty, then the constructed MyStringBuilder object should also be empty (have no CharNodes). Hint: You should use the append()
method of Part C for this.
the third constructor constructs a MyStringBuilder using a MyStringBuilder object by deep copying its contents. If the MyStringBuilder object is null, you should throw a BadInputException
. Since CharNodes store their proceeding nodes inside themselves, you should start from the reference to the first charNode and deep copy all charNodes until we get to the end and construct MyStringBuilder object.
For example, consider the following MyStringBuilder in the diagram: start
points to the charNode with 'r' and end
points to the charNode with 't'. You should
traverse from start
charNode till the end
charNode and deep copy each charNode i.e. deep copying data
and next
.
These are the following methods that you have to implement in MyStringBuilder.java.
public int length()
It is useful to know the data structure’s size. One implementation of this would just have us traverse through each node, counting them. However, this is pretty inefficient to do this every time we want to know the length. Therefore, we have the length
instance variable that will keep track of all changes to the length from other methods. You can therefore directly return the value from that instance variable for this method.
Note: Your other methods must appropriately update the length
variable each time.
Time to start adding functionality to our MyStringBuilder!
public MyStringBuilder append(char c)
This is a helper method used to append a single char to the end of MyStringBuilder. It should be used by the method below, to append an entire string to MyStringBuilder.
start
and end
must point to a new CharNode containing this char.next
reference is always null.
You will make the previously last node point at a newly created CharNode containing c
. Remember to update the instance variables. If you skipped the second MyStringBuilder constructor, you should revisit it now.
A fresh MyStringBuilder object msb
, in a diagram:if we call msb.append('a')
, then:
Afterwards if we call msb.append(‘b’)
, then:
public MyStringBuilder append (String str) throws BadInputException
This method should append an entire string to the end of your current MyStringBuilder object. It should then return your new MyStringBuilder object.
public String toString()
Since the MSB class is a way to manipulate and store Strings, we should have a toString()
method which returns an actual String
object of our stored String. This function accomplishes that goal. Yet, what we currently have is not a char array or a String, but a sequence of nodes of chars. This function takes this sequence of CharNodes and turns the sequence of chars into a String
.
public MyStringBuilder insert (char c, int index) throws MSBOutOfBoundsException
This is a helper method that will be used to insert an entire String at a specified index. The method should work as follows:
The index
is the position in our sequence of nodes where we want to insert a char. Once finished, c
will have position index
. The previous node at position index
will be at position index + 1
. In fact, all nodes whose positions were index
or higher will have their positions incremented. Remember to update the instance variables.
For example, you have a string “rest” and call insert('e', 3)
on this string. Before insert()
was called, the letter ‘t’ was at position 3. Now, ‘e’ is at position 3 while ‘t’ is at position 4, resulting in the string “reset”. Note that the node containing ‘s’ must point to a different node now.
Before insert()
:
After insert()
:
Exception Handling:
index
must be non-negative (0 is not negative) and less than or equal to the length of the sequence. Make sure you handle any invalid input accordingly (by throwing the appropriate exception). (Notice that it is fine if index is equal to the length of the sequence. What does this mean?)public MyStringBuilder insert (String str, int index) throws BadInputException, MSBOutOfBoundsException
This method should be similar to your append()
method. index
should represent the position in MyStringBuilder where the first character of the str
should start. You will need to handle invalid input for both the index
and str
parameter. If str
is null, throw BadInputException
and if index
is out of bounds, throw MSBOutOfBoundsException
.
Hint: You might find using your other insert() method (the one that takes a char) as a useful subroutine.
protected CharNode findIndex(int index) throws MSBOutOfBoundsException
This method should find the character at a given index. You have to iterate through the CharNodes in MyStringBuilder until index
is reached.
index
should be non-negative, and index
should be in the MyStringBuilder (i.e. length
should be greater than index
). Be sure to handle any exceptions accordingly.
index
would be out of bounds.public MyStringBuilder remove (int index) throws MSBOutOfBoundsException
This method should remove the CharNode at a given index. Like in insert()
, index represents the position of the node which we want to remove. After this node is removed, the previous node at position index + 1
should be at position index
. Remember to update the instance variables.
index
should be non-negative and within the length of MyStringBuilder. Make sure you handle any exceptions appropriately.public MyStringBuilder delete (int startIndex) throws MSBOutOfBoundsException
This is a method used to delete a range of characters. startIndex
specifies an index in MyStringBuilder. All nodes starting from and including startIndex
should be removed from MyStringBuilder. Remember to update the instance variables.
After delete, the node at startIndex - 1
should now point to null as its next node.
startIndex
must be nonnegative, and within the bounds of MyStringBuilder. Make sure you handle any exceptions and update instance variables appropriately.Consider the string “abc”.
delete(1)
will delete "bc".delete(2)
will delete ‘c’.delete(0)
will delete the whole sequence.public MyStringBuilder delete (int startIndex, int endIndex) throws BadInputException, MSBOutOfBoundsException
This method deletes a subsequence of characters from MyStringBuilder. (Deleting the entire sequence is also considered deleting a subsequence). The nodes for deletion starts at position startIndex and deletes up to, but not including, the node at position endIndex
.
startIndex == endIndex
, then nothing is deleted.startIndex
must be within the bounds of MyStringBuilder and non-negative, endIndex
must be less than or equal to the length of MyStringBuilder. If these are not true, throw an MSBOutOfBoundsException
. Notice that endIndex
is allowed to be equal to the length of this MyStringBuilder.endIndex
is less than startIndex
throw a BadInputException
.Consider the string “abc”.
delete(1,1)
does nothing.delete(1, 2)
will delete ‘b’ and cause the node containing ‘a’ to point at the node containing ‘c’.delete(0, length())
will delete the whole sequence. When in doubt, draw it out!public String substring(int startIndex) throws MSBOutOfBoundsException
This is a method used to get substring starting from startIndex
. startIndex
specifies an index in MyStringBuilder. All characters of nodes starting from and including startIndex
should be concatenated and returned as string.
startIndex
must be nonnegative, and within the bounds of MyStringBuilder. Make sure you handle any exceptions appropriately.Consider the string “abc”.
substring(1)
will return "bc".substring(2)
will return "c".substring(0)
will return "abc".public String substring(int startIndex, int endIndex) throws MSBOutOfBoundsException, BadInputException
This method concatenates characters from startIndex to endIndex and returns the substring.
All nodes starting from and including startIndex
till endIndex
, but not including the node at position
endIndex
are considered.
startIndex == endIndex
, then empty string is returned.startIndex
must be within the bounds of MyStringBuilder and non-negative, endIndex
must be less than or equal to the length of MyStringBuilder. If these are not true, throw an MSBOutOfBoundsException
. Notice that endIndex
is allowed to be equal to the length of this MyStringBuilder.endIndex
is less than startIndex
throw a BadInputException
.Consider the string “abc”.
substring(1,1)
will return an empty string.substring(1, 2)
will return "b".substring(0, length())
will return "abc"public MyStringBuilder concat(MyStringBuilder rightOperand) throws BadInputException
This method concatenates two MSB objects together. rightOperand
represents the MSB Object that should be the second part of the current MSB object. Remember to update the instance variables.
rightOperand
. Therefore, it will have the same start
, end
and length
variables as rightOperand
.rightOperand
is not a valid input, you need to throw a BadInputException
.We now have a working MyStringBuilder! This allows us to manipulate sequences of characters as we please. Make sure to test out your implementation, and then pat yourself on the back.
Please fill out our student satisfaction survey. We are changing how we approach giving assignments and would like to hear about your experiences. After filling out the survey, please write the following sentence at the top of your README.md
file: I have completed the student satisfaction survey.
Make sure you follow the below guidelines for styling since we will be grading you on it.
We will grade your code style thoroughly. Namely, there are a few things you must have in each file / class / method:
A full style guideline can be found here. If you need any clarifications, feel free to ask on Piazza.
Well done! With that, we have wrapped up the last assignment in Intro to Computer Science: Java II. Congratulations for completing the course assignments and good luck in your future ventures!