Other useful techniques
We'll now take a look at some useful programming techniques and classes.
StringBuilder
Let's look at the following program.
String numbers = "";
for (int i = 1; i < 5; i++) {
numbers = numbers + i;
}
System.out.println(numbers);
1234
The program structure is straightforward. A string containing the number 1234 is created, and the string is then outputted.
The program works, but there is a small problem invisible to the user. Calling numbers + i
creates a new string. Let's inspect the program line-by-line with the repetition block unpacked.
String numbers = ""; // creating a new string: ""
int i = 1;
numbers = numbers + i; // creating a new string: "1"
i++;
numbers = numbers + i; // creating a new string: "12"
i++;
numbers = numbers + i; // creating a new string: "123"
i++;
numbers = numbers + i; // creating a new string: "1234"
i++;
System.out.println(numbers); // printing the string
In the previous example, five strings are created in total.
Let's look at the same program where a new line is added after each number.
String numbers = "";
for (int i = 1; i < 5; i++) {
numbers = numbers + i + "\n";
}
System.out.println(numbers);
1 2 3 4
Each +
-operation forms a new string. On the line numbers + i + "\n";
a string is first created, after which another string is created joining a new line onto the previous string. Let's write this out as well.
String numbers = ""; // creating a new string: ""
int i = 1;
// first creating the string "1" and then the string "1\n"
numbers = numbers + i + "\n";
i++;
// first creating the string "1\n2" and then the string "1\n2\n"
numbers = numbers + i + "\n"
i++;
// first creating the string "1\n2\n3" and then the string "1\n2\n3\n"
numbers = numbers + i + "\n"
i++;
// and so on
numbers = numbers + i + "\n"
i++;
System.out.println(numbers); // outputting the string
In the previous example, a total of nine strings is created.
String creation - although unnoticeable at a small scale - is not a quick operation. Space is allocated in memory for each string where the string is then placed. If the string is only needed as part of creating a larger string, performance should be improved.
Java's ready-made StringBuilder class provides a way to concatenate strings without the need to create them. A new StringBuilder object is created with a new StringBuilder()
call, and content is added to the object using the overloaded append
method, i.e., there are variations of it for different types of variables. Finally, the StringBuilder object provides a string using the toString
method.
In the example below, only one string is created.
StringBuilder numbers = new StringBuilder();
for (int i = 1; i < 5; i++) {
numbers.append(i);
}
System.out.println(numbers.toString());
Using StringBuilder is more efficient than creating strings with the +
operator.
Log in to view the quiz
Log in to view the quiz
Regular Expressions
A regular expression defines a set of strings in a compact form. Regular expressions are used, among other things, to verify the correctness of strings. We can assess whether or not a string is in the desired form by using a regular expression that defines the strings considered correct.
Let's look at a problem where we need to check if a student number entered by the user is in the correct format. A student number should begin with "01" followed by 7 digits between 0–9.
You could verify the format of the student number, for instance, by going through the character string representing the student number using the charAt
method. Another way would be to check that the first character is "0" and call the Integer.valueOf
method to convert the string to a number. You could then check that the number returned by the Integer.valueOf
method is less than 20000000.
Checking correctness with the help of regular expressions is done by first defining a suitable regular expression. We can then use the matches
method of the String
class, which checks whether the string matches the regular expression given as a parameter. For the student number, the appropriate regular expression is "01[0-9]{7}"
, and checking the student number entered by a user is done as follows:
System.out.print("Provide a student number: ");
String number = scanner.nextLine();
if (number.matches("01[0-9]{7}")) {
System.out.println("Correct format.");
} else {
System.out.println("Incorrect format.");
}
Let's go through the most common characters used in regular expressions.
Alternation (Vertical Line)
A vertical line indicates that parts of a regular expressions are optional. For example, 00|111|0000
defines the strings 00
, 111
and 0000
. The respond
method returns true
if the string matches any one of the specified group of alternatives.
String string = "00";
if (string.matches("00|111|0000")) {
System.out.println("The string contained one of the three alternatives");
} else {
System.out.println("The string contained none of the alternatives");
}
The string contained one of the three alternatives
The regular expression 00|111|0000
demands that the string is exactly it specifies it to be - there is no "contains" functionality.
String string = "1111";
if (string.matches("00|111|0000")) {
System.out.println("The string contained one of the three alternatives");
} else {
System.out.println("The string contained none of the three alternatives");
}
The string contained none of the three alternatives
Affecting Part of a String (Parentheses)
You can use parentheses to determine which part of a regular expression is affected by the rules inside the parentheses. Say we want to allow the strings 00000
and 00001
. We can do that by placing a vertical bar in between them this way 00000|00001
. Parentheses allow us to limit the option to a specific part of the string. The expression 0000(0|1)
specifies the strings 00000
and 00001
.
Similarly, the regular expression car(|s|)
defines the singular (car) and plural (cars) forms of the word car.
Quantifiers
What is often desired is that a particular sub-string is repeated in a string. The following expressions are available in regular expressions:
-
The quantifier
*
repeats 0 ... times, for example;
String string = "trolololololo";
if (string.matches("trolo(lo)*")) {
System.out.println("Correct form.");
} else {
System.out.println("Incorrect form.");
}
Correct form.
-
The quantifier
+
repeats 1 ... times, for example;
String string = "trolololololo";
if (string.matches("tro(lo)+")) {
System.out.println("Correct form.");
} else {
System.out.println("Incorrect form.");
}
Correct form.
String string = "nananananananana Batmaan!";
if (string.matches("(na)+ Batmaan!")) {
System.out.println("Correct form.");
} else {
System.out.println("Incorrect form.");
}
Correct form.
-
The quantifier
?
repeats 0 or 1 times, for example:
String string = "You have to accidentally the whole meme";
if (string.matches("You have to accidentally (delete )?the whole meme")) {
System.out.println("Correct form.");
} else {
System.out.println("Incorrect form.");
}
Correct form.
-
The quantifier
{a}
repeatsa
times, for example:
String string = "1010";
if (string.matches("(10){2}")) {
System.out.println("Correct form.");
} else {
System.out.println("Incorrect form.");
}
Correct form.
-
The quantifier
{a,b}
repeatsa
...b
times, for example:
String string = "1";
if (string.matches("1{2,4}")) {
System.out.println("Correct form.");
} else {
System.out.println("Incorrect form.");
}
Incorrect form.
-
The quantifier
{a,}
repeatsa
... times, for example:
String string = "11111";
if (string.matches("1{2,}")) {
System.out.println("Correct form.");
} else {
System.out.println("Incorrect form.");
}
Correct form.
You can use more than one quantifier in a single regular expression. For example, the regular expression 5{3}(1|0)*5{3}
defines strings that begin and end with three fives. An unlimited number of ones and zeros are allowed in between.
Character Classes (Square Brackets)
A character class can be used to specify a set of characters in a compact way. Characters are enclosed in square brackets, and a range is indicated with a dash. For example, [145]
means (1|4|5)
and [2-36-9]
means (2|3|6|7|8|9)
. Similarly, the entry [a-c]*
defines a regular expression that requires the string to contain only a
, b
and c
.
Log in to view the quiz
Almost all programming languages support regular expressions nowadays. The theory of regular expressions is one of the topics considered in the course Computational Models (TKT-20005). You can find more regular expressions by googling regular expressions java, for instance.
Enumerated Type - Enum
If we know the possible values of a variable in advance, we can use a class of type enum
, i.e., enumerated type to represent the values. Enumerated types are their own type in addition to being normal classes and interfaces. An enumerated type is defined by the keyword enum
. For example, the following Suit
enum class defines four constant values: DIAMOND
, SPADE
, CLUB
and HEART
.
public enum Suit {
DIAMOND, SPADE, CLUB, HEART
}
In its simplest form, enum
lists the constant values it declares, separated by a comma. Enum types, i.e., constants, are conventionally written with capital letters.
An Enum is (usually) written in its own file, much like a class or interface. In NetBeans, you can create an Enum by selecting new/other/java/java enum from project.
The following is a Card
class where the suit is represented by an enum:
public class Card {
private int value;
private Suit suit;
public Card(int value, Suit suit) {
this.value = value;
this.suit = suit;
}
@Override
public String toString() {
return suit + " " + value;
}
public Suit getSuit() {
return suit;
}
public int getValue() {
return value;
}
}
The card is used in the following way:
Card first = new Card(10, Suit.HEART);
System.out.println(first);
if (first.getSuit() == Suit.SPADE) {
System.out.println("is a spade");
} else {
System.out.println("is not a spade");
}
The output:
HEARTS 10 is not a spade
enum
data type at http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html .Object References In Enums
Enumerated types may contain object reference variables. The values of the reference variables should be set in an internal constructor of the class defining the enumerated type, i.e., within a constructor having a private
access modifier. Enum type classes cannot have a public
constructor.
Color
that contains the constants RED, GREEN and BLUE. The constants have been declared with object reference variables referring to their color codes:public enum Color {
// constructor parameters are defined as
// the constants are enumerated
RED("#FF0000"),
GREEN("#00FF00"),
BLUE("#0000FF");
private String code; // object reference variable
private Color(String code) { // constructor
this.code = code;
}
public String getCode() {
return this.code;
}
}
The enum Color
can be used like so:
System.out.println(Color.GREEN.getCode());
Iterator
Let's look at the following Hand
class that represents the set of cards that a player is holding:
public class Hand {
private List<Card> cards;
public Hand() {
this.cards = new ArrayList<>();
}
public void add(Card card) {
this.cards.add(card);
}
public void print() {
this.cards.stream().forEach(card -> {
System.out.println(card);
});
}
}
The print
method of the class prints each card in the current hand.
ArrayList and other "object containers" that implement the Collection interface implement the Iterable interface, and they can also be iterated over with the help of an iterator - an object specifically designed to go through a particular type of object collection. The following is a version of printing the cards that uses an iterator:
public void print() {
Iterator<Card> iterator = cards.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
The iterator is requested from the cards
list containing cards. The iterator can be thought of as a "finger" that always points to a particular object inside the list. Initially it points to the first item, then to the next, and so on... until all the objects have been gone through with the help of the "finger".
The iterator offers a few methods. The hasNext()
method is used to ask if there are any objects still to be iterated over. If there are, the next object in line can be requested from the iterator using the next()
method. This method returns the next object in line to be processed and moves the iterator, or "finger", to point to the following object in the collection.
The object reference returned by the iterator's next method can of course also be stored in a variable. As such, the print
method could also be written in the following way.
public void print(){
Iterator<Card> iterator = cards.iterator();
while (iterator.hasNext()) {
Card nextInLine = iterator.next();
System.out.println(nextInLine);
}
}
Let's now consider a use case for an iterator. We'll first approach the issue problematically to provide motivation for the coming solution. We attempt to create a method that removes cards from a given stream with a value lower than the given value.
public class Hand {
// ...
public void removeWorst(int value) {
this.cards.stream().forEach(card -> {
if (card.getValue() < value) {
cards.remove(card);
}
});
}
}
Executing the method results in an error.
Exception in thread "main" java.util.ConcurrentModificationException at ... Java Result: 1
The reason for this error lies in the fact that when a list is iterated over using the forEach method, it's assumed that the list is not modified during the traversal. Modifying the list (in this case deleting elements) causes an error - we can think of the forEach method as getting "confused" here.
If you want to remove some of the objects from the list during a traversal, you can do so using an iterator. Calling the remove
method of the iterator object neatly removes form the list the item returned by the iterator with the previous next
call. Here's a working example of the version of the method:
public class Hand {
// ...
public void removeWorst(int value) {
Iterator<Card> iterator = cards.iterator();
while (iterator.hasNext()) {
if (iterator.next().getValue() < value) {
// removing from the list the element returned by the previous next-method call
iterator.remove();
}
}
}
}
Remember to check your points from the ball on the bottom-right corner of the material!