# Notes

# Streams API Interface

# Lambda Expressions

3 ways to execute

Comparator<Employee> byAge =
    (Employee emp1, Employee emp2) -> emp1.getAge() - emp2.getAge();
List<Employee> emps = fetchMembers();
emps.sort(byAge);
    
List<Employee> emps = fetchMembers();
emps.sort((Employee emp1, Employee emp2) -> emp1.getAge() - emp2.getAge());
    
List<Employee> emps = fetchMembers();
emps.sort((emp1, emp2) -> emp1.getAge() - emp2.getAge());

Lambdas or Anonymous functions.

# Syntax

lambda operator -> body
where lambda operator can be...

  • Zero parameter:
    () -> System.out.println("Zero parameter lambda");

  • One parameter:
    (p) -> System.out.println("One parameter: " + p);
    It is not mandatory to use parentheses, if the type of that variable can be inferred from the context

  • Multiple parameters :
    (p1, p2) -> System.out.println("Multiple parameters: " + p1 + ", " + p2);

// A Java program to demonstrate simple lambda expressions 
import java.util.ArrayList; 
class Test { 
    public static void main(String args[])     { 
        // Creating an ArrayList with elements {1, 2, 3, 4} 
        ArrayList<Integer> arrL = new ArrayList<Integer>(); 
        arrL.add(1); 
        arrL.add(2); 
        arrL.add(3); 
        arrL.add(4); 
  
        // Using lambda expression to print all elements of arrL 
        arrL.forEach(n -> System.out.print(n + " ")); 
  
        // Using lambda expression to print even elements of arrL 
        arrL.forEach(n -> { if (n%2 == 0) System.out.print(n + " "); }); 
    }
}

Output

1 2 3 4 2 4

# Stream API functionality

Streams can be obtained in a number of ways.
Some examples include:

  • From a Collection via the stream()
  • From an array via Arrays.stream(Object[]) ;

Package => java.util.stream

Classes used to support functional-style operations on streams of elements, such as map-reduce transformations on collections.
用于支持对元素流进行功能样式操作的类,例如集合上的 map-reduce 转换。

For example:

int sum = widgets.stream()
          .filter(b -> b.getColor() == RED)
          .mapToInt(b -> b.getWeight())
          .sum();

# Operations and pipelines

A stream is a limitless iterator that allows you to process collections.
流是无限的迭代器,可以处理集合。

Stream operations are divided into intermediate and terminal operations, and are combined to form stream pipelines or chained operations. The pipeline usually takes the form of map-filter-reduce pattern.
流操作分为中间操作和终端操作,结合在一起以形成流管道或链接操作。 管道通常采用 map-filter-reduce 模式的形式。

The .map() method is not an associative data structure, rather it is a transformative operation. It takes a Stream<T> and it returns either a Stream<T> or Stream<U> .
.map() 方法不是关联数据结构,而是一种转换操作。 它采用 Stream <T>,并返回 Stream <T > 或 Stream <U>。

Example: Stream<String> to Stream<String>
Example: Stream<String> to Stream<Date>

A stream pipeline consists of a source (such as a Collection or an array); followed by zero or more intermediate operations such as Stream.filter or Stream.map ; and a terminal operation such as Stream.forEach or Stream.reduce .
流管道由一个源(例如 Collection 或数组)组成; 随后是零个或多个中间操作,例如 Stream.filterStream.map ; 以及诸如 Stream.forEachStream.reduce 之类的终端操作。

Intermediate operations
return a new stream. They are always lazy; executing an intermediate operation such as filter() does not actually perform any filtering, but instead creates a new stream. Some method examples follow:
中间操作返回一个新的流。 它们总是懒惰的。 执行诸如 filter() 之类的中间操作实际上并不执行任何过滤,而是创建一个新的流。 一些方法示例如下:
peek() method
map() method
filter() method
Terminal operations
such as Stream.forEach or IntStream.sum , may traverse the stream to produce a result or a side-effect. After the terminal operation is performed, the stream pipeline is considered consumed, and can no longer be used. Example methods follow:
诸如 Stream.forEachIntStream.sum 之类的终端操作,可能会遍历该流以产生结果或副作用。 执行终端操作后,流管线被视为已消耗,无法再使用。 示例方法如下:
forEach() method
count() method
max() method
collect() method
reduce() method

# Ex use cases

