JavaRush/Java блог/Random UA/Кава-брейк #137. For loop або Foreach - що з них швидше J...

Кава-брейк #137. For loop або Foreach - що з них швидше Java? 8 ефективних способів перебору кожного запису Java Map

Стаття з групи Random UA
учасників

For loop або Foreach - що з них швидше Java?

Джерело: Medium Коли я кілька років тому шукав роботу, одне з питань, яке мені поставабо на співбесіді, було про те, чи повинні ми перебирати ArrayList , використовуючи for або forEach ? Кава-брейк #137.  For loop або Foreach - що з них швидше Java?  8 ефективних способів перебору кожного запису Java Map - 1Спори про різницю у перевагах між forEach і for відомі давно. У мене склалося враження, що длячого швидше. Але зрештою я зрозумів, що помилявся. На вашу думку, цикл forEach (або вдосконалений цикл for ), представлений в Java 1.5, позбавляє безладу і ймовірності помилки, повністю приховуючи ітератор або індексну змінну. Я вважаю, що єдина практична різниця між for і forEach полягає в тому, що у випадку об'єктів, що індексуються, у нас немає доступу до index.
for(int i = 0; i < mylist.length; i++) {
 if(i < 5) {
 //do something
 } else {
 //do other stuff
 }
}
Однак ми можемо створити окрему індексну змінну типу int за допомогою forEach . Наприклад:
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 */
З наведеного вище байт-коду ми бачимо:
  1. Команда getfield використовується отримання змінних цілих чисел.

  2. Виклик List.iterator для отримання екземпляра ітератора.

  3. Виклик iterator.hasNext . Якщо він повертає true, слід викликати метод iterator.next .

Проведемо тест продуктивності. В основному методі IterateListTest я створив список і повторив його, використовуючи цикли for і forEach .
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);
	}
}
І ось результат: Кава-брейк #137.  For loop або Foreach - що з них швидше Java?  8 ефективних способів перебору кожного запису Java Map - 2Як бачимо, продуктивність циклу for краще, ніж циклу forEach . Якщо ж використовувати LinkedList замість ArrayList , то ви зможете побачити, що продуктивність forEach краще для LinkedList . ArrayList всередині використовує масиви для зберігання елементів. Оскільки масиви є безперервні області пам'яті, тимчасова складність становить O (1). Це тим, що дані витягуються через індекси. LinkedList використовує двонаправлений пов'язаний список. Коли ми використовуємо цикл для реалізації обходу, він щоразу починається з головного вузла зв'язаного списку, тому тимчасова складність дорівнює O (n * n).

8 ефективних способів перебору кожного запису Java Map

Джерело: Medium Минулого тижня стажерка запитала мене, як виконати ітерацію Java Map. Я відповів, що оскільки це дуже просто, відповідь на це питання завжди є у Google. Через деякий час вона надіслала мені адресау сторінки в StackOverflow і виявилося, що на цю проблему звертає увагу величезна кількість людей. Тому я вирішив докладно зупинитися на питанні ітерації та поділитись кількома способами її виконання з вами. Кава-брейк #137.  For loop або Foreach - що з них швидше Java?  8 ефективних способів перебору кожного запису Java Map - 3

1. Використання iterator та 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. Використання foreach з Java 8

@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;
}
Отримуємо: Кава-брейк #137.  For loop або Foreach - що з них швидше Java?  8 ефективних способів перебору кожного запису Java Map - 4Кава-брейк #137.  For loop або Foreach - що з них швидше Java?  8 ефективних способів перебору кожного запису Java Map - 5Кава-брейк #137.  For loop або Foreach - що з них швидше Java?  8 ефективних способів перебору кожного запису Java Map - 6

Висновок

З порівняння даних ми дізналися, що метод 6 займає більше часу, а метод 8 займає більше часу, коли число невелике, але займає найменше часу, коли число значуще, оскільки метод 8 виконується одночасно. Цікаво, що порядок виконання тесту завжди for -> while -> foreach/stream, і я не знаю чому :(
Коментарі
  • популярні
  • нові
  • старі
Щоб залишити коментар, потрібно ввійти в систему
Для цієї сторінки немає коментарів.