Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,54 +23,54 @@

package com.iluwatar.collectionpipeline;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* In imperative-style programming, it is common to use for and while loops for
* most kinds of data processing. Function composition is a simple technique
* that lets you sequence modular functions to create more complex operations.
* When you run data through the sequence, you have a collection pipeline.
* Together, the Function Composition and Collection Pipeline patterns enable
* you to create sophisticated programs where data flow from upstream to
* downstream and is passed through a series of transformations.
*
* In imperative-style programming, it is common to use for and while loops for most kinds of data
* processing. Function composition is a simple technique that lets you sequence modular functions
* to create more complex operations. When you run data through the sequence, you have a collection
* pipeline. Together, the Function Composition and Collection Pipeline patterns enable you to
* create sophisticated programs where data flow from upstream to downstream and is passed through a
* series of transformations.
*/
public class App {

private static final Logger LOGGER = LoggerFactory.getLogger(App.class);

/**
* Program entry point.
*
* @param args
* command line args
*
* @param args command line args
*/
public static void main(String[] args) {

List<Car> cars = CarFactory.createCars();

List<String> modelsImperative = ImperativeProgramming.getModelsAfter2000(cars);
LOGGER.info(modelsImperative.toString());

List<String> modelsFunctional = FunctionalProgramming.getModelsAfter2000(cars);
LOGGER.info(modelsFunctional.toString());

Map<Category, List<Car>> groupingByCategoryImperative = ImperativeProgramming.getGroupingOfCarsByCategory(cars);

Map<Category, List<Car>> groupingByCategoryImperative =
ImperativeProgramming.getGroupingOfCarsByCategory(cars);
LOGGER.info(groupingByCategoryImperative.toString());

Map<Category, List<Car>> groupingByCategoryFunctional = FunctionalProgramming.getGroupingOfCarsByCategory(cars);
Map<Category, List<Car>> groupingByCategoryFunctional =
FunctionalProgramming.getGroupingOfCarsByCategory(cars);
LOGGER.info(groupingByCategoryFunctional.toString());

Person john = new Person(cars);

List<Car> sedansOwnedImperative = ImperativeProgramming.getSedanCarsOwnedSortedByDate(List.of(john));
List<Car> sedansOwnedImperative =
ImperativeProgramming.getSedanCarsOwnedSortedByDate(List.of(john));
LOGGER.info(sedansOwnedImperative.toString());

List<Car> sedansOwnedFunctional = FunctionalProgramming.getSedanCarsOwnedSortedByDate(List.of(john));
List<Car> sedansOwnedFunctional =
FunctionalProgramming.getSedanCarsOwnedSortedByDate(List.of(john));
LOGGER.info(sedansOwnedFunctional.toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@ public class Car {

/**
* Constructor to create an instance of car.
* @param make the make of the car
* @param model the model of the car
*
* @param make the make of the car
* @param model the model of the car
* @param yearOfMake the year of built of the car
* @param category the {@link Category} of the car
* @param category the {@link Category} of the car
*/
public Car(String make, String model, int yearOfMake, Category category) {
this.make = make;
Expand Down Expand Up @@ -103,7 +104,7 @@ public String getModel() {
public int getYear() {
return year;
}

public Category getCategory() {
return category;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ private CarFactory() {

/**
* Factory method to create a {@link List} of {@link Car} instances.
*
* @return {@link List} of {@link Car}
*/
public static List<Car> createCars() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
package com.iluwatar.collectionpipeline;

/**
* Enum for the category of car
* Enum for the category of car.
*/
public enum Category {
JEEP, SEDAN, CONVERTIBLE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,56 +30,53 @@

/**
* Iterating and sorting with a collection pipeline
*
*
* <p>In functional programming, it's common to sequence complex operations through
* a series of smaller modular functions or operations. The series is called a
* composition of functions, or a function composition. When a collection of
* data flows through a function composition, it becomes a collection pipeline.
* Function Composition and Collection Pipeline are two design patterns
* frequently used in functional-style programming.
*
* a series of smaller modular functions or operations. The series is called a composition of
* functions, or a function composition. When a collection of data flows through a function
* composition, it becomes a collection pipeline. Function Composition and Collection Pipeline are
* two design patterns frequently used in functional-style programming.
*
* <p>Instead of passing a lambda expression to the map method, we passed the
* method reference Car::getModel. Likewise, instead of passing the lambda
* expression car -> car.getYear() to the comparing method, we passed the method
* reference Car::getYear. Method references are short, concise, and expressive.
* It is best to use them wherever possible.
*
* method reference Car::getModel. Likewise, instead of passing the lambda expression car ->
* car.getYear() to the comparing method, we passed the method reference Car::getYear. Method
* references are short, concise, and expressive. It is best to use them wherever possible.
*/
public class FunctionalProgramming {
private FunctionalProgramming() {
}

/**
* Method to get models using for collection pipeline.
*
*
* @param cars {@link List} of {@link Car} to be used for filtering
* @return {@link List} of {@link String} representing models built after year 2000
*/
public static List<String> getModelsAfter2000(List<Car> cars) {
return cars.stream().filter(car -> car.getYear() > 2000)
.sorted(Comparator.comparing(Car::getYear))
.map(Car::getModel).collect(Collectors.toList());
.sorted(Comparator.comparing(Car::getYear))
.map(Car::getModel).collect(Collectors.toList());
}

/**
* Method to group cars by category using groupingBy
*
* Method to group cars by category using groupingBy.
*
* @param cars {@link List} of {@link Car} to be used for grouping
* @return {@link Map} with category as key and cars belonging to that category as value
*/
public static Map<Category, List<Car>> getGroupingOfCarsByCategory(List<Car> cars) {
return cars.stream().collect(Collectors.groupingBy(Car::getCategory));
}

/**
* Method to get all Sedan cars belonging to a group of persons sorted by year of manufacture
*
* Method to get all Sedan cars belonging to a group of persons sorted by year of manufacture.
*
* @param persons {@link List} of {@link Person} to be used
* @return {@link List} of {@link Car} to belonging to the group
*/
public static List<Car> getSedanCarsOwnedSortedByDate(List<Person> persons) {
return persons.stream().map(Person::getCars).flatMap(List::stream)
.filter(car -> Category.SEDAN.equals(car.getCategory()))
.sorted(Comparator.comparing(Car::getYear)).collect(Collectors.toList());
.filter(car -> Category.SEDAN.equals(car.getCategory()))
.sorted(Comparator.comparing(Car::getYear)).collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,30 +31,28 @@
import java.util.Map;

/**
* Imperative-style programming to iterate over the list and get the names of
* cars made later than the year 2000. We then sort the models in ascending
* order by year.
*
* Imperative-style programming to iterate over the list and get the names of cars made later than
* the year 2000. We then sort the models in ascending order by year.
*
* <p>As you can see, there's a lot of looping in this code. First, the
* getModelsAfter2000UsingFor method takes a list of cars as its parameter. It
* extracts or filters out cars made after the year 2000, putting them into a
* new list named carsSortedByYear. Next, it sorts that list in ascending order
* by year-of-make. Finally, it loops through the list carsSortedByYear to get
* the model names and returns them in a list.
*
* getModelsAfter2000UsingFor method takes a list of cars as its parameter. It extracts or filters
* out cars made after the year 2000, putting them into a new list named carsSortedByYear. Next, it
* sorts that list in ascending order by year-of-make. Finally, it loops through the list
* carsSortedByYear to get the model names and returns them in a list.
*
* <p>This short example demonstrates what I call the effect of statements. While
* functions and methods in general can be used as expressions, the {@link Collections}
* sort method doesn't return a result. Because it is used as a statement, it
* mutates the list given as argument. Both of the for loops also mutate lists
* as they iterate. Being statements, that's just how these elements work. As a
* result, the code contains unnecessary garbage variables
* functions and methods in general can be used as expressions, the {@link Collections} sort method
* doesn't return a result. Because it is used as a statement, it mutates the list given as
* argument. Both of the for loops also mutate lists as they iterate. Being statements, that's just
* how these elements work. As a result, the code contains unnecessary garbage variables
*/
public class ImperativeProgramming {
private ImperativeProgramming() {
}

/**
* Method to return the car models built after year 2000 using for loops.
*
* @param cars {@link List} of {@link Car} to iterate over
* @return {@link List} of {@link String} of car models built after year 2000
*/
Expand All @@ -80,16 +78,16 @@ public int compare(Car car1, Car car2) {

return models;
}

/**
* Method to group cars by category using for loops
*
* Method to group cars by category using for loops.
*
* @param cars {@link List} of {@link Car} to be used for grouping
* @return {@link Map} with category as key and cars belonging to that category as value
*/
public static Map<Category, List<Car>> getGroupingOfCarsByCategory(List<Car> cars) {
Map<Category, List<Car>> groupingByCategory = new HashMap<>();
for (Car car: cars) {
for (Car car : cars) {
if (groupingByCategory.containsKey(car.getCategory())) {
groupingByCategory.get(car.getCategory()).add(car);
} else {
Expand All @@ -100,33 +98,34 @@ public static Map<Category, List<Car>> getGroupingOfCarsByCategory(List<Car> car
}
return groupingByCategory;
}

/**
* Method to get all Sedan cars belonging to a group of persons sorted by year of manufacture using for loops
*
* Method to get all Sedan cars belonging to a group of persons sorted by year of manufacture
* using for loops.
*
* @param persons {@link List} of {@link Person} to be used
* @return {@link List} of {@link Car} to belonging to the group
*/
public static List<Car> getSedanCarsOwnedSortedByDate(List<Person> persons) {
List<Car> cars = new ArrayList<>();
for (Person person: persons) {
for (Person person : persons) {
cars.addAll(person.getCars());
}

List<Car> sedanCars = new ArrayList<>();
for (Car car: cars) {
for (Car car : cars) {
if (Category.SEDAN.equals(car.getCategory())) {
sedanCars.add(car);
}
}

sedanCars.sort(new Comparator<Car>() {
@Override
public int compare(Car o1, Car o2) {
return o1.getYear() - o2.getYear();
}
});

return sedanCars;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class Person {

/**
* Constructor to create an instance of person.
*
* @param cars the list of cars owned
*/
public Person(List<Car> cars) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,13 @@

package com.iluwatar.collectionpipeline;

import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Tests that Collection Pipeline methods work as expected.
Expand All @@ -39,35 +38,35 @@ public class AppTest {
private static final Logger LOGGER = LoggerFactory.getLogger(AppTest.class);

private List<Car> cars = CarFactory.createCars();

@Test
public void testGetModelsAfter2000UsingFor() {
var models = ImperativeProgramming.getModelsAfter2000(cars);
assertEquals(List.of("Avenger", "Wrangler", "Focus", "Cascada"), models);
}

@Test
public void testGetModelsAfter2000UsingPipeline() {
var models = FunctionalProgramming.getModelsAfter2000(cars);
assertEquals(List.of("Avenger", "Wrangler", "Focus", "Cascada"), models);
}

@Test
public void testGetGroupingOfCarsByCategory() {
var modelsExpected = Map.of(
Category.CONVERTIBLE, List.of(new Car("Buick", "Cascada", 2016, Category.CONVERTIBLE),
new Car("Chevrolet", "Geo Metro", 1992, Category.CONVERTIBLE)),
Category.SEDAN, List.of(new Car("Dodge", "Avenger", 2010, Category.SEDAN),
new Car("Ford", "Focus", 2012, Category.SEDAN)),
Category.JEEP, List.of(new Car("Jeep", "Wrangler", 2011, Category.JEEP),
new Car("Jeep", "Comanche", 1990, Category.JEEP)));
Category.CONVERTIBLE, List.of(new Car("Buick", "Cascada", 2016, Category.CONVERTIBLE),
new Car("Chevrolet", "Geo Metro", 1992, Category.CONVERTIBLE)),
Category.SEDAN, List.of(new Car("Dodge", "Avenger", 2010, Category.SEDAN),
new Car("Ford", "Focus", 2012, Category.SEDAN)),
Category.JEEP, List.of(new Car("Jeep", "Wrangler", 2011, Category.JEEP),
new Car("Jeep", "Comanche", 1990, Category.JEEP)));
var modelsFunctional = FunctionalProgramming.getGroupingOfCarsByCategory(cars);
var modelsImperative = ImperativeProgramming.getGroupingOfCarsByCategory(cars);
LOGGER.info("Category " + modelsFunctional);
assertEquals(modelsExpected, modelsFunctional);
assertEquals(modelsExpected, modelsImperative);
}

@Test
public void testGetSedanCarsOwnedSortedByDate() {
var john = new Person(cars);
Expand Down
Loading