map
The map method is used to map the items in the collection to other objects according to the Function passed as argument.
用于根据作为参数传递的 Function,将集合中的项目映射到其他对象。
List number = Arrays.asList(2,3,4,5);
List square = number.stream().map(x -> x*x).collect(Collectors.toList());
filter
The filter method is used to select elements as per the Predicate passed as argument.
根据作为参数传递的词,进行元素选择。
List names = Arrays.asList("Reflection","Collection","Stream");
List result = names.stream().filter(s->s.startsWith("S")).collect(Collectors.toList());
sorted
The sorted method is used to sort the stream.
用于对流进行排序。
List names = Arrays.asList("Reflection","Collection","Stream");
List result = names.stream().sorted().collect(Collectors.toList());
Map example
// applying 12% VAT on each purchase
// Without lambda expressions:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
for (Integer cost : costBeforeTax) {
      double price = cost + .12*cost;
      System.out.println(price);
}
// With Lambda expression:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
costBeforeTax.stream().map((cost) -> cost + .12*cost).forEach(System.out::println);

Output

112.0
224.0
336.0
448.0
560.0
112.0
224.0
336.0
448.0
560.0
Map reduce example
// Applying 12% VAT on each purchase
// Old way:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double total = 0;
for (Integer cost : costBeforeTax) {
 double price = cost + .12*cost;
 total = total + price;
}
System.out.println("Total : " + total);
// New way:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double bill = costBeforeTax.stream().map((cost) -> cost + .12*cost).reduce((sum, cost) -> sum + cost).get();
System.out.println("Total : " + bill);

Output

Total : 1680.0
Total : 1680.0
Example
// joining list and appending data
List<String> l = new ArrayList(Arrays.asList("one", "two"));
Stream<String> sl = l.stream();
l.add("three");
String s = sl.collect(joining(" "));

# Further uses of Stream API!!

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Employees {
    //put a structure together
    String name;
    int rank;
    //add constructor
    public Employees(String n, int r){ name=n; rank=r;  }
    
    public static void main(String[] args) {
        List<Employees> list = new ArrayList<>();
        list.add(new Employees("james papa", 3));
        list.add(new Employees("luke papa", 1));
        list.add(new Employees("kay papa", 2));
        /*Old school coding - sort first then display filtered results
        Collections.sort(list, new EmployeesSort());
        for(Employees e : list) {
            if(e.rank == 4)
                System.out.println(e.name + " " + e.rank);
        }
        */
    
        //New school output list - stream alt. and sorted!
        list.stream()
            .filter((e) -> (e.rank == 4))
            .sorted((a, b) -> a.name.compareTo(b.name))
            .forEachOrdered((e) -> {
                System.out.println(e.name + " " + e.rank);
            });
    }
}
/*
import java.util.Comparator;
public class EmployeesSort implements Comparator<Employees>{
    //compare by name
    public int compare(Employees obj1, Employees obj2) {
        return obj1.name.compareTo(obj2.name);
    }
    
}*/

# Lab

  • PROJECT
    Bank record generations

  • Objective
    To write a program that performs data analysis from class objects created in lab #2.

# PROJECT DESCRIPTION

Bank of IIT is in desperate need of analytics from its clients for its loan application process. Currently records show 600 clients exist and the bank is hoping to expand its clientele and its potential by offering premium loans to deserved grantees.
IIT 银行迫切需要客户对其贷款申请流程进行分析。 目前的记录显示有 600 个客户,该银行希望通过向应得的受赠人提供优质贷款来扩大其客户群和潜力。

Perform the data analysis as follows for this lab.
对该实验室执行以下数据分析。

# Project Details

  • Add to your existing project files from lab 2, a new class called Records. Have the class extend the BankRecords class to grab hold of its instance methods plus the BankRecords object array.
    在 lab2 的项目文件中,添加一个名为 Records 的新类。让该类扩展 BankRecords 类以获取其实例方法,以及 BankRecords 对象数组。

  • Provide at least 2 comparator classes implementing the java.util.Comparator interface for comparing various fields for the following data analysis requirements.
    提供至少两个比较器类,这些比较器类实现 java.util.Comparator 接口,以比较各个字段,以符合以下数据分析要求。

  • Perform the following analysis requirements and output detail for the Records class.
    Records 类执行以下分析要求和输出详细信息。

    Display the following data analytics in a coherent manner to the console:
    以一致的方式在控制台上显示以下数据分析:

  • average income for males vs. females
    男性与女性的平均收入
  • number of females with a mortgage and savings account
    拥有抵押和储蓄账户的女性人数
  • number of males with both a car and 1 child per location
    每个地点有汽车和 1 个孩子的男性人数
  • Write all displayed data to a text file called bankrecords.txt relative to your project path as well. Append your name within the text file at the end of the file plus the date/time.
    将所有显示的数据写入一个名为 bankrecords.txt 的文本文件中,并保存在相对于您的项目路径的位置。在文件末尾的文本中添加您的姓名以及日期 / 时间。

    Include for credit, your project files including your .csv file and bankrecords.txt file into a zip file, also your jar (executable) file as well as a separate file, and your snapshots of console output and your bankrecords.txt file plus your source code into a Word doc file.
    项目文件(包括.csv 文件和 bankrecords.txt 文件)压缩到 zip 文件中,还包括 jar(可执行)文件也是一个单独的文件。控制台输出快照、bankrecords.txt 文件以及您的源代码转换为 Word doc 文件。

