In this article, I will introduce some operation methods of iterative processing using the data structure of List
regardless of implementation.
The content of the article is at the level of common sense for those who usually program using Java, so the target audience is beginners to intermediates who have just started learning Java. However, it seems that few people are still using the iterative process using the forEach
method added in JDK 1.8 at the development site, so I would like you to remember how to use the forEach
method at this time. ..
Note that this article deals with simple iterations using the List collection, so we won't touch on the Stream API
, which performs intermediate operations such as filter
. I would like to introduce the Stream API
in another article.
--Iterative processing using List collection
--Random access using the get (int)
method
--Sequential access using extended for statement
--Sequential access using the forEach (Consumer <T>)
method (JDK 1.8 or later)
get
methodSince the introduction of convenient and efficient functions such as the extended for statement
and the forEach
method in JDK 1.8, there is almost no need to use the get
method for the List collection and iterate with random access. I did. However, this random access is the most basic iterative method using the List collection, regardless of implementation, so be sure to remember it if you are programming in Java.
The basic operation method is as follows.
import java.util.List;
import java.util.ArrayList;
public final class TestGet {
public static void main(String[] args) {
final List<String> testList = new ArrayList<>(3);
testList.add("test1");
testList.add("test2");
testList.add("test3");
for (int i = 0, size = testList.size(); i < size; i++) {
//Get the i-th element of the variable (random access)
System.out.println(testList.get(i));
}
}
}
In the sample code above, we first implement a test List that can store three elements as ʻArrayList, and then ʻadd
the test values.
After that, the basic for statement is used, and the process is repeated for the size of testList
prepared earlier. In the above sample code, the variable ʻiis incremented each time it is repeated, so as you can see in the comment, the processing of
testList.get (i)is sequentially performed on the elements of
testList`. It will be randomly accessed.
Therefore, the execution result of the above sample code is output as follows.
test1
test2
test3
--Be sure to clarify the accessible range when performing random access
When using random access using the get
method as in the sample code above, be sure to check the size of the List to be accessed. If you specify an index larger than the size of the target List in the get
method, ʻIndexOutOfBoundsException` will always occur at runtime and the process will fail.
For example, in the following cases.
import java.util.List;
import java.util.ArrayList;
public final class TestIndexOutOfBoundsException {
public static void main(String[] args) {
final List<String> testList = new ArrayList<>();
testList.add("test1");
testList.add("test2");
testList.add("test3");
//Specify a fourth index that does not exist
testList.get(3);
}
}
When I run the sample code above, I get the following exception:
java.lang.IndexOutOfBoundsException: Index 3 out of bounds for length 3
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
at java.base/java.util.Objects.checkIndex(Objects.java:373)
at java.base/java.util.ArrayList.get(ArrayList.java:425)
The size of the List to be randomly accessed can be obtained with the size ()
method as shown in the sample code that ends normally. If you can always get only one record from the database based on a unique key, use the size ()
method without checking the size of the List, such as testList.get (0);
It is allowed to get the target value in the process.
The important point is to clarify the accessible range so that ʻIndexOutOfBoundsException` does not occur at runtime when performing random access.
--Avoid random access to LinkedList
as much as possible
Within the Java List collection, there is a commonly used data structure called LinkedList
, which is similar to ʻArrayList. This
LinkedList` is a mechanism that holds the context of the stored elements with a link, so it becomes a data structure suitable for inserting new elements into the array and deleting the elements in the array.
However, random access to the LinkedList
has the worst performance. If the number of elements to be randomly accessed is about 100, the processing time will be almost the same as when using ʻArrayList, but with
LinkedList`, random access is repeated for 100,000 or more large-scale data. That is not realistic in terms of performance.
Therefore, when handling large-scale data with LinkedList
in iterative processing, use sequential access such as the extended for statement
and forEach
method, which will be introduced later, instead of random access using the get
method. Please go.
I introduced the following sample code for random access earlier.
import java.util.List;
import java.util.ArrayList;
public final class TestGet {
public static void main(String[] args) {
final List<String> testList = new ArrayList<>(3);
testList.add("test1");
testList.add("test2");
testList.add("test3");
for (int i = 0, size = testList.size(); i < size; i++) {
//Get the i-th element of the variable (random access)
System.out.println(testList.get(i));
}
}
}
I thought that the for statement in the above sample code might not be familiar to beginners, so I will leave an explanation. In the sample code above, the for statement can be written as follows.
for (int i = 0; i < testList.size(); i++) {
// do something
}
In the sample code introduced at the beginning, I tried to get the size of testList
by executing the call of thesize ()
method in the initialization expression part in the conditional expression part.
However, you should avoid writing the for statement above. This is because the conditional expression part ʻi <testList.size ()of the for statement is executed every time iterative processing is performed, so in the case of the above code, the
size ()method is called by the size of
testList`. It will be. Around 10 or 100 will have little impact on performance, but even small differences can make a big difference.
For example, you can check that the processing of the conditional expression part is executed every time with the following sample code.
public final class Main {
public static void main(String[] args) throws Exception {
//Make sure the conditional expression part is executed every time
for (int i = 0; i < 10 && saySomething(); i++) {
// do something
}
}
private static boolean saySomething() {
System.out.println("Hello World!");
return true;
}
}
The execution result of the above sample code is output as follows. I wouldn't write code like the one above in my normal development, but I was able to confirm that the saySomething ()
method of the conditional expression part was executed 10 times.
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
From the above, you can write a leaner and more efficient program by finding the number of items to be used in the conditional expression part in the initialization expression part that is executed only once at the start of the for statement. This is a technique that can be applied not only to the size ()
method in the List collection but also to various situations, so I would like people who aim for a better program to keep in mind every day.
extended for statement
It's been a while since the extended for statement
was added as a Java feature, and it's already become the de facto standard for sequential iterative processing of List collections. The for statement is also simpler and more efficient than the one for random access.
Prior to the introduction of the extended for statement
, sequential access was achieved by implementing the ʻIterator interface on its own. Sequential access on the ʻIterator
interface is complicated and often implemented, and it is rarely used in legacy writing styles, so I will not cover it in detail in this article, but as a knowledge, before the appearance of the extended for statement
Keep in mind that ʻIterator` interface was implemented by itself to achieve sequential access.
In addition, the performance degradation when using ʻIndexOutOfBoundsException(out-of-range exception) and
LinkedList introduced during random access using the
getmethod does not occur with sequential access using the
extended for statement`.
Therefore, if you can use the extended for statement
, do not repeat the process with random access, and try to write the iterative process using the extended for statement
.
Now, if you rewrite the sample code of random access using the get
method introduced earlier with sequential access using the extended for statement
, it will be as follows.
import java.util.List;
import java.util.ArrayList;
public final class TestEnhancedForStatement {
public static void main(String[] args) {
final List<String> testList = new ArrayList<>(3);
testList.add("test1");
testList.add("test2");
testList.add("test3");
for (String value : testList) {
//Get the values stored in testList in order from the beginning
System.out.println(value);
}
}
}
The way to read the extended for statement
in the above sample code is as follows:" Get the values stored in testList
as String
type values in order from the beginning and store them in the variable name value
. Become. The grammar is similar in other high-level languages such as Python and Ruby, so if you have already touched other high-level languages, there should be no particular difficulty.
And the output result of executing the above sample code is as follows.
test1
test2
test3
forEach
methodWith the introduction of the functional interface in JDK1.8, the forEach (Consumer <T>)
method for iterating over the List collection has been added. This method is very similar to the forEach (BiConsumer <T, U>)
method I introduced earlier in my article [^ 1] on iterating over Map collections. It is now possible to write sequential access processing more concisely and intuitively than the extended for statement
.
The basic writing method is as follows.
import java.util.List;
import java.util.ArrayList;
public final class TestForEach {
public static void main(String[] args) {
final List<String> testList = new ArrayList<>(3);
testList.add("test1");
testList.add("test2");
testList.add("test3");
testList.forEach((value) -> {
//Get the values stored in testList in order from the beginning
System.out.println(value);
});
}
}
The point that seems difficult is the argument, but the argument of the forEach ()
method is a lambda expression that implements the functional interface Consumer <T>
. Consumer <T>
is a functional interface that abstracts the operation of receiving only one argument and not returning any value.
I will omit the lambda expression and functional interface because they deviate from the purpose of this article, but when using the forEach ()
method in List, it is okay if you remember how to write the above sample code.
And the output result of executing the above sample code is as follows.
test1
test2
test3
--The forEach
method cannot return a value
As I mentioned earlier in the description of functional interfaces, the forEach
method cannot return a value due to the characteristics ofConsumer <T>
. Therefore, if you want to return false
as a boolean value when some error is detected during the iterative processing of List, consider using the extended for statement
.
[^ 1]: [Java] How to get the key and value stored in Map by iterative processing
Recommended Posts