As Java developers, we inevitably create all sorts of whimsical bugs in the process of writing code, some of which are quite frustrating, such as null pointer exceptions, exceptions caused by deletion operations in ArrayList iterations, array subscript out-of-bounds exceptions, and so on.
If you accidentally see a colleague’s code with these bugs I described, then you dump this article on him! You dumped him an article, and let him pay attention to a wave of cxuan, you will reap the benefits of his eyes in the back like a treasure and full of worship God’s eyes.
The following is to get to the point.
Error 1: Converting Array to ArrayList
Array to ArrayList and still have errors? What is this stupid .Wait, don’t be in a hurry, let’s see what’s going on first.
If you want to convert an array to an ArrayList, our general approach will be like this:
List<String> list = Arrays.asList(arr);
Arrays.asList() will return an ArrayList, which is a private static class in Arrays, it is not a java.util.
As shown in the following image:
The ArrayList inside Arrays only has methods like set, get, contains, etc., but there are no methods like add that can change its internal structure, so the size of the ArrayList inside Arrays is fixed.
If you want to create an ArrayList that can add elements, you can use the following creation method.
ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(arr));
Since the constructor of an ArrayList can receive a collection, this creation method is possible.
Error 2: Checking whether an array contains a value
Checking whether an array contains a value, as some programmers often do.
Set<String> set = new HashSet<String>(Arrays.asList(arr));
return set.contains(targetValue);
This code is correct, but there is additional performance loss. Normally, instead of converting it to a set, just do this.
return Arrays.asList(arr).contains(targetValue);
Or use something like the following (exhaustive method, circular judgment)
for(String s: arr){
if(s.equals(targetValue))
return true;
}
return false;
The first code above is more readable than the second one.
Error 3: Deleting elements in a loop in a list
I’m sure many of you know this mistake. Deleting elements in a loop is a taboo, and for a while I liked to see if the rest of the team made this mistake when I was reviewing the code.
After all, why can’t we do that (remove elements from a collection)? Look at the following code
ArrayList<String> list = new ArrayList<String>(Arrays.asList(“a”, “b”, “c”, “d”));
for (int i = 0; i < list.size(); i++) {
list.remove(i);
}
System.out.println(list);
Can you imagine this output result? Are you excited to try it out?
The answer is actually [b,d]
Why are there only two values? Isn’t this a looped output?
In fact, inside the list, when you use external remove, once you remove an element, the internal structure of the list will change, the total capacity of the set is 4 at the beginning, after removing an element will become 3, and then compare with i to determine 。。。。。。 So only two elements can be output.
You probably know that using an iterator is the right way to remove elements, and you probably know that for-each and iterator work in a similar way, so you wrote 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);
}
Then you confidently run xxx.main() method, the result ConcurrentModificationException
Why? That’s because using an external remove element in an ArrayList causes changes to its internal structure and cursors.
There are also instructions in the Arri development spec not to remove/add elements within a for-each loop.
So if you want to add or delete elements using List, you must use an iterator for deletion. That is:
ArrayList<String> list = new ArrayList<String>(Arrays.asList(“a”, “b”, “c”, “d”));
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
String s = iter.next();
if (s.equals(“a”)) {
iter.remove();
}
}
.next() must be called before .remove(). In a foreach loop, the compiler will call .next() after the operation to remove the element, resulting in a ConcurrentModificationException.
Error 4: Hashtable and HashMap
This is an algorithmic statute: according to algorithmic conventions, Hashtable is the name of the data structure, but in Java, the name of the data structure is HashMap. One of the main differences between Hashtable and HashMap is that Hashtable is synchronous, so many times you don’t need Hashtable, but use HashMap.
Error 5: Using a collection of primitive types
This is a generic constraint.
In Java, primitive types and unbounded wildcard types can easily be mixed together. Take Set for example, where Set is a primitive type and Set<? > is an unbounded wildcard type.
For example, the following code uses the primitive type List as an argument.
public static void add(List list, Object o){
list.add(o);
}
public static void main(String[] args){
List<String> list = new ArrayList<String>();
add(list, 10);
String s = list.get(0);
}
This code throws a java.lang.ClassCastException, why?
Using primitive type collections is dangerous because primitive types skip generic checks and are unsafe, Set, Set<? > and Set<Object> are vastly different, and generics can easily cause type erasure in use.
As we all know, Java generics are pseudo-generics because all generic information is erased during the compilation of Java, and the first prerequisite to understand the concept of generics is to understand type erasure. The compiler will remove it when it is compiled, and this process becomes type erasure.
If you define List<Object> and List<String> in the code, they will become List after compilation, and the JVM will only see the List, but the type information attached by the generic type will not be visible to the JVM. Type erasure is also an important difference between Java’s generics and the C++ template implementation.
For example, the following example
public class Test {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<String>();
list1.add(“abc”);
ArrayList<Integer> list2 = new ArrayList<Integer>();
list2.add(123);
System.out.println(list1.getClass() == list2.getClass());
}
}
In this example, we define two ArrayList arrays, but one is of the generic type ArrayList<String>, which can only store strings, and one is of the generic type ArrayList<Integer>, which can only store integers, and finally, we get their class information through the getClass() method of the list1 object and list2 object. This means that the generic types String and Integer have been erased, leaving only the original types.
So, the code at the top, adding 10 to Object type is perfectly fine, however, converting “10” of Object type to String type will throw a type conversion exception.
Bug 6: Access Level Problem
I believe that most developers design classes or member variables by simply and brutally declaring public xxx, which is a bad design, and declaring it public makes it easy to be naked, which is dangerous for classes or member variables.
Mistake #7: ArrayList and LinkedList
Hahaha, ArrayList is the most frequently used tool class I’ve seen programmers use, bar none.
When developers don’t know the difference between ArrayList and LinkedList, they often use ArrayList (in fact, even if they knew the difference, they wouldn’t use LinkedList because the performance is not worth mentioning) because it looks like ArrayList is more familiar 。。。。。。
But in reality, there is a huge performance difference between ArrayList and LinkedList, in short, if there are a lot of add/delete operations and not a lot of random access operations, then LinkedList should be preferred. if there are a lot of access operations, then ArrayList is preferred, but ArrayList is not suitable for a lot of add/delete operations.
Error 8: Mutable and immutable
Immutable objects have many advantages, such as simplicity and safety. However, immutable objects require a separate object for each different value, and the objects are not reusable, which can lead to costly garbage collection if there are too many of these objects. There needs to be a balance when choosing between mutable and immutable.
In general, mutable objects are used to avoid creating too many intermediate objects. For example, you want to concatenate a large number of strings. If you use a non-variable string, you will generate a lot of objects that can be garbage collected immediately. This wastes CPU time and effort, and using mutable objects is the right solution (e.g. StringBuilder). The following code shows this.
String result=””;
for(String s: arr){
result = result + s;
}
Therefore, the correct choice between mutable and immutable objects needs to be made carefully.
Error 9: Constructor
First look at the code and analyze why it does not compile.
This compilation error occurs because the default Super constructor is not defined. In Java, if a class does not define a constructor, the compiler will insert a default no-argument constructor for the class by default. If a constructor is defined in the Super class, in this case Super(String s), the compiler will not insert a default no-argument constructor. This is the case of the Super class above.
To solve this problem, just add a constructor with no parameters to Super.
public Super(){
System.out.println(“Super”);
}
Error 10: Whether to use “” or constructor
Consider the following code.
String x = “abc”;
String y = new String(“abc”);
What is the difference between these two pieces of code?
Maybe the following code will give you the answer
String a = “abcd”;
String b = “abcd”;
System.out.println(a == b); // True
System.out.println(a.equals(b)); // True
String c = new String(“abcd”);
String d = new String(“abcd”);
System.out.println(c == d); // False
System.out.println(c.equals(d)); // True
This is a typical memory allocation problem.
Today I gave you a summary of the 10 common mistakes in Java development, although relatively simple, but it is easy to ignore the problem, the details of perfection, see if you will make again.