# Objectives

# Example of creating a comparator

For your first analytic response that is to show the
average income for males vs. females
you may want to create a comparator class to sort by sex
对于第一个分析,即显示男性与女性的平均收入,可能要创建一个比较器类,以按性别排序。

Example
import java.util.Comparator;
public class SexComparator implements Comparator<BankRecords> {
    @Override
    public int compare(BankRecords o1, BankRecords o2) {
        // use compareTo to compare strings
        int result = o1.getSex().compareTo(o2.getSex());
        return result;
    }
}

Think of comparators you can set up with not only a primary sort but secondary sorts as well. Example maybe a female sort as a primary sort and mortgages as a secondary sort, etc.
比较器不仅可以设置一级排序,还可以设置二级排序。 例如,女性排序为主要排序,抵押贷款为次要排序,等等。

Another example here maybe to create a comparator java class for sorting by location…
这里的另一个示例可创建一个比较器 Java 类,以按地点排序…

Example
import java.util.Comparator;
public class LocationComparator implements Comparator<BankRecords>{
    @Override
    public int compare(BankRecords o1, BankRecords o2) {
        // use compareTo to compare strings
        int result = o1.getRegion().compareTo(o2.getRegion());
        return result;
    }
}

# Call up your comparators in your Records.java file.

在 Records.java 文件中调用比较器。

Ex. in main() , you can call a series of functions to perform your analytic results.
例如 在 main 函数中,可以调用一系列函数来执行分析结果。

Set up your file to also not only write to the console but to a text file.
将文件设置为不仅要写入控制台,还要写入文本文件。

