package com.example.lambda;

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.Comparator;
import java.util.function.Function;
import java.util.stream.Collectors; 
import java.util.stream.IntStream;

public class ForeachExample {

	public static void main(String[] args) {
		// list of integers
		List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

		// use traditional for loop
		System.out.println("== traditional for loop ==");
		for (int i = 0; i < list.size(); i++) {
			System.out.println(list.get(i));
		}

		// use foreach
		System.out.println("== foreach loop ==");
		for (int num : list) {
			System.out.println(num);
		}

		// use stream foreach
		System.out.println("== stream foreach ==");
		// define task to be passed to foreach
		// 'tasl' is an object that store reference to a function definition
		Consumer<Integer> task = n -> System.out.println(n);
		// pass 'task' as parameter to foreach()
		list.stream().forEach(task);

		// alternative 1
		System.out.println("== alternative 1 ==");
		list.forEach(n -> System.out.println(n));

		// alternative 2 - use method reference
		System.out.println("== alternative 2 ==");
		list.forEach(System.out::println);
		
		// display list of numbers in reverse order
		System.out.println("== reversed sort ==");
		// sorted is the intermediate operation
		// forEach is the terminal operation
		list.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);
		
		// foreach number, get the squares and the calculate the total of all numbers
		// display the total
		// do it in parallel stream
		// result = 1 + 4 + 9 + 16 + 25
		int result = list.parallelStream().map(n -> n * n).reduce(0, (a, b) -> a + b);
		System.out.println("result: " + result);
		
		// use the traditional way
		int sum = 0;
		for (int i = 0; i < list.size(); i++) {
			int squares = list.get(i) * list.get(i);
			sum += squares;
		}
		System.out.println("sum is " + sum);
		
		// use concrete class implementation instead of lambda
		MapFunction mf = new MapFunction();
		// pass mf as parameter to map()
		result = list.parallelStream().map(mf).reduce(0, (a, b) -> a + b);
		System.out.println("result2 is " + result);
		
		// generate a list of 10 numbers (0-9) and the display
		List<Integer> numbers = IntStream.range(0, 
				10).boxed().collect(Collectors.toList()); 
		System.out.println(numbers);
		// use stream foreach to display numbers
		numbers.forEach(System.out::println);
	}
}

class MapFunction implements Function<Integer, Integer> {
	public Integer apply(Integer num) {
		System.out.println("apply() executed...");
		return num * num;
	}
}
