For 循环或 Foreach - Java 中哪一个更快?
来源:Medium 几年前,当我找工作时,我在面试中被问到的问题之一是我们是否应该使用for或forEach来迭代ArrayList? 关于forEach和for之间偏好差异的争论已经存在很长时间了。我的印象是forEach更快。但最后我意识到我错了。仅供参考, Java 1.5 中引入的forEach循环(或改进的for循环)通过完全隐藏迭代器或索引变量来消除混乱和错误的可能性。我相信for和forEach之间唯一的实际区别是,对于索引对象,我们无法访问索引。for(int i = 0; i < mylist.length; i++) {
if(i < 5) {
//do something
} else {
//do other stuff
}
}
但是,我们可以使用forEach创建一个int类型的单独索引变量。例如:
int index = -1;
for(int myint : mylist) {
index++;
if(index < 5) {
//do something
} else {
//do other stuff
}
}
让我们编写一个简单的类,它有一个foreachTest()方法,该方法使用forEach迭代列表。
import java.util.List;
public class ForEachTest {
List<Integer> intList;
public void foreachTest(){
for(Integer i : intList){
}
}
}
当我们编译此类时,编译器在内部将代码转换为迭代器实现。我通过运行javap -verbose IterateListTest反编译了已编译的代码。
public void foreachTest();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=3, args_size=1
0: aload_0
1: getfield #19 // Field intList:Ljava/util/List;
4: invokeinterface #21, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
9: astore_2
10: goto 23
13: aload_2
14: invokeinterface #27, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
19: checkcast #33 // class java/lang/Integer
22: astore_1
23: aload_2
24: invokeinterface #35, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
29: ifne 13
32: return
LineNumberTable:
line 9: 0
line 12: 32
LocalVariableTable:
Start Length Slot Name Signature
0 33 0 this Lcom/greekykhs/springboot/ForEachTest;
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 13
locals = [ class com/greekykhs/springboot/ForEachTest, top, class java/util/Iterator ]
stack = []
frame_type = 9 /* same */
从上面的字节码我们看到:
-
getfield命令用于获取整型变量。
-
调用List.iterator获取迭代器实例。
-
调用iterator.hasNext。如果返回 true,则应该调用iterator.next方法。
import java.util.ArrayList;
import java.util.List;
public class IterateListTest {
public static void main(String[] args) {
List<Integer> mylist = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
mylist.add(i);
}
long forLoopStartTime = System.currentTimeMillis();
for (int i = 0; i < mylist.size(); i++) {mylist.get(i);}
long forLoopTraversalCost =System.currentTimeMillis()-forLoopStartTime;
System.out.println("for loop traversal cost for ArrayList= "+ forLoopTraversalCost);
long forEachStartTime = System.currentTimeMillis();
for (Integer integer : mylist) {}
long forEachTraversalCost =System.currentTimeMillis()-forEachStartTime;
System.out.println("foreach traversal cost for ArrayList= "+ forEachTraversalCost);
}
}
结果如下: 正如我们所看到的, for循环的性能优于forEach循环。如果您使用LinkedList而不是ArrayList,您可以看到forEach 的性能比LinkedList更好。 ArrayList内部使用数组来存储元素。由于数组是连续的内存区域,因此时间复杂度为 O(1)。这是因为数据是通过索引检索的。 LinkedList使用双向链表。当我们使用for循环实现遍历时,每次都是从链表的头节点开始,所以时间复杂度为O(n*n)。
循环遍历 Java 映射中每个条目的 8 种有效方法
来源:Medium 上周,一位实习生问我如何迭代 Java Map。我回答说,因为很简单,这个问题的答案总是在谷歌上。一段时间后,她把StackOverflow上的页面地址发给我,结果发现有很多人在关注这个问题。因此,我决定深入探讨迭代问题,并与大家分享几种实现迭代的方法。1.使用迭代器和Map.Entry
@Test
public void test1_UsingWhileAndMapEntry(){
long i = 0;
Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, Integer> pair = it.next();
i += pair.getKey() + pair.getValue();
}
System.out.println(i);
}
2.使用foreach和Map.Entry
@Test
public void test2_UsingForEachAndMapEntry(){
long i = 0;
for (Map.Entry<Integer, Integer> pair : map.entrySet()) {
i += pair.getKey() + pair.getValue();
}
System.out.println(i);
}
3. 从 Java 8 使用 foreach
@Test
public void test3_UsingForEachAndJava8(){
final long[] i = {0};
map.forEach((k, v) -> i[0] += k + v);
System.out.println(i[0]);
}
4.使用keySet和foreach
@Test
public void test4_UsingKeySetAndForEach(){
long i = 0;
for (Integer key : map.keySet()) {
i += key + map.get(key);
}
System.out.println(i);
}
5.使用keySet和iterator
@Test
public void test5_UsingKeySetAndIterator(){
long i = 0;
Iterator<Integer> it = map.keySet().iterator();
while (it.hasNext()) {
Integer key = it.next();
i += key + map.get(key);
}
System.out.println(i);
}
6. 使用 for 和 Map.Entry
@Test
public void test6_UsingForAndIterator(){
long i = 0;
for (Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator(); entries.hasNext(); ) {
Map.Entry<Integer, Integer> entry = entries.next();
i += entry.getKey() + entry.getValue();
}
System.out.println(i);
}
7.使用Java 8 Stream API
@Test
public void test7_UsingJava8StreamApi(){
System. out .println(map.entrySet().stream().mapToLong(e -> e.getKey() + e.getValue()).sum());
}
8. Java 8 Stream API的并行使用
@Test
public void test8_UsingJava8StreamApiParallel(){
System. out .println(map.entrySet().parallelStream().mapToLong(e -> e.getKey() + e.getValue()).sum());
}
每种方法在速度方面的比较:
public final static Integer SIZE = 1000000;
public Map<Integer, Integer> map = toMap();
public Map<Integer, Integer> toMap(){
map = new HashMap<>(SIZE);
for (int i = 0; i < SIZE; i++) {
map.put(i, i);
}
return map;
}
我们得到:
GO TO FULL VERSION