Example
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
public class Records extends BankRecords {
    // create formatted object to write output directly to console & file
    static FileWriter fw = null;
    public Records() {
        try {
            fw = new FileWriter("bankrecords.txt");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        Records br = new Records();
        br.readData();
        // call functions to perform analytics
        AvgComp();     // analyze average income per loc
        //femsComp();  // female count w. mort/savings accounts 
        //malesComp(); // male counts per loc. w. car & 1 child 
        // *** close out file object ***//
        try {
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private static void AvgComp() {
        Arrays.sort(robjs, new SexComparator());
        // set up needed variables to gather counts & income by sex 
        // to determine average income by sex
        int maleCt = 0, femCt = 0;
        double maleInc =0, femInc = 0;
        for (int i = 0; i < robjs.length; i++) {
            if (robjs[i].getSex().equals("FEMALE")) {
                ++femCt;
                femInc += robjs[i].getIncome();
            } else {
                //...
            }
        }
        // display resulting averages to console and to file
         
        System.out.printf(" ... ");
        try {
            fw.write("Avg inc. for Females: $" + (femInc/femCt) );
            fw.write("...");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Continue in like manner as above to finish up two other method definitions, callable in main().

以上述方式继续完成另外两个方法定义,可在 main 函数中调用。

# code

Records.java
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
public class Records extends BankRecords {
    // create formatted object to write output directly to console & file
    static FileWriter fw = null;
    public Records() {
        try {
            fw = new FileWriter("bankrecords.txt");
            fw.write("Data analytic results:\n");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        Records br = new Records();
        br.readData();
        
        System.out.printf("Data analytic results:\n");
        // call functions to perform analytics
        AvgComp(); // analyze average income for males vs. females
        femsComp(); // female count with mortgage/savings accounts 
        malesComp(); // male counts per loc. w. car & 1 child 
        // *** close out file object ***//
        try {
            Date date=new Date();
            fw.write("\n\nShuang Liu @ " + date.toString());
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private static void AvgComp() {
        Arrays.sort(robjs, new SexComparator());
        // set up needed variables to gather counts & income by sex 
        // to determine average income by sex
          
        int maleCt = 0, femCt = 0;
        double maleInc =0, femInc = 0;
        
        for (int i = 0; i < robjs.length; i++) {
            if (robjs[i].getSex().equals("FEMALE")) {
                ++femCt;
                femInc += robjs[i].getIncome();
            } else {
                ++maleCt;
                maleInc += robjs[i].getIncome();
            }
        }
        // display resulting averages to console and to file
        
        String femAvg = String.format("\nAverage income for Females: $%.2f", femInc/femCt).toString();
        String maleAvg = String.format("\nAverage income for Males: $%.2f", maleInc/maleCt).toString();
        
        System.out.printf(femAvg);
        System.out.printf(maleAvg);
        
        try {
            fw.write(femAvg);
            fw.write(maleAvg);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private static void femsComp() {
        Arrays.sort(robjs, new SexComparator());        
        
        int femCt = 0;
        
        for (int i = 0; i < robjs.length; i++) {
            if (robjs[i].getSex().equals("FEMALE")
                    && robjs[i].getMortgage().equals("YES")
                    && robjs[i].getSave_act().equals("YES")) {
                ++femCt;
            }
        }
        
        String femNum = "\n\nNum. of Females with Mortgage & saving act: " + femCt + "\n";
        System.out.printf(femNum);
        
        try {
            fw.write(femNum);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private static void malesComp() {
        Arrays.sort(robjs, new LocationComparator());
        HashMap<String, Integer> map = new HashMap<String, Integer>();
        
        for (int i = 0; i < robjs.length; i++) {
            if (robjs[i].getSex().equals("MALE")
                    && robjs[i].getCar().equals("YES")
                    && robjs[i].getChildren() == 1) {
                if(map.containsKey(robjs[i].getRegion())) {
                    map.put(robjs[i].getRegion(), map.get(robjs[i].getRegion())+1);
                } else {
                    map.put(robjs[i].getRegion(), 1);
                }
            }
        }
        
        StringBuffer sb = new StringBuffer();
        
        map.forEach((region, count) -> {            
            char[] cs= region.toLowerCase().toCharArray();
            cs[0] -= 32;
            
            sb.append("\n" + String.valueOf(cs) + " region males with car & 1 child: " + count);
        });
        
        System.out.printf(sb.toString());
        
        try {
            fw.write(sb.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
        
    }
}
SexComparator.java
import java.util.Comparator;
public class SexComparator implements Comparator<BankRecords> {
    @Override
    public int compare(BankRecords o1, BankRecords o2) {
        // use compareTo to compare strings
        int result = o1.getSex().compareTo(o2.getSex());
        return result;
    }
}
LocationComparator.java
import java.util.Comparator;
public class LocationComparator implements Comparator<BankRecords>{
    @Override
    public int compare(BankRecords o1, BankRecords o2) {
        // use compareTo to compare strings
        int result = o1.getRegion().compareTo(o2.getRegion());
        return result;
    }
}
BankRecords.java
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class BankRecords extends Client {
    //setup static objects for IO processing
    //array of BankRecords objects
    static BankRecords robjs[] = new BankRecords[600]; 
    //arraylist to hold spreadsheet rows & columns
    static ArrayList<List<String>> array = new ArrayList<>();  
    //instance fields
    private String id;
    private int age;
    private String sex;
    private String region;
    private double income;
    private String married;
    private int children;
    private String car;
    private String save_act;
    private String current_act;
    private String mortgage;
    private String pep; 
    
    @Override
    public void readData() {
        BufferedReader br = null;
        //initialize reader object and set file path to root of project
        try {
            br = new BufferedReader(new FileReader (new File("bank-Detail.csv")));
            String line;
            //read each record in csv file
            while ((line = br.readLine()) != null) {
                //parse each record in csv file by a comma ( , )
                //into a list stored in the arraylist-> Arrays
                array.add(Arrays.asList(line.split(",")));
            }
        } catch (Exception e) {
            System.err.println("There was a problem loading the file");
        }
        
        processData();  //call function for processing record data
    }
    
    @Override
    public void processData() {
        //create index for array while iterating thru arraylist
        int idx=0;
        
        //create for each loop to cycle thru arraylist of values 
        //and PASS that data into your record objects' setters 
        for (List<String> rowData: array) {
            //initialize array of objects
            robjs[idx] = new BankRecords();
            //call setters below and populate them, item by item
            robjs[idx].setId(rowData.get(0)); //get 1st column
            robjs[idx].setAge(Integer.parseInt(rowData.get(1))); //get 2nd column
            robjs[idx].setSex(rowData.get(2));
            robjs[idx].setRegion(rowData.get(3));
            robjs[idx].setIncome(Double.parseDouble(rowData.get(4)));
            robjs[idx].setMarried(rowData.get(5));
            robjs[idx].setChildren(Integer.parseInt(rowData.get(6)));
            robjs[idx].setCar(rowData.get(7));
            robjs[idx].setSave_act(rowData.get(8));
            robjs[idx].setCurrent_act(rowData.get(9));
            robjs[idx].setMortgage(rowData.get(10));
            robjs[idx].setPep(rowData.get(11));
            /*continue processing arraylist item values into each array object -> robjs[] by index*/
            idx++;
        }
        
        //printData();  //call function to print objects held in memory       
    }
    
    @Override
    public void printData() {
        //1. Set appropriate headings for displaying first 25 records
        //2. Create for loop and print each record objects instance data 
        //3. Within for loop use appropriate formatting techniques to print out record detail  
        
        //Set heading
        System.out.printf("First 25 Client details:\n\n%-10s\t%-10s\t%-10s\t%-10s\t%-10s\t%-10s\n", "ID","Age","Sex","Region","Income","Mortgage");
        
        for (int i=0;i<25;i++){
            
            String s=String.format("%-10s\t%-10d\t%-10s\t%-10s\t%-10.2f\t%-10s", 
                    robjs[i].getId(),robjs[i].getAge(),robjs[i].getSex(),robjs[i].getRegion(),robjs[i].getIncome(),robjs[i].getMortgage());
            System.out.println(s);
            
        }
    }   
    
    /**
     * @return the id
     */
    public String getId() {
        return id;
    }
    /**
     * @param id the id to set
     */
    public void setId(String id) {
        this.id = id;
    }
    /**
     * @return the age
     */
    public int getAge() {
        return age;
    }
    /**
     * @param age the age to set
     */
    public void setAge(int age) {
        this.age = age;
    }
    /**
     * @return the sex
     */
    public String getSex() {
        return sex;
    }
    /**
     * @param sex the sex to set
     */
    public void setSex(String sex) {
        this.sex = sex;
    }
    /**
     * @return the region
     */
    public String getRegion() {
        return region;
    }
    /**
     * @param region the region to set
     */
    public void setRegion(String region) {
        this.region = region;
    }
    /**
     * @return the income
     */
    public double getIncome() {
        return income;
    }
    /**
     * @param income the income to set
     */
    public void setIncome(double income) {
        this.income = income;
    }
    /**
     * @return the married
     */
    public String getMarried() {
        return married;
    }
    /**
     * @param married the married to set
     */
    public void setMarried(String married) {
        this.married = married;
    }
    /**
     * @return the children
     */
    public int getChildren() {
        return children;
    }
    /**
     * @param children the children to set
     */
    public void setChildren(int children) {
        this.children = children;
    }
    /**
     * @return the car
     */
    public String getCar() {
        return car;
    }
    /**
     * @param car the car to set
     */
    public void setCar(String car) {
        this.car = car;
    }
    /**
     * @return the save_act
     */
    public String getSave_act() {
        return save_act;
    }
    /**
     * @param save_act the save_act to set
     */
    public void setSave_act(String save_act) {
        this.save_act = save_act;
    }
    /**
     * @return the current_act
     */
    public String getCurrent_act() {
        return current_act;
    }
    /**
     * @param current_act the current_act to set
     */
    public void setCurrent_act(String current_act) {
        this.current_act = current_act;
    }
    /**
     * @return the mortgage
     */
    public String getMortgage() {
        return mortgage;
    }
    /**
     * @param mortgage the mortgage to set
     */
    public void setMortgage(String mortgage) {
        this.mortgage = mortgage;
    }
    /**
     * @return the pep
     */
    public String getPep() {
        return pep;
    }
    /**
     * @param pep the pep to set
     */
    public void setPep(String pep) {
        this.pep = pep;
    }   
}
Client.java
public abstract class Client {
    public abstract void readData();  //read file detail
    public abstract void processData(); //process file detail
    public abstract void printData(); //print file detail
     
}