Simple Java
Simple Java
Contents
I
Freface
II
Java Questions
13
16
18
20
23
24
25
28
33
11
35
38
40
42
43
46
46
52
54
58
61
63
64
65
67
72
73
80
81
84
86
86
92
94
96
101
103
105
107
109
111
114
116
117
121
124
126
130
131
137
139
142
148
150
151
153
155
156
58 java.util.ConcurrentModificationException
158
160
160
165
165
167
170
173
66 Java Serialization
175
177
180
Part I.
Freface
7 | 183
The creation of Program Creek is inspired by the saying that Every developer
should have a blog." The word creek" is picked because of the beautiful scenes of
Arkansas which is a central state of America where I studied and worked 3 years. The
blog has been used as my notes to track what I have done and my learning experience. Unexpectedly, millions of people have visited Program Creek since I wrote the
first post 5 years ago.
The large amount of traffic indicates a more important fact other than that my writing skill is good(which is totally the opposite): Developers like to read simple learning
materials and quick solutions. By analyzing the traffic data of blog posts, I learned
which ways of explaining things are preferred by developers.
Many people believe in no diagram no talk. While visualization is a good way
to understand and remember things, there are other ways to enhance learning experience. One is by comparing different but related concepts. For example, by comparing
ArrayList with LinkedList, one can better understand them and use them properly.
Another way is to look at the frequently asked questions. For example, by reading
Top 10 methods for Java arrays, one can quickly remember some useful methods
and use the methods used by the majority.
There are numerous blogs, books and tutorials available to learn Java. A lot of them
receive large traffic by developers with a large variety of different interests. Program
Creek is just one of them. This collection might be useful for two kinds of people: first,
the regular visitors of Program Creek; second, developers who want to read something
in a more readable format. Repetition is key of learning any programming languages.
Hopefully, this contributes another non-boring repetition for you.
Since this collection is 100% from the blog, there is no good reason to keep two
versions of it. The PDF book is converted automatically from the original blog posts.
Every title in the book is linked back to the original blog. When it is clicked, it opens
the original post in your browser. If you find any problem, please go to the post
and leave your comment there. As it is an automatic conversion, there may be some
format problem. Please leave your comment if you find one. You can also contact me
by email: contact@programcreek.com. Thank you for downloading this PDF!
Chrismas Day 2013
Part II.
Java Questions
11 | 183
2) Using Set:
public static boolean useSet(String[] arr, String targetValue) {
Set<String> set = new HashSet<String>(Arrays.asList(arr));
return set.contains(targetValue);
}
13 | 183
if(a > 0)
return true;
else
return false;
}
Result:
useList: 13
useSet: 72
useLoop: 5
useArraysBinarySearch: 9
Result:
useList: 112
useSet: 2055
useLoop: 99
useArrayBinary: 12
Result:
useList: 1590
useSet: 23819
useLoop: 1526
useArrayBinary: 12
Clearly, using a simple loop method is more efficient than using any collection. A
lot of developers use the first method, but it is inefficient. Pushing the array to another
collection requires spin through all elements to read them in before doing anything
with the collection type.
The array must be sorted, if Arrays.binarySearch() method is used. In this case, the
array is not sorted, therefore, it should not be used.
15 | 183
The reason is that you dont know where in the try block the exception would be
thrown. It is quite possible that the exception is thrown before the object is declared.
This is true for this particular example.
But to have better code readability, you should wrap the embedded try-catch block
as a new method, and then put the method invocation in the finally clause.
public static void main(String[] args) {
File file1 = new File("path1");
File file2 = new File("path2");
try {
FileInputStream fis = new FileInputStream(file1);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
methodThrowException();
}
}
18 | 183
We did create two Sub objects, but why the second one prints out "Super"?
The field in the superclass is hidden. It is NOT overridden, so it can not be accessed
polymorphically.
It prints:
Super
Sub
20 | 183
When inheriting from another class, super() has to be called first in the constructor.
If not, the compiler will insert that call. This is why super constructor is also invoked
when a Sub object is created.
This doesnt create two objects, only one Sub object. The reason to have super
constructor called is that if super class could have private fields which need to be
initialized by its constructor.
After compiler inserts the super constructor, the sub class constructor looks like the
following:
public Sub(){
super();
System.out.println("Sub");
}
This compilation error occurs because the default super constructor is undefined. In
Java, if a class does not define a constructor, compiler will insert a default no-argument
constructor for the class by default. If a constructor is defined in Super class, in this
case Super(String s), compiler will not insert the default no-argument constructor. This
is the situation for the Super class above.
The constructors of the Sub class, either with-argument or no-argument, will call
the no-argument Super constructor. Since compiler tries to insert super() to the 2 constructors in the Sub class, but the Supers default constructor is not defined, compiler
reports the error message.
To fix this problem, simply 1) add a Super() constructor to the Super class like
public Super(){
System.out.println("Super");
}
The Sub constructor explicitly call the super constructor with parameter. The super
constructor is defined, and good to invoke.
Output:
RED
YELLOW
BLUE
23 | 183
}
//define instance method
public void printValue(){
System.out.println(this.value);
}
}
1
2
3
This is ambiguous, and other developers have no idea how value to use. If you have
an enum Color with BLACK, RED, etc. the signature becomes:
public void doSomethingWithColor(Color color);
Code calling this method will be far more readable, and cant provide invalid data.
24 | 183
For class access level, the keyword can be public or no explicit modifier(packageprivate). For member access level, the keyword can be public, protected, packageprivate (no explicit modifier), or private.
The following table summarizes the access level of different modifiers for members.
Access level determines the accessibility of fields and methods. It has 4 levels: public,
protected, package-private (no explicit modifier), or private.
First of all, "Collection" and "Collections" are two different concepts. As you will see
from the hierarchy diagram below, "Collection" is a root interface in the Collection
hierarchy but "Collections" is a class which provide static methods to manipulate on
some Collection types.
25 | 183
System.out.print("\t" + l1 + "\n");
Set<String> s1 = new HashSet<String>(); // or new TreeSet() will order the
elements;
s1.add("Program");
s1.add("Creek");
s1.add("Java");
s1.add("Java");
s1.add("tutorial");
System.out.println("Set Elements");
System.out.print("\t" + s1 + "\n");
Map<String, String> m1 = new HashMap<String, String>(); // or new TreeMap()
will order based on keys
m1.put("Windows", "2000");
m1.put("Windows", "XP");
m1.put("Language", "Java");
m1.put("Website", "programcreek.com");
System.out.println("Map Elements");
System.out.print("\t" + m1);
Output:
ArrayList Elements
[Program, Creek, Java, Java]
LinkedList Elements
[Program, Creek, Java, Java]
Set Elements
[tutorial, Creek, Program, Java]
Map Elements
{Windows=XP, Website=programcreek.com, Language=Java}
28 | 183
The other way is to use SortedMap, which further provides a total ordering on its
keys. Therefore all keys must either implement Comparable or be accepted by the
comparator.
One implementing class of SortedMap is TreeMap. Its constructor can accept a
comparator. The following code shows how to transform a general map to a sorted
map.
SortedMap sortedMap = new TreeMap(new Comparator() {
@Override
public int compare(K k1, K k2) {
return k1.compareTo(k2);
}
});
sortedMap.putAll(map);
We can still use a sorted map for this question, but only if the values are unique too.
Under such condition, you can reverse the key=value pair to value=key. This solution
has very strong limitation therefore is not really recommended by me.
Guava libraries also support different ways of intilizaing a static and immutable
collection. To learn more about the benefits of Guavas immutable collection utilities,
see Immutable Collections Explained in Guava User Guide.
Read more about HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMap.
8.9. For this reason, I will not even tell you how to use
clone() method to copy a map. 8. Create an empty Map
If the map is immutable, use
map = Collections.emptyMap();
THE END
The Java super class java.lang.Object has two very important methods defined:
public boolean equals(Object obj)
public int hashCode()
33 | 183
In this example, a green apple object is stored successfully in a hashMap, but when
the map is asked to retrieve this object, the apple object is not found. The program
above prints null. However, we can be sure that the object is stored in the hashMap by
inspecting in the debugger:
35 | 183
Arrays in Java store one of two things: either primitive values (int, char, ...) or references (a.k.a pointers).
When an object is creating by using "new", memory is allocated on the heap and a
reference is returned. This is also true for arrays, since arrays are objects.
The int[] arr is just the reference to the array of 3 integer. If you create an array with
10 integer, it is the same - an array is allocated and a reference is returned.
With the above declaration, lets invoke m1() and see what happens:
When m1 is invoked, a new frame (Frame-1) is pushed into the stack, and local
variable i is also created in Frame-1.
Then m2 is invoked inside of m1, another new frame (Frame-2) is pushed into
the stack. In m2, an object of class A is created in the heap and reference variable
is put in Frame-2. Now, at this point, the stack and heap looks like the following:
Arrays are treated the same way like objects, so how array locates in memory is
straight-forward.
38 | 183
From the diagram, there are referenced objects and unreferenced objects. Unreferenced objects will be garbage collected, while referenced objects will not be garbage
collected. Unreferenced objects are surely unused, because no other objects refer to it.
However, unused objects are not all unreferenced. Some of them are being referenced!
Thats where the memory leaks come from.
40 | 183
sortedMap.putAll(countMap);
printMap(sortedMap);
There are different ways of sorting HashMap, this way has been voted the most in
stackoverflow.
42 | 183
From the compiler perspective, how is code generated for the correct function calls?
Static overloading is not hard to implement. When processing the declaration of an
overloaded function, a new binding maps it to a different implementation. During
the type checking process, compiler analyzes the parameters real type to determine
which function to use.
Dynamic overloading allows different implementations of a function to be chosen
on the run-time type of an actual parameter. It is a form of dynamic dispatch.
Dynamic dispatch is also used to implement method overriding. The overridden
method are determined by real object type during run-time.
To understand dynamic dispatch, there is a post about object layout in memory.
It prints "ab".
In C++, the code is as follows:
void change(string &x) {
x = "cd";
}
int main(){
string x = "ab";
43 | 183
change(x);
cout << x << endl;
}
it prints "cd".
Because java is pass-by-value, the value of x is the reference to "ab". When the
method change() gets invoked, it creates a new "cd" object, and x now is pointing to
"cd" like the following:
It seems to be a pretty reasonable explanation. They are clear that Java is always
pass-by-value. But what is wrong here?
When we use Java to write something to a file, we can do it in the following two ways.
One uses FileOutputStream, the other uses FileWriter.
Using FileOutputStream:
File fout = new File(file_location_string);
FileOutputStream fos = new FileOutputStream(fout);
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(fos));
out.write("something");
Using FileWriter:
FileWriter fstream = new FileWriter(file_location_string);
BufferedWriter out = new BufferedWriter(fstream);
out.write("something");
Both will work, but what is the difference between FileOutputStream and FileWriter?
There are a lot of discussion on each of those classes, they both are good implements
of file i/o concept that can be found in a general operating systems. However, we dont
care how it is designed, but only how to pick one of them and why pick it that way.
From Java API Specification:
FileOutputStream is meant for writing streams of raw bytes such as image data. For
writing streams of characters, consider using FileWriter.
If you are familiar with design patterns, FileWriter is a typical usage of Decorator
pattern actually. I have use a simple tutorial to demonstrate the Decorator pattern,
since it is very important and very useful for many designs.
One application of FileOutputStream is converting a file to a byte array.
46 | 183
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
}
}
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
Output:
5 3 2 1 4
From the output below, we can clearly wee that HashSet is the fastest one.
HashSet: 2244768
TreeSet: 3549314
LinkedHashSet: 2263320
* The test is not precise, but can reflect the basic idea that TreeSet is much slower
because it is sorted.
This example use FileOutputStream, instead you can use FileWriter or PrintWriter
which is normally good enough for a text file operations.
52 | 183
Use FileWriter:
public static void writeFile2() throws IOException {
FileWriter fw = new FileWriter("out.txt");
for (int i = 0; i < 10; i++) {
fw.write("something");
}
fw.close();
}
Use PrintWriter:
public static void writeFile3() throws IOException {
PrintWriter pw = new PrintWriter(new FileWriter("out.txt"));
for (int i = 0; i < 10; i++) {
pw.write("something");
}
pw.close();
}
Use OutputStreamWriter:
public static void writeFile4() throws IOException {
File fout = new File("out.txt");
FileOutputStream fos = new FileOutputStream(fout);
OutputStreamWriter osw = new OutputStreamWriter(fos);
for (int i = 0; i < 10; i++) {
osw.write("something");
}
osw.close();
}
53 | 183
of any I/O failure. PrintWriter methods do not throws IOException, instead they set
a boolean flag which can be obtained using checkError(). PrintWriter automatically
invokes flush after every byte of data is written. In case of FileWriter, caller has to take
care of invoking flush.
The parameter "String[] args" indicates that an array of strings can be sent to the
program to help with program initialization.
We can see a lot of opcode(e.g. CA, 4C, etc) in the bytecode above, each of them has
a corresponding mnemonic code (e.g., aload_0 in the example below). The opcode is
not readable, but we can use javap to see the mnemonic form of a .class file.
"javap -c" prints out disassembled code for each method in the class. Disassembled
code means the instructions that comprise the Java bytecodes.
javap -classpath . -c HelloWorld
The code above contains two methods: one is the default constructor, which is inferred by the compiler; the other is the main method.
Below each method, there are a sequence of instructions, such as aload_0, invokespecial #1, etc. What each instruction does can be looked up in Java bytecode instruction
listings. For instance, aload_0 loads a reference onto the stack from local variable 0,
getstatic fetches a static field value of a class. Notice the "#2" after getstatic instruction
points to the run-time constant pool. Constant pool is one of the JVM run-time data
areas. This leads us to take a look at the constant pool, which can be done by using
"javap -verbose" command.
In addition, each instruction starts with a number, such as 0, 1, 4, etc. In the .class
file, each method has a corresponding bytecode array. These numbers correspond to
the index of the array where each opcode and its parameters are stored. Each opcode
is 1 byte long and instructions can have 0 or multiple parameters. Thats why these
numbers are not consecutive.
Now we can use "javap -verbose" to take a further look of the class.
javap -classpath . -verbose HelloWorld
const
const
const
const
const
const
const
const
const
const
const
const
#17
#18
#19
#20
#21
#22
#23
#24
#25
#26
#27
#28
=
=
=
=
=
=
=
=
=
=
=
=
{
public HelloWorld();
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 2: 0
From JVM specification: The run-time constant pool serves a function similar to that
of a symbol table for a conventional programming language, although it contains a
wider range of data than a typical symbol table.
The "#1" in the "invokespecial #1" instruction points to #1 constant in the constant
pool. The constant is "Method #6.#15;". From the number, we can get the final constant
recursively.
LineNumberTable provides information to a debugger to indicate which line of Java
source code corresponds to which byte code instruction. For example, line 9 in the Java
source code corresponds to byte code 0 in the main method and line 10 corresponds
to byte code 8.
If you want to know more about bytecode, you can create and compile a more
complicated class to take a look. HelloWorld is really a start point of doing this.
This loading job is done by Java Classloaders. When the JVM is started, three class
loaders are used:
Bootstrap class loader: loads the core Java libraries located in the /jre/lib directory. It is a part of core JVM, and is written in native code.
Extensions class loader: loads the code in the extension directories(e.g., /jar/lib/ext).
System class loader: loads code found on CLASSPATH.
So HelloWorld class is loaded by system class loader. When the main method is
executed, it will trigger loading, linking, and initialization of other dependent classes
if they exist.
Finally, the main() frame is pushed into the JVM stack, and program counter(PC) is
set accordingly. PC then indicates to push println() frame to the JVM stack. When the
main() method completes, it will popped up from the stack and execution is done.
58 | 183
This will create a new char array that represents the new string. The above approach sometimes can make your code faster, because Garbage Collector can collect
the unused large string and keep only the sub string.
In Oracle JDK 7, substring() creates a new char array, not uses the existing one.
Check out the diagram for showing substring() difference between JDK 6 and JDK 7.
The pointed memory location are pointed by both a and b. During run-time, the
61 | 183
The reason is that Java handles aliasing during run-time. During run-time, it knows
that the first element should be a B object, instead of A.
Therefore, it only runs correctly if it is changed to:
B[] b = new B[10];
A[] a = b;
a[0] = new B();
b[0].methodParent();
63 | 183
then the following statement is legal and can pass static type checking:
//legal
((C) new B().me()).beBad();
Compiler does not know its real time, but runtime will throw a cast exception since
B can not be casted to C:
java.lang.ClassCastException: B cannot be cast to C
64 | 183
Use one thread to do addition, one thread to do multiplication, and a main thread
to do the division. Since there is no need to communicate data between threads, so
only need to consider the order of thread execution.
In the main thread, let addition and multiplication join the main thread. The join()
method is used when we want the parent thread waits until the threads which call
join() ends. In this case, we want addition and multiplication complete first and then
do the division.
class Add extends Thread {
int value;
public void run() {
value = 1 + 2;
}
}
class Mul extends Thread {
int value;
public void run() {
value = 1 * 2;
}
}
public class Main{
public static void main(String[] args){
Add t1 = new Add();
Mul t2 = new Mul();
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
double n = ((double)t2.value/t1.value);
System.out.println(n);
}
}
65 | 183
If string is not immutable, changing the string with one reference will lead to the
wrong value for the other references.
In this example, if String is mutable, its value can be changed which would violate
the design of set (set contains unduplicated elements). This example is designed for
simplicity sake, in the real String class there is no value field.
23.4. Security
String is widely used as parameter for many java classes, e.g. network connection,
opening files, etc. Were String not immutable, a connection or file would be changed
and lead to serious security threat. The method thought it was connecting to one
machine, but was not. Mutable strings could cause security problem in Reflection too,
as the parameters are strings.
Here is a code example:
boolean connect(string s){
if (!isSecure(s)) {
throw new SecurityException();
}
//here will cause problem, if s is changed before this by using other
references.
causeProblem(s);
}
67 | 183
As shown in the examples above, they are similar to use. The real difference is their
underlying implementation and their operation complexity.
24.5. Vector
Vector is almost identical to ArrayList, and the difference is that Vector is synchronized. Because of this, it has an overhead than ArrayList. Normally, most Java programmers use ArrayList instead of Vector because they can synchronize explicitly by
themselves.
* add() in the table refers to add(E e), and remove() refers to remove(int index)
ArrayList has O(n) time complexity for arbitrary indices of add/remove, but
O(1) for the operation at the end of the list.
LinkedList has O(n) time complexity for arbitrary indices of add/remove, but
O(1) for operations at end/beginning of the List.
I use the following code to test their performance:
ArrayList<Integer> arrayList = new ArrayList<Integer>();
LinkedList<Integer> linkedList = new LinkedList<Integer>();
// ArrayList add
long startTime = System.nanoTime();
for (int i = 0; i < 100000; i++) {
arrayList.add(i);
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println("ArrayList add: " + duration);
// LinkedList add
startTime = System.nanoTime();
for (int i = 0; i < 100000; i++) {
linkedList.add(i);
}
endTime = System.nanoTime();
duration = endTime - startTime;
System.out.println("LinkedList add: " + duration);
// ArrayList get
startTime = System.nanoTime();
for (int i = 0; i < 10000; i++) {
arrayList.get(i);
}
endTime = System.nanoTime();
// ArrayList remove
startTime = System.nanoTime();
for (int i = 9999; i >=0; i--) {
arrayList.remove(i);
}
endTime = System.nanoTime();
duration = endTime - startTime;
System.out.println("ArrayList remove: " + duration);
// LinkedList remove
startTime = System.nanoTime();
for (int i = 9999; i >=0; i--) {
linkedList.remove(i);
}
endTime = System.nanoTime();
duration = endTime - startTime;
System.out.println("LinkedList remove: " + duration);
72 | 183
System.out.println(a);
}
Map is one of the most important data structures. In this tutorial, I will show you how
to use different maps such as HashMap, TreeMap, HashTable and LinkedHashMap.
73 | 183
26.2. HashMap
If key of the HashMap is self-defined objects, then equals() and hashCode() contract
need to be followed.
class Dog {
String color;
Dog(String c) {
color = c;
}
public String toString(){
return color + " dog";
}
}
10);
15);
5);
20);
//print size
System.out.println(hashMap.size());
//loop HashMap
for (Entry<Dog, Integer> entry : hashMap.entrySet()) {
System.out.println(entry.getKey().toString() + " - " +
entry.getValue());
}
}
}
Output:
4
white dog
black dog
red dog white dog
- 5
- 15
10
- 20
Note here, we add "white dogs" twice by mistake, but the HashMap takes it. This
does not make sense, because now we are confused how many white dogs are really
there.
The Dog class should be defined as follows:
class Dog {
String color;
Dog(String c) {
color = c;
}
public boolean equals(Object o) {
return ((Dog) o).color.equals(this.color);
}
public int hashCode() {
return color.length();
}
public String toString(){
return color + " dog";
}
}
The reason is that HashMap doesnt allow two identical elements. By default, the
hashCode() and equals() methods implemented in Object class are used. The default
hashCode() method gives distinct integers for distinct objects, and the equals() method
only returns true when two references refer to the same object. Check out the hashCode() and equals() contract if this is not obvious to you.
Check out the most frequently used methods for HashMap, such as iteration, print,
etc.
26.3. TreeMap
A TreeMap is sorted by keys. Lets first take a look at the following example to understand the "sorted by keys" idea.
class Dog {
String color;
Dog(String c) {
color = c;
}
public boolean equals(Object o) {
return ((Dog) o).color.equals(this.color);
}
public int hashCode() {
return color.length();
}
public String toString(){
return color + " dog";
}
}
public class TestTreeMap {
public static void main(String[] args) {
Dog d1 = new Dog("red");
Dog d2 = new Dog("black");
Output:
Exception in thread "main" java.lang.ClassCastException: collection.Dog
cannot be cast to java.lang.Comparable
at java.util.TreeMap.put(Unknown Source)
at collection.TestHashMap.main(TestHashMap.java:35)
Since TreeMaps are sorted by keys, the object for key has to be able to compare with
each other, thats why it has to implement Comparable interface. For example, you
use String as key, because String implements Comparable interface.
Lets change the Dog, and make it comparable.
class Dog implements Comparable<Dog>{
String color;
int size;
Dog(String c, int s) {
color = c;
size = s;
}
public String toString(){
return color + " dog";
}
@Override
public int compareTo(Dog o) {
return o.size - this.size;
}
}
public class TestTreeMap {
public static void main(String[] args) {
Dog d1 = new Dog("red", 30);
Dog d2 = new Dog("black", 20);
Output:
red dog - 10
black dog - 15
white dog - 20
- 20
10
- 15
- 5
The reason is that TreeMap now uses compareTo() method to compare keys. Different sizes make different dogs!
26.4. Hashtable
From Java Doc: The HashMap class is roughly equivalent to Hashtable, except that it
is unsynchronized and permits nulls.
26.5. LinkedHashMap
LinkedHashMap is a subclass of HashMap. That means it inherits the features of
HashMap. In addition, the linked list preserves the insertion-order.
Lets replace the HashMap with LinkedHashMap using the same code used for
HashMap.
class Dog {
String color;
Dog(String c) {
color = c;
}
public boolean equals(Object o) {
return ((Dog) o).color.equals(this.color);
}
public int hashCode() {
return color.length();
}
public String toString(){
return color + " dog";
}
}
public class TestHashMap {
public static void main(String[] args) {
Dog
Dog
Dog
Dog
d1
d2
d3
d4
=
=
=
=
new
new
new
new
Dog("red");
Dog("black");
Dog("white");
Dog("white");
Output is:
red dog - 10
black dog - 15
white dog - 20
The difference is that if we use HashMap the output could be the following - the
insertion order is not preserved.
red dog - 10
white dog - 20
black dog - 15
Output:
static initializer called
instance initializer called
constructor called
instance initializer called
constructor called
80 | 183
Therefore, instance initializers and instance variable initializers are pretty much the
same.
81 | 183
int[] intArray = { 1, 2, 3, 4, 5 };
int[] removed = ArrayUtils.removeElement(intArray, 3);//create a new array
System.out.println(Arrays.toString(removed));
84 | 183
It seems fine since Object is a super type of String obviously. However, that will not
work. Compilation will not pass, and give you an error at the line of accept(al);:
The method accept(ArrayList < Object > ) in the type Main is not applicable
for the arguments
(ArrayList < String > )
29.4. Comparisons
Now we know that ArrayList <String >is NOT a subtype of ArrayList <Object >. As a
comparison, you should know that if two generic types have the same parameter, their
inheritance relation is true for the types. For example, ArrayList <String >is subtype
of Collection<String>.
Arrays are different. They know and enforce their element types at runtime. This is
called reification. For example, Object[] objArray is a super type of String[] strArr. If
you try to store a String into an array of integer, you will get an ArrayStoreException
during run-time.
Many people believe in "no diagram no talk". While visualization is a good way
to understand and remember things, there are also other ways to enhance learning
experience. One is by comparing different but related concepts. For example, by
comparing ArrayList with LinkedList, one can better understand them and use them
properly. Another way is to look at the frequently asked questions. For example,
by reading "Top 10 methods for Java arrays", one can quickly remember some useful
methods and use the methods used by the majority.
There are numerous blogs, books and tutorials available to learn Java. A lot of them
receive large traffic from developers with a wide variety of interests. Program Creek is
just one of them. This collection might be useful for two kinds of developers: first, the
regular visitors of Program Creek; second, developers who want to read something in
a more readable format. Repetition is key of learning any programming languages.
Hopefully, this contributes another non-boring repetition for you.
Since this collection is 100
86 | 183
Arrays.asList() will return an ArrayList which is a private static class inside Arrays,
it is not the java.util.ArrayList class. The java.util.Arrays.ArrayList class has set(), get(),
contains() methods, but does not have any methods for adding elements, so its size is
fixed. To create a real ArrayList, you should do:
ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(arr));
The constructor of ArrayList can accept a Collection type, which is also a super type
for java.util.Arrays.ArrayList.
The code works, but there is no need to convert a list to set first. Converting a list to
a set requires extra time. It can as simple as:
Arrays.asList(arr).contains(targetValue);
or
for(String s: arr){
if(s.equals(targetValue))
return true;
}
return false;
There is a serious problem in this method. When an element is removed, the size
of the list shrinks and the index changes. So if you want to delete multiple elements
inside a loop by using the index, that will not work properly.
You may know that using iterator is the right way to delete elements inside loops,
and you know foreach loop in Java works like an iterator, but actually it is not. Consider the following code:
ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c",
"d"));
for (String s : list) {
if (s.equals("a"))
list.remove(s);
}
.next() must be called before .remove(). In the foreach loop, compiler will make
the .next() called after the operation of removing element, which caused the ConcurrentModificationException. You may want to take a look at the source code of
ArrayList.iterator()().
Using raw type collection is dangerous as the raw type collections skip the generic
type checking and not safe. There are huge differences between Set, Set<?>, and
Set<Object>. Check out Raw type vs. Unbounded wildcard and Type Erasure.
number of add/remove operations and there are not a lot of random access operations.
Check out ArrayList vs. LinkedList to get more information about their performance
if this is new to you.
Immutable objects have many advantages such simplicity, safety, etc. But it requires a
separate object for each distinct value, and too many objects might cause high cost of
garbage collection. There should be a balance when choosing between mutable and
immutable.
In general, mutable objects are used to avoid producing too many intermediate objects. One classic example is concatenating a large number of strings. If you use an
immutable string, you would produce a lot of objects that are eligible for garbage collection immediately. This wastes time and energy on the CPU, using a mutable object
the right solution (e.g. StringBuilder).
String result="";
for(String s: arr){
result = result + s;
}
There are other situations when mutable objects are desirable. For example passing mutable objects into methods lets you collect multiple results without jumping
through too many syntactic hoops. Another example is sorting and filtering: of course,
you could make a method that takes the original collection, and returns a sorted one,
but that would become extremely wasteful for larger collections. (From dasblinkenlights answer on Stack Overflow)
Why String is Immutable??
This compilation error occurs because the default super constructor is undefined. In
Java, if a class does not define a constructor, compiler will insert a default no-argument
constructor for the class by default. If a constructor is defined in Super class, in this
case Super(String s), compiler will not insert the default no-argument constructor. This
is the situation for the Super class above.
The constructors of the Sub class, either with-argument or no-argument, will call
the no-argument Super constructor. Since compiler tries to insert super() to the 2 constructors in the Sub class, but the Supers default constructor is not defined, compiler
reports the error message.
To fix this problem, simply 1) add a Super() constructor to the Super class like
public Super(){
System.out.println("Super");
}
String x = "abc";
//2. use constructor
String y = new String("abc");
For more details about how they are allocated in memory, check out Create Java
String Using or Constructor??.
This post explains a general interview question that has been asked by Google and
a lot of companies. Its low-level and not about how to design concurrent program.
92 | 183
First of all, the answer is NO. The method is not thread-safe, because the counter++
operation is not atomic, which means it consists more than one atomic operations. In
this case, one is accessing value and the other is increasing the value by one.
When Thread 1 accesses the method at t1, Thread 2 may not be done with the
method. So the value returned to Thread 1 is the value that has not been increased.
If the method is not static, then adding synchronized keyword willsynchronize the
instance of the class, not the Class object.
94 | 183
belong to the global scope, which means there are many other entities that are Entries and are not necessary Maps entries. This indicates that Entry represents entries
related to the Map.
Because an interface can not be instantiated, the inner interface only makes sense if
it is static. Therefore, by default inter interface is static, no matter you manually add
static or not.
MapImpl.java
public class MapImpl implements Map {
96 | 183
the new array will take O(n) time. Also adding or removing an element needs to move
existing elements in an array. This might be the most disadvantage to use ArrayList.
LinkedList is a double linked list. Therefore, to access an element in the middle, it
has to search from the beginning of the list. On the other hand, adding and removing
an element in LinkedList is quicklier, because it only changes the list locally.
In summary, the worst case of time complexity comparison is as follows:
| Arraylist | LinkedList
-----------------------------------------get(index)
|
O(1) | O(n)
add(E)
|
O(n) | O(1)
add(E, index) | O(n) | O(n)
remove(index) | O(n) | O(n)
Iterator.remove() | O(n) | O(1)
Iterator.add(E) | O(n) | O(1)
Despite the running time, memory usage should be considered too especially for
large lists. In LinkedList, every node needs at least two extra pointers to link the
previous and next nodes; while in ArrayList, only an array of elements is needed.
More comparisons between list.
In JDK, there is no short-cut. Note that you can not use List.toArray(), because that
will convert List to Integer[]. The correct way is following,
int[] array = new int[list.size()];
for(int i=0; i < list.size(); i++) {
array[i] = list.get(i);
}
Of course you can mimic the way of what Guava and Apache did, by introducing a
new interface Predicate. That might also be what most advanced developers will do.
public interface Predicate<T> {
boolean test(T o);
}
public static <T> void filter(Collection<T> collection, Predicate<T>
predicate) {
if ((collection != null) && (predicate != null)) {
Iterator<T> itr = collection.iterator();
while(itr.hasNext()) {
T obj = itr.next();
if (!predicate.test(obj)) {
itr.remove();
}
}
}
}
If you DO care about the ordering, order can be preserved by putting a list into a
LinkedHashSet which is in the standard JDK.
34.10. Collections.copy
There are two ways to copy a source list to a destination list. One way is to use
ArrayList constructor
The other is to use Collections.copy() (as below). Note the first line, we allocate a
list at least as long as the source list, because in the javadoc of Collections, it says The
destination list must be at least as long as the source list.
ArrayList<Integer> dstList = new ArrayList<Integer>(srcList.size());
Collections.copy(dstList, srcList);
Both methods are shallow copy. So what is the difference between these two methods?
First, Collections.copy() wont reallocate the capacity of dstList even if dstList
does not have enough space to contain all srcList elements. Instead, it will throw
an IndexOutOfBoundsException. One may question if there is any benefit of it.
One reason is that it guarantees the method runs in linear time. Also it makes
suitable when you would like to reuse arrays rather than allocate new memory
in the constructor of ArrayList.
Collections.copy() can only accept List as both source and destination, while
ArrayList accepts Collection as the parameter, therefore more general.
101 | 183
Since Set<?>can hold any type of elements, we simply use Object in the loop.
Item 2 means the following situation which is illegal:
//Illegal Code
public static void printSet(Set<?> s) {
s.add(10);//this line is illegal
for (Object o : s) {
System.out.println(o);
}
}
Because we dont know the type of <?>exactly, we can not add any thing to it other
than null. For the same reason, we can not initialize a set with Set<?>. The following
is illegal:
//Illegal Code
Set<?> set = new HashSet<?>();
because raw type has no restrictions. However, this will easily corrupt the invariant
of collection.
In brief, wildcard type is safe and the raw type is not. We can not put any element
into a Set<?>.
First, lets take a look at the Java Doc for the constructor method of ArrayList.
103 | 183
ArrayList(Collection <? extends E >c) : Constructs a list containing the elements of the
specified collection, in the order they are returned by the collections iterator.
So what the constructor does is the following: 1. Convert the collection c to an array
2. Copy the array to ArrayLists own back array called "elementData"
Here is the source code of Contructor of ArrayList.
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
It is not the best, because the size of the list returned from asList() is fixed. Actually the list returned is not java.util.ArrayList, but a private static class defined inside
java.util.Arrays. We know ArrayList is essentially implemented as an array, and the
list returned from asList() is a fixed-size list backed by the original array. In this way,
if add or remove elements from the returned list, an UnsupportedOperationException
will be thrown.
list.add(new Element(4));
Method 2:
private static void readFile2(File fin) throws IOException {
// Construct BufferedReader from FileReader
BufferedReader br = new BufferedReader(new FileReader(fin));
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
}
105 | 183
106 | 183
Reading the class hierarchy diagram is also very helpful for understanding those inputstream and reader related concept: http://www.programcreek.com/2012/05/javaio-class-hierarchy-diagram/.
Code:
class Apple {
public String color="red";
}
public class Main {
public static void main(String[] args) {
Apple apple = new Apple();
System.out.println(apple.color);
changeApple(apple);
System.out.println(apple.color);
}
public static void changeApple(Apple apple){
apple.color = "green";
}
}
Since the orignal and copied reference refer the same object, the member value gets
changed.
Output:
red
green
If a customer wants to occupy the special room, he has to enter the Hallway(Entry
Set) to wait first. Scheduler will pick one based on some criteria(e.g. FIFO). If he is
suspended for some reason, he will be sent to the wait room, and be scheduled to
reenter the special room later. As it is shown in the diagram above, there are 3 rooms
in this building.
109 | 183
In brief, a monitor is a facility which monitors the threads access to the special
room. It ensures that only one thread can access the protected data or code.
Those methods can only be invoked within a synchronized statement or synchronized method. The reason is that if a method does not require mutual exclusion, there
is no need to monitor or collaborate between threads, every thread can access that
method freely.
Here are some synchronization code examples.
Output:
bc
111 | 183
However, this diagram is not exactly right or it represents what really happens in
the heap. What really happens when substring() is called is different between JDK 6
and JDK 7.
The following code is simplified and only contains the key point for explain this
problem.
//JDK 6
String(int offset, int count, char value[]) {
this.value = value;
this.offset = offset;
this.count = count;
}
public String substring(int beginIndex, int endIndex) {
//check boundary
return new String(offset + beginIndex, endIndex - beginIndex, value);
}
If you have a VERY long string, but you only need a small part each time by using
substring(). This will cause a performance problem, since you need only a small part,
you keep the whole thing. For JDK 6, the solution is using the following, which will
make it point to a real sub string:
x = x.substring(x, y) + ""
This is improved in JDK 7. In JDK 7, the substring() method actually create a new
array in the heap.
//JDK 7
public String(char value[], int offset, int count) {
//check boundary
this.value = Arrays.copyOfRange(value, offset, offset + count);
}
public String substring(int beginIndex, int endIndex) {
//check boundary
int subLen = endIndex - beginIndex;
return new String(value, beginIndex, subLen);
}
114 | 183
throw exception;
}
}
This is the reason that only one parent class in the catch clause is syntactically safe.
void run() {
A().run();
Thread(new A(), "name_thread2").run();
Thread(new A(), "name_thread3").start();
}
public class Main {
public static void main(String[] args) {
new Thread(new B(), "name_thread1").start();
}
}
116 | 183
43.1. Comparable
Comparable is implemented by a class in order to be able to comparing object of
itself with some other objects. The class itself must implement the interface in order
to be able to compare its instance(s). The method required for implementation is
compareTo(). Here is an example:
class HDTV implements Comparable<HDTV> {
private int size;
private String brand;
public HDTV(int size, String brand) {
this.size = size;
this.brand = brand;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
@Override
public int compareTo(HDTV tv) {
Sony is better.
43.2. Comparator
In some situations, you may not want to change a class and make it comparable. In
such cases, Comparator can be used if you want to compare objects based on certain
attributes/fields. For example, 2 persons can be compared based on height or age
etc. (this can not be done using comparable.)
The method required to implement is compare(). Now lets use another way to compare those TV by size. One common use of Comparator is sorting. Both Collections
and Arrays classes provide a sort method which use a Comparator.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
class HDTV {
private int size;
private String brand;
public HDTV(int size, String brand) {
this.size = size;
this.brand = brand;
}
Output:
Panasonic
Samsung
Sony
Output:
[3,1,2]
[1,2,3]
[3,2,1]
@Override
public int compare(Dog d1, Dog d2) {
return d1.size - d2.size;
}
}
public class ImpComparable {
public static void main(String[] args) {
TreeSet<Dog> d = new TreeSet<Dog>(new SizeComparator()); // pass
comparator
d.add(new Dog(1));
d.add(new Dog(2));
d.add(new Dog(1));
}
}
121 | 183
Overriding and Overloading are two very important concepts in Java. They are confusing for Java novice programmers. This post illustrates their differences by using
two simple examples.
44.1. Definitions
Overloading occurs when two or more methods in one class have the same method
name but different parameters.
Overriding means having two methods with the same method name and parameters
(i.e., method signature). One of the methods is in the parent class and the other is in
the child class. Overriding allows a child class to provide a specific implementation of
a method that is already provided its parent class.
class Dog{
public void bark(){
System.out.println("woof ");
}
}
class Hound extends Dog{
public void sniff(){
System.out.println("sniff ");
}
public void bark(){
System.out.println("bowl");
}
}
public class OverridingTest{
public static void main(String [] args){
Dog dog = new Hound();
dog.bark();
}
}
Output:
bowl
In the example above, the dog variable is declared to be a Dog. During compile
time, the compiler checks if the Dog class has the bark() method. As long as the Dog
class has the bark() method, the code compilers. At run-time, a Hound is created and
assigned to dog. The JVM knows that dog is referring to the object of Hound, so it
calls the bark() method of Hound. This is called Dynamic Polymorphism.
123 | 183
In this overloading example, the two bark method can be invoked by using different
parameters. Compiler know they are different because they have different method
signature (method name and method parameter list).
Integer i = (Integer)room.get();
System.out.println(i);
}
}
The program runs totally fine when we add an integer and cast it. But if a user
accidentally add a string "60" to it, compiler does not know it is a problem. When the
program is run, it will get a ClassCastException.
Exception in thread "main" java.lang.ClassCastException: java.lang.String
cannot be cast to java.lang.Integer
at collection.Main.main(Main.java:21)
You may wonder why not just declare the field type to be Integer instead of Object.
If so, then the room is not so much useful because it can only store one type of thing.
Now if someone adds room.add("60"), a compile-time error will be shown like the
following:
We can easily see how this works. In addition, there is no need to cast the result any
more from room.get() since compile knows get() will return an Integer.
45.4. Summary
To sum up, the reasons to use Generics are as follows:
Stronger type checking at compile time.
Elimination of explicit cast.
Enabling better code reusability such as implementation of generic algorithms
Java Generic type is only a compile-time concept. During run-time, all types information is erased, and this is call Type Erasure. Here is an interesting example to show
how to avoid the common mistakes of Type Erasure.
126 | 183
}
}
class DogSizeComparator implements Comparator<Dog>{
@Override
public int compare(Dog o1, Dog o2) {
return o1.size - o2.size;
}
}
public class ArraySort {
public static void main(String[] args) {
Dog d1 = new Dog(2);
Dog d2 = new Dog(1);
Dog d3 = new Dog(3);
Dog[] dogArray = {d1, d2, d3};
printDogs(dogArray);
Arrays.sort(dogArray, new DogSizeComparator());
printDogs(dogArray);
}
public static void printDogs(Dog[] dogs){
for(Dog d: dogs)
System.out.print(d.size + " " );
System.out.println();
}
}
Output:
2 1 3
1 2 3
int size;
int weight;
public Dog(int s, int w){
size = s;
weight = w;
}
}
class DogSizeComparator implements Comparator<Dog>{
@Override
public int compare(Dog o1, Dog o2) {
return o1.size - o2.size;
}
}
class DogWeightComparator implements Comparator<Dog>{
@Override
public int compare(Dog o1, Dog o2) {
return o1.weight - o2.weight;
}
}
public class ArraySort {
public static void main(String[] args) {
Dog d1 = new Dog(2, 50);
Dog d2 = new Dog(1, 30);
Dog d3 = new Dog(3, 40);
Dog[] dogArray = {d1, d2, d3};
printDogs(dogArray);
Arrays.sort(dogArray, new DogSizeComparator());
printDogs(dogArray);
Arrays.sort(dogArray, new DogWeightComparator());
printDogs(dogArray);
}
public static void printDogs(Dog[] dogs){
for(Dog d: dogs)
System.out.print("size="+d.size + " weight=" + d.weight + " ");
System.out.println();
}
}
Comparator is just an interface. Any Comparator that implements this interface can
be used during run-time. This is the key idea of Strategy design pattern.
46.4. Summary
To summarize, the takeaway messages from Arrays.sort():
generic - super
strategy pattern
merge sort - nlog(n) time complexity
Java.util.Collections#sort(List <T >list, Comparator <? super T >c) has similar
idea with Arrays.sort.
130 | 183
131 | 183
In each loop, you check if the key exists or not. If it does, increment the old value by
1, if not, set it to 1. This approach is simple and straightforward, but it is not the most
efficient approach. This method is considered less efficient for the following reasons:
containsKey(), get() are called twice when a key already exists. That means
searching the map twice.
Since Integer is immutable, each loop will create a new one for increment the old
value
this.val = val;
}
public int get() {
return val;
}
public void set(int val) {
this.val = val;
}
//used to print value convinently
public String toString(){
return Integer.toString(val);
}
}
This seems better because it does not require creating many Integer objects any
longer. However, the search is still twice in each loop if a key exists.
}
}
The difference is significant - 223 vs. 117 vs. 96. There is huge difference between
Naive and Better, which indicates that creating objects are expensive!
String s = "one two three two three three";
String[] sArr = s.split(" ");
long startTime = 0;
long endTime = 0;
long duration = 0;
// naive approach
startTime = System.nanoTime();
HashMap<String, Integer> counter = new HashMap<String, Integer>();
for (int i = 0; i < 1000000; i++)
for (String a : sArr) {
if (counter.containsKey(a)) {
int oldValue = counter.get(a);
counter.put(a, oldValue + 1);
} else {
counter.put(a, 1);
}
}
endTime = System.nanoTime();
duration = endTime - startTime;
System.out.println("Naive Approach : " + duration);
// better approach
startTime = System.nanoTime();
HashMap<String, MutableInteger> newCounter = new HashMap<String,
MutableInteger>();
for (int i = 0; i < 1000000; i++)
for (String a : sArr) {
if (newCounter.containsKey(a)) {
When you use a counter, you probably also need a function to sort the map by value.
You can check out the frequently used method of HashMap.
48.6. Conclusion
137 | 183
This approach is very useful, if you would do a lot of search operations for the
collection. The sorted data structure will give time complexity of O(logn), which is
lower than O(n).
50.1. Core
Apache Commons Lang - Apaches library that provides a host of helper utilities
for the java.lang API, such as String manipulation, object creation, etc.
Google Guava - Googles Core library for collections, caching, primitives support, etc. (example)
139 | 183
50.9. JSON
Jackson - a multi-purpose Java library for processing JSON data format. Jackson aims to be the best possible combination of fast, correct, lightweight, and
ergonomic for developers.
XStream - a simple library to serialize objects to XML and back again.
Google Gson - a Java library that can be used to convert Java Objects into their
JSON representation. (example)
JSON-lib - a java library for transforming beans, maps, collections, java arrays
and XML to JSON and back again to beans and DynaBeans.
50.10. . Math
Apache Commons Math - provide functions for math and statistics.
50.11. . Logging
Apache Log4j - most popular logging library. (example)
Logback - a successor to the popular log4j project.
50.12. . Office-Complicant
Apache POI - APIs for manipulating various file formats based upon Microsofts
OLE 2 Compound Document format using pure Java.
Docx4j - a Java library for creating and manipulating Microsoft Open XML (Word
docx, Powerpoint pptx, and Excel xlsx) files.
More from comments
50.14. . Database
Hibernate / EclipseLink / JPA
JDO
jOOQ
SpringJDBC / Spring Data
Apache DbUtils
51.1. Inheritance
Lets suppose we have an Insect class. This class contains two methods: 1) move() and
2) attack().
class Insect {
private int size;
private String color;
public Insect(int size, String color) {
this.size = size;
this.color = color;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public String getColor() {
return color;
}
public void setColor(String color) {
142 | 183
this.color = color;
}
public void move() {
System.out.println("Move");
}
public void attack() {
move(); //assuming an insect needs to move before attacking
System.out.println("Attack");
}
}
Now you want to define a Bee class, which is a type of Insect, but have different
implementations of attack() and move(). This can be done by using an inheritance
design like the following:
class Bee extends Insect {
public Bee(int size, String color) {
super(size, color);
}
public void move() {
System.out.println("Fly");
}
public void attack() {
move();
super.attack();
}
}
Output:
Fly
Fly
Attack
"Fly" was printed twice, which indicates move() is called twice. But it should be
called only ONCE.
The problem is caused by the super.attack() method. The attack() method of Insect
invokes move() method. When the subclass calls super.attack(), it also invokes the
overridden move() method.
To fix the problem, we can:
eliminate the subclasss attack() method. This will make the subclass depends on
the superclasss implementation of attack(). If the attack() method in the superclass is changed later (which is out of your control), e.g., the superclasss attack()
method use another method to move, the subclass will need to be changed too.
This is bad encapsulation.
rewrite the attack() method like the following: public void attack() move(); System.out.println("Attack"); This would guarantee the correct result, because the
subclass is not dependent on the superclass any more. However, the code is the
duplicate of the superclass. (Image attack() method does complex things other
than just printing a string) This does not following software engineering rule of
reusing.
This inheritance design is bad, because the subclass depends on the implementation
details of its superclass. If the superclass changes, the subclass may break.
51.2. Composition
Instead of inheritance, composition can be used in this case. Lets first take a look at
the composition solution.
The attack function is abstracted as an interface.
interface Attack {
public void move();
public void attack();
}
Since the attack function is extracted, Insect does not do anything related with attack
any longer.
class Insect {
private int size;
private String color;
public Insect(int size, String color) {
this.size = size;
this.color = color;
}
fly
move
fly
sting
148 | 183
Output:
Tree Set Example!
Tree set data: 12 34 45 63
Tree Set size: 4
First data: 12
Last data: 63
Data is removed from tree set
Now the tree set contain: 12 34 63
Now the size of tree set: 3
150 | 183
Method Area: it stores run-time constant pool, field and method data, and methods
and constructors code.
Runtime Constant Pool: It is a per-class or per-interface run-time representation of
the constant_pool table in a class file. It contains several kinds of constants, ranging
from numeric literals known at compile-time to method and field references that must
be resolved at run-time.
Stack contains Frames, and a frame is pushed to the stack when a method is invoked.
A frame contains local variable array, Operand Stack, Reference to Constant Pool.
For more information, please go to the offical JVM specification site.
s stores the reference of the string object. The arrow below should be interpreted as
"store reference of".
151 | 183
s2 stores the same reference value, since it is the same string object.
54.4. Summary
Once a string is created in memory(heap), it can not be changed. We should note that
all methods of String do not change the string itself, but rather return a new String.
If we need a string that can be modified, we will need StringBuffer or StringBuilder.
Otherwise, there would be a lot of time wasted for Garbage Collection, since each time
a new String is created. Here is an example of StringBuilder usage.
What is the difference between using double quotes and using constructor?
153 | 183
a==b is true because a and b are referring to the same string literal in the method
area. The memory references are the same.
When the same string literal is created more than once, only one copy of each distinct
string value is stored. This is called "string interning". All compile-time constant
strings in Java are automatically interned.
Example 2:
String c = new String("abcd");
String d = new String("abcd");
System.out.println(c == d); // False
System.out.println(c.equals(d)); // True
c==d is false because c and d refer to two different objects in the heap. Different
objects always have different memory references.
154 | 183
If you do need to create a new object in the heap, constructor should be used. Here
is a use case.
First of all, null is not a valid object instance, so there is no memory allocated for it.
It is simply a value that indicates that the object reference is not currently referring to
an object.
From JVM Specifications:
The Java Virtual Machine specification does not mandate a concrete value encoding null.
I would assume it is all zeros of something similar like itis on other C like languages.
Now we know what null is. And we know a variable is a storage location and an
associated symbolic name (an identifier) which contains some value. Where exactly x
is in memory?
From the diagram of JVM run-time data areas, we know that since each method has
a private stack frame within the threads steak, the local variable are located on that
frame.
In Java, exception can be checked or unchecked. They both fit into a class hierarchy.
The following diagram shows Java Exception classes hierarchy.
Red colored are checked exceptions. Any checked exceptions that may be thrown in
a method must either be caught or declared in the methods throws clause. Checked
exceptions must be caught at compile time. Checked exceptions are so called because
both the Java compiler and the Java virtual machine check to make sure this rule
is obeyed. Green colored are uncheck exceptions. They are exceptions that are not
expected to be recovered, such as null pointer, divide by 0, etc.
156 | 183
58. java.util.ConcurrentModificationException
This post shows show to solve the problem of java.util.ConcurrentModificationException
for ArrayList.
The error message looks like the following:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
...
...
158 | 183
58.2. Solution 1
Iterator can be used to solve this problem. Iterators allow the caller to remove elements
from the underlying collection during the iteration.
Iterator<String> iter = list.iterator();
while(iter.hasNext()){
String str = iter.next();
if( str.equals("B") )
{
iter.remove();
}
}
58.3. Solution 2
Instead of ArrayList, CopyOnWriteArrayList can be used to solve the problem. CopyOnWriteArrayList is a thread-safe variant of ArrayList in which all mutative operations
(add, set, and so on) are implemented by making a fresh copy of the underlying array.
public static void main(String args[]) {
List<String> list = new CopyOnWriteArrayList<String>();
list.add("A");
list.add("B");
for (String s : list) {
if (s.equals("B")) {
list.remove(s);
}
}
}
The above code is fine, because they do not use array as the underlining data structure.
160 | 183
But if you dont want empty lines, you can use, which is also my favourite way:
String.split("[\\r\\n]+")
A more robust way, which is really system independent, is as follows. But remember,
you will still get empty lines if two newline characters are placed side by side.
String.split(System.getProperty("line.separator"));
3. Importance of Pattern.compile()
A regular expression, specified as a string, must first be compiled into an instance of
Pattern class. Pattern.compile() method is the only way to create a instance of object.
A typical invocation sequence is thus
Pattern p = Pattern.compile("a*b");
Matcher matcher = p.matcher("aaaaab");
assert matcher.matches() == true;
is equivalent to the first code above, though for repeated matches it is less efficient
since it does not allow the compiled pattern to be reused.
4. How to escape text for regular expression?
In general, regular expression uses "to escape constructs, but it is painful to precede
the backslash with another backslash for the Java string to compile. There is another
way for users to pass string Literals to the Pattern, like "$5". Instead of writing
$5 or [$]5, we can type
Pattern.quote("$5");
expression first, then explain it a little bit. For a comprehensive explanation, I would
refer you to read How can we match an bn with Java regex.
Pattern p = Pattern.compile("(?x)(?:a(?= a*(\\1?+b)))+\\1");
// true
System.out.println(p.matcher("aaabbb").matches());
// false
System.out.println(p.matcher("aaaabbb").matches());
// false
System.out.println(p.matcher("aaabbbb").matches());
// false
System.out.println(p.matcher("caaabbb").matches());
Instead of explaining the syntax of this complex regular expression, I would rather
say a little bit how it works.
In the first iteration, it stops at the first a then looks ahead (after skipping some
as by using a*) whether there is a b. This was achieved by using (?:a(?= a*(
1?+b))). If it matches,
1, the self-reference matching, will matches the very inner parenthesed elements,
which is one single b in the first iteration.
In the second iteration, the expression will stop at the second a, then it looks
ahead (again skipping as) to see if there will be b. But this time,
1+b is actually equivalent to bb, therefore two bs have to be matched. If so,
1 will be changed to bb after the second iteration.
In the nth iteration, the expression stops at the nth a and see if there are n bs
ahead.
By this way, the expression can count the number of as and match if the number of
bs followed by a is same.
7. How to replace 2 or more spaces with single space in string and delete leading
spaces only?
String.replaceAll() replaces each substring that matches the given regular expression
with the given replacement. "2 or more spaces" can be expressed by regular expression
[ ]+. Therefore, the following code will work. Note that, the solution wont ultimately
remove all leading and trailing whitespaces. If you would like to have them deleted,
you can use String.trim() in the pipeline.
String line = " aa bbbbb ccc d ";
// " aa bbbbb ccc d "
System.out.println(line.replaceAll("[\\s]+", " "));
// false
System.out.println(prime(1));
// true
System.out.println(prime(2));
// true
System.out.println(prime(3));
// true
System.out.println(prime(5));
// false
System.out.println(prime(8));
// true
System.out.println(prime(13));
// false
System.out.println(prime(14));
// false
System.out.println(prime(15));
}
public static boolean prime(int n) {
return !new String(new char[n]).matches(".?|(..+?)\\1+");
}
The function first generates n number of characters and tries to see if that string
matches .?|(..+?)
1+. If it is prime, the expression will return false and the ! will reverse the result.
The first part .? just tries to make sure 1 is not primer. The magic part is the second
part where backreference is used. (..+?)
1+ first try to matches n length of characters, then repeat it several times by
1+.
By definition, a prime number is a natural number greater than 1 that has no positive
divisors other than 1 and itself. That means if a=n*m then a is not a prime. n*m can be
further explained "repeat n m times", and that is exactly what the regular expression
does: matches n length of characters by using (..+?), then repeat it m times by using
1+. Therefore, if the pattern matches, the number is not prime, otherwise it is. Remind
that ! will reverse the result.
9. How to split a comma-separated string but ignoring commas in quotes?
You have reached the point where regular expressions break down. It is better and
more neat to write a simple splitter, and handles special cases as you wish.
Alternative, you can mimic the operation of finite state machine, by using a switch
statement or if-else. Attached is a snippet of code.
public static void main(String[] args) {
String line = "aaa,bbb,\"c,c\",dd;dd,\"e,e";
List<String> toks = splitComma(line);
for (String t : toks) {
System.out.println("> " + t);
}
}
165 | 183
I asked this question to developers of different levels: entry and intermediate. They
can not answer the question correctly or confidently. While IDE provides convenient
code autocompletion, it also brings the problem of "surface understanding". In this
post, I will explain some key concepts about Java arrays.
The answer:
int[] arr = new int[3];
System.out.println(arr.length);//length for array
String str = "abc";
System.out.println(str.length());//length() for string
The question is why array has the length field but string does not? Or why string
has the length() method while array does not?
An array initializer creates an array and provides initial values for all its components.
It is written as a comma-separated list of expressions, enclosed by braces and .
For example,
int[] arr = {1,2,3};
An array contains all the members inherited from class Object(except clone). Why
there is not a class definition of an array? We can not find an Array.java file. A rough
explanation is that theyre hidden from us. You can think about the question - if there
IS a class Array, what would it look like? It would still need an array to hold the array
data, right? Therefore, it is not a good idea to define such a class.
Actually we can get the class of an array by using the following code:
int[] arr = new int[3];
System.out.println(arr.getClass());
Output:
class [I
"class [I" stands for the run-time type signature for the class object "array with component type int".
167 | 183
A.java
package compiler;
public class A {
public void method(){
System.out.println("inside of A");
}
}
By running the following command, we can get information about each class loaded.
The "-verbose:class" option displays information about each class loaded.
java -verbose:class -classpath /home/ron/workspace/UltimateTest/bin/
compiler.TestLoader
Part of output:
[Loaded sun.misc.JavaSecurityProtectionDomainAccess from
/usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
[Loaded java.security.ProtectionDomain$2 from
/usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
[Loaded java.security.ProtectionDomain$Key from
/usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
[Loaded java.security.Principal from
/usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
[Loaded compiler.TestLoader from
file:/home/xiwang/workspace/UltimateTest/bin/]
test
And run the same command again, the output would be:
[Loaded sun.misc.JavaSecurityProtectionDomainAccess from
/usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
[Loaded java.security.ProtectionDomain$2 from
/usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
[Loaded java.security.ProtectionDomain$Key from
/usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
[Loaded java.security.Principal from
/usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
[Loaded compiler.TestLoader from
file:/home/xiwang/workspace/UltimateTest/bin/]
test
[Loaded compiler.A from file:/home/xiwang/workspace/UltimateTest/bin/]
inside of A
[Loaded java.lang.Shutdown from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
[Loaded java.lang.Shutdown$Lock from
/usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
We can see the difference highlighted in red. A.class is loaded only when it is used.
In summary, a class is loaded:
when the new bytecode is executed. For example, SomeClass f = new SomeClass();
when the bytecodes make a static reference to a class. For example, System.out.
169 | 183
Java Class Instance Initialization is an example that shows the order of execution for
field, static field and constructor.
}
}
}
class ThreadB extends Thread{
int total;
@Override
public void run(){
synchronized(this){
for(int i=0; i<100 ; i++){
total += i;
}
notify();
}
}
}
The result would be 0, 10, etc. Because sum is not finished before it is used.
Consumer(Producer p) {
producer = p;
}
@Override
public void run() {
try {
while (true) {
String message = producer.getMessage();
System.out.println("Got message: " + message);
//sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String args[]) {
Producer producer = new Producer();
producer.start();
new Consumer(producer).start();
}
}
message:
message
message
message
message
message
message:
message:
message:
message:
message:
message
message
message
message
message
message:
message:
message:
Fri
Fri
Fri
Fri
Fri
Dec
Dec
Dec
Dec
Dec
02
02
02
02
02
21:37:21
21:37:21
21:37:21
21:37:21
21:37:21
EST
EST
EST
EST
EST
2011
2011
2011
2011
2011
173 | 183
//try-with-resource statement
try (PrintWriter out2 = new PrintWriter(
new BufferedWriter(
new FileWriter("out.txt", true)))) {
out2.println("the text");
} catch (IOException e) {
e.printStackTrace();
}
65.1. Answer
Because the Writer should be closed in either case (exception or no exception), close()
should be put in finally clause.
From Java 7, we can use try-with-resources statement.
175 | 183
package serialization;
import
import
import
import
import
java.io.FileInputStream;
java.io.FileOutputStream;
java.io.IOException;
java.io.ObjectInputStream;
java.io.ObjectOutputStream;
try {
FileOutputStream fileOut = new FileOutputStream("./dog.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(e);
out.close();
fileOut.close();
System.out.printf("Serialized dog is saved in ./dog.ser");
} catch (IOException i) {
i.printStackTrace();
}
e = null;
//Deserialize
try {
FileInputStream fileIn = new FileInputStream("./dog.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
e = (Dog) in.readObject();
in.close();
fileIn.close();
} catch (IOException i) {
i.printStackTrace();
return;
} catch (ClassNotFoundException c) {
System.out.println("Dog class not found");
c.printStackTrace();
return;
}
System.out.println("\nDeserialized Dog ...");
System.out.println("Name: " + e.getName());
System.out.println("Color: " + e.getColor());
System.out.println("Weight: " + e.getWeight());
e.introduce();
}
}
Output:
Serialized dog is saved in ./dog.ser
Deserialized Dog...
Name: bulldog
Color: white
Weight: 0
I have a white bulldog.
177 | 183
In order to run this program, the computer needs to build up a chain of multiplications: factorial(n) factorial(n-1) factorial(n-2) ... factorial(1). Therefore,
the computer has to keep track of the multiplications to be performed later on. This
type of program, characterized by a chain of operations, is called recursion. Recursion
can be further categorized into linear and tree recursion. When the amount of information needed to keep track of the chain of operations grows linearly with the input,
the recursion is called linear recursion. The computation of n! is such a case, because
the time required grows linearly with n. Another type of recursion, tree recursion,
happens when the amount of information grows exponentially with the input. But we
will leave it undiscussed here and go back shortly afterwards.
67.2. Iteration
A different perspective on computing factorials is by first multiplying 1 by 2, then
multiplying the result by 3, then by 4, and so on until n. More formally, the program
can use a counter that counts from 1 up to n and compute the product simultaneously
until the counter exceeds n. Therefore the program can be written as:
Program 2:
int factorial (int n) {
int product = 1;
for(int i=2; i<n; i++) {
product *= i;
}
return product;
}
and i. This type of program is called iteration, whose state can be summarized by
a fixed number of variables, a fixed rule that describes how the variables should be
updated, and an end test that specifies conditions under which the process should
terminate. Same as recursion, when the time required grows linearly with the input,
we call the iteration linear recursion.
By the definition, Fibonacci numbers have the following sequence, where each number is the sum of the previous two: 0, 1, 1, 2, 3, 5, 8, 13, 21, ...
A recursive program can be immediately written as:
Program 3:
int fib (int n) {
if (n == 0) {
return 0;
} else if (n == 1) {
return 1;
} else {
return fib(n-1) + fib(n-2);
}
}
Therefore, to compute fib(5), the program computes fib(4) and fib(3). To computer
fib(4), it computes fib(3) and fib(2). Notice that the fib procedure calls itself twice at
the last line. Two observations can be obtained from the definition and the program:
The ith Fibonacci number Fib(i) is equal to phi(i)/rootsquare(5) rounded to the
nearest integer, which indicates that Fibonacci numbers grow exponentially.
This is a bad way to compute Fibonacci numbers because it does redundant
computation. Computing the running time of this procedure is beyond the
scope of this article, but one can easily find that in books of algorithms, which is
O(phi(n)). Thus, the program takes an amount of time that grows exponentially
with the input.
On the other hand, we can also write the program in an iterative way for computing
the Fibonacci numbers. Program 4 is a linear iteration. The difference in time required
by Program 3 and 4 is enormous, even for small inputs.
Program 4:
int fib (int n) {
int fib = 0;
int a = 1;
for(int i=0; i<n; i++) {
fib = fib + a;
a = fib;
}
return fib;
}
However, one should not think tree-recursive programs are useless. When we consider programs that operate on hierarchically data structures rather than numbers,
tree-recursion is a natural and powerful tool. It can help us understand and design
programs. Compared with Program 3 and 4, we can easily tell Program 3 is more
straightforward, even if less efficient. After that, we can most likely reformulate the
program into an iterative way.
180 | 183
void go() {
System.out.println("Inner class reference is: " + this);
}
}
}
public class Test {
public static void main(String[] args) {
Outer.Inner n = new Outer.Inner();
n.go();
}
}
Outer x is 100
Inner class reference is Outer$Inner@4dfd9726
Outer class reference is Outer@43ce67ca
x is outer
x is static outer