Collections&Streams&Optional

Collections

集合,是将许多元素组合成一个单一单元的容器对象

集合,可用于存储/检索/操作/传输/聚合数据

集合框架,是用于表示和操作集合的体系结构,集合框架包含

接口(Interfaces)

实现(Implementations)

算法(Algorithms)

即,Java提供了一套包含,多种集合类型,多种数据结构实现,以及操作处理算法的集合框架,供开发人员直接使用

Iterable接口

实现此接口的类支持ForEach循环语句.Iterable接口不属于Java集合框架

Collection(接口)

表示一组被称为元素的对象,Collection接口继承自Iterable接口。即,所有集合类型均支持foreach循环语句

<E> ,泛型。集合并不关心元素的具体类型,因此设计使用泛型

List(接口)

java.util.List<E>集合。有序的,允许包含重复元素的集合。除从Collection继承的方法外,提供基于位置索引的操作方法

void add(int index, E element),将指定位置元素后移,

添加 E set(int index, E element),替换 E get(int index),

获取 E remove(int index),移除

 List集合接口基本实现类,即不同的数据结构

java.util.ArrayList<E>类,基于对象数组数据结构的实现

java.util.LinkedList<E>类,基于双向链表数据结构的实现

List集合的声明与创建

声明List集合类型变量 <>括号中声明集合中元素的类型必须为引用类型 基于对象数组存储结构ArrayList实现类 创建集合对象

Subtyping(子类型)

赋值支持子类型关系而非子类关系。即,等号右侧必须是左侧的子类型

继承关系的类型是子类型的一种。A继承自B,A是B的子类也是子类型。按JVM规范,元素间拥有继承关系的数组也是子类型。即,A[]虽然不是B[]的子类,但确是子类型 (基本数据类型int是long的子类型,因此可以实现赋值)

public class User {
    public String name;
    public User(String name) {
        this.name = name;
    }
    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
import java.util.ArrayList;
import java.util.List;
public class Main {
    private static final List<User> USERS = create();
    private static List<User> create() {
        User user = new User("BO");
        User user2 = new User ("SUN");
        User user3 = new User("SUN");
        List<User> users = new ArrayList<>();
        users.add(user2);
        users.add(user3);
        return users;
    }
     public static void main(String[] args) {
        System.out.println(USERS.isEmpty());
        System.out.println(USERS.size());
        for (User u : USERS) {
            System.out.println(u.getName());
        }
    }
}
        User user = USERS.get(0);
        User user2 = USERS.get(1);
        System.out.println("Before: " + user.getName());
        System.out.println("Before: " + user2.getName());
        for(User u : USERS) {
            u.setName("ZHANG");
        }
        System.out.println("Before: " + user.getName());
        System.out.println("Before: " + user2.getName());

因此,集合为逻辑上的容器,容器中仅保存元素对象的引用地址;操作集合中的元素时,实际操作的是元素引用的对象

ArrayList

ArrayList构造函数

ArrayList(),创建空List集合。默认创建0个元素的对象数组(OpenJDK)

ArrayList(int initialCapacity),基于指定长度创建List集合。长度仅初始化集合时使用,后期添加/移除自动更改容量

ArrayList(Collection<? extends E> c),基于指定集合创建List集合

可快速基于索引访问元素对象;其底层使用Arrays.copyOf()方法实现对象数组的增删,性能损失较小

LinkedList

List集合,基于LinkedList双向链表数据结构的实现

为每个元素创建2个节点对象,保存前/后元素的地址

当需要极其频繁的在集合头部添加元素时,效率较高。但需要为每一个元素创建两个节点对象,基于索引位置的访问需要线性时间(Positional access requires linear-time),整体性能开销较大

多数情况下使用ArrayList或不可变集合(后期讨论)即可

The Map Interface

Map接口没有继承Collection接口 Collection接口与Map接口 是平行并列的

java.util.Map<K,V>

Map,用于存放键值对(key-value)

Map不是集合

通过key值 ,保存其对应的value值,通过Key值获取对其对应的value值操作。

Map中key必须是唯一的,且每个key只能对应一个value

但不同key,可以对应同一个value

添加key-value时,如果key已经存在,则后一个覆盖前一个

基本实现类 java.util.HashMap<K, V>,查询效率与内存占用最平衡,非线程安全 java.util.TreeMap <K, V>/HashTable<K, V>

常用操作方法 V put(K key, V value),

保存键值对 V get(K key),基于key获取对应的value,如果value不存在,返回null

default V getOrDefault(Object key, V defaultValue),获取对应的value,没有则使用默认值。但不会自动存入

V remove(Object key)

boolean containsKey(Object key)

boolean containsValue(Object value)

int size()

boolean isEmpty()

putAll()/clear()

Map没有,基于index索引的操作

Map没有,继承Iterable接口,不支持foreach语句遍历*

测试用List集合 2城4人

import java.util.ArrayList;
import java.util.List;
public class User {
    public static final String HAERBIN = "哈尔滨";
    public static final String BEIJING = "北京";
    private int id;
    private String name;
    private String city;
    private static final List<User> Users = create();
    public User(int id, String name,String city) {
        this.id = id;
        this.name = name;
        this.city = city;
    }
    private static List<User> create() {
        User u = new User(1,"BO",User.HAERBIN);
        User u1 = new User(2,"SUN",User.BEIJING);
        User u2 = new User(3,"ZHANG",User.BEIJING);
        User u3 = new User(4,"LIU",User.HAERBIN);
        List<User> users = new ArrayList<>();
        users.add(u);users.add(u1);
        users.add(u2);users.add(u3);
        return users;
    }
}
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
public class User {
    public static final String HAERBIN = "哈尔滨";
    public static final String BEIJING = "北京";
    private int id;
    private String name;
    private String city;
    private static final List<User> Users = create();
    public User(int id, String name,String city) {
        this.id = id;
        this.name = name;
        this.city = city;
    }
    private static List<User> create() {
        User u = new User(1,"BO",User.HAERBIN);
        User u1 = new User(2,"SUN",User.BEIJING);
        User u2 = new User(3,"ZHANG",User.BEIJING);
        User u3 = new User(4,"LIU",User.HAERBIN);
        List<User> users = new ArrayList<>();
        users.add(u);users.add(u1);
        users.add(u2);users.add(u3);
        return users;
    }
    public int getId() {
        return id;
    }
    public void setId() {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName() {
        this.name = name;
    }
    public String getCity() {
        return city;
    }
    public void setCity() {
        this.city = city;
    }

    public static void main(String[] args) {
        List<User> USERS = create();
        Map<Integer,User> uMap = new HashMap<>();
        for (User u : USERS) {
            uMap.put(u.getId(),u);
        }
        System.out.println(uMap.size());
        User u = uMap.get(1);
        System.out.println(u.getCity());
    }
}

需求:以居民ID为key,居民本身为value

需求:以城市名为key,以对应的居民为value,分组置于Map

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
public class User {
    public static final String HAERBIN = "哈尔滨";
    public static final String BEIJING = "北京";
    private int id;
    private String name;
    private String city;
    private static final List<User> Users = create();
    public User(int id, String name,String city) {
        this.id = id;
        this.name = name;
        this.city = city;
    }
    private static List<User> create() {
        User u = new User(1,"BO",User.HAERBIN);
        User u1 = new User(2,"SUN",User.BEIJING);
        User u2 = new User(3,"ZHANG",User.BEIJING);
        User u3 = new User(4,"LIU",User.HAERBIN);
        List<User> users = new ArrayList<>();
        users.add(u);users.add(u1);
        users.add(u2);users.add(u3);
        return users;
    }
    public int getId() {
        return id;
    }
    public void setId() {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName() {
        this.name = name;
    }
    public String getCity() {
        return city;
    }
    public void setCity() {
        this.city = city;
    }

    public static void main(String[] args) {
        List<User> USERS = create();
        List<User> hList = new ArrayList<>();
        hList.add(USERS.get(0));
        hList.add(USERS.get(3));
        List<User> bList = new ArrayList<>();
        bList.add(USERS.get(1));
        bList.add(USERS.get(2));
        Map<String,List<User>> uMap = new HashMap<>();
        uMap.put(User.HAERBIN,hList);
        uMap.put(User.BEIJING,bList);
        System.out.println(uMap.size());
        for (User u : uMap.get(User.BEIJING)) {
            System.out.println(u.getName());
        }
    }
}

HashMap

基于hashCode()方法获计算key的hash值

创建Node数组,基于加载因子扩容,平衡内存占用与执行效率

创建Node对象,基于key的hash值+算法,计算Node对象在数组中的索引。Node对象中,封装Key/value对象,相同位置以及存在Node对象

减少数量小于等于6个,基于单向链表保存

增加数量大于等于8个,基于红黑树保存

数量改变时,转换数据结构

获取时,基于key的hash值+算法,直接在Node数组获取对应的Node对象,基于具体数据结构(单向链表/红黑树)进一步获取value对象

The Set Interface

java.util.Set<E>

Set集合,不包含重复元素(数学中集合的抽象)

Set接口,只包含继承自Collection方法,并包含重复元素的校验  

基本实现类

java.util.HashSet<E>,元素无序(底层基于HashMap确定元素是否重复) java.util.LinkedHashSet<E>,元素有序

java.util.TreeSet <E>,元素有序

无论使用有序/无序实现,均无基于索引的操作方法

无序 则无基于索引的获取元素方法

遍历时元素无序输出

Iterators

java.util.Iterator<E>

Iterator接口。迭代器,允许遍历集合,并根据需求选择性地从集合中移除元素

不同集合类型,的不同数据结构的实现类,有不同的迭代器实现,但仅需面向Iterator接口完成遍历与移除

Iterator<E> iterator()方法,Collection接口方法,获取集合对象的迭代器

Immutable Collection

如果一个对象的状态在构造后不能改变,则该对象被认为是不可变的(immutable)

不可变集合是线程安全

因不可变集合的结构不变,构造时速度更快,消耗的内存空间更小

即,不可变集合的结构不可变(长度不可变),一旦创建即不可添加/移除元素,如需改变结构必须创建新的集合(类似数组)

但,不可变集合中元素可以替换,元素对象属性值可以改变

List.of()/Set.of()/Map.of() ,返回空集合对象

List.of(elements…)/Set.of(elements…)/Map.of(K, V, ….),类似直接创建指定元素的数组,直接基于集合元素创建相应集合对象

集合长度不可变 但元素对象中封装的数据可变

null 是所有引用类型的默认值

但不可变类型集合不允许包含null

Functional Programming

函数式编程,是一种构建程序结构的编程范式。是一种与面向对象程序设计,完全不同的应用程序设计思想

在函数式编程中,函数的输出应且仅应依赖于函数的本身。即,函数的执行,不应依赖于函数外部数据的状态(闭包)

函数式编程与面向对象编程是不同场景下,分析设计应用的思考方式,无优劣之分

Lambda Expressions

通过函数式接口,声明一个函数(一个包含约束函数参数/返回类型的抽象方法的接口)(后期讨论)

通过Lambda表达式,实现/描述一个函数(函数式接口的实现)

函数有自己的参数列表,函数体以及返回值。且具有

闭包。独立于类

匿名。实现时无需声明修饰符/返回类型/名称(Think More, Write Less)

传递。可以像引用类型变量一样声明,像对象一样传递

Lambda表达式语法

(arg1, arg2) -> expression

(arg1, arg2) -> {body}

参数列表,当参数为空时,需声明空括号;当只有一个参数时,可省略括号;参数类型可省略,编译器自动完成类型推导

Lambda 表达式的函数体,可包含0或多条语句

函数体,只有一条语句的表达式,可省略{}号;包含一条以上语句,必须包含在括号中(代码块);返回类型必须匹配;没有可不声明返回值

有参数无返回值的函数 基于语句,也可省略{}

有2参数有返回值的函数 多条语句,必须使用{} 必须声明返回结果

Processing Data with Streams

java.util.stream.Stream<T>接口

鉴于Java对集合操作的复杂性,Java8中引入Stream API,用于操作处理集合中的元素

集合是存储元素对象的容器

而Stream(集合流),并不是存储元素的数据结构,而是操作集合元素的管道

商品(元素)从仓库(集合)取出,经商品包装流水线(集合流)操作,达到出厂销售的结果

Stream操作的是集合中的元素,而非集合本身。因此,将创建新集合聚合Stream操作的结果,而不影响源集合结构

Stream仅会对流过的元素操作一次(流走了)(与Iterator的游标相似)

因此,必须生成一个新Stream才能继续操作

Stream上的操作会被延迟处理,即针对一个集合的多次操作会被优化后执行,从而提高执行效率

通过Collection接口中stream()方法获取当前集合的Stream对象

Terminal Operations

Terminal Operations。终止操作,终止stream操作处理,消费stream操作产生的结果

collect():聚合在stream中间操作的结果

forEach():迭代stream的每个元素

Collectors(java.util.stream.Collectors)类,用于操作聚合结果的工具类

groupingBy()/mapping()

toList()/toSet()/toMap()

单条语句 可进一步简化省略{}

Intermediate Operations

Intermediate Operations。中间操作,对集合中元素的执行的具体操作

Stream filter():基于参数选择stream中的元素,过滤

Stream map():基于stream操作映射为新的类型,映射

Stream sorted():排序stream中的元素,排序

Long count():获取stream中元素个数,计数

中间操作执行后,将结果置入一个新Stream,从而允许基于新Stream实现后续操作,形成基于Stream的操作链

import java.util.List;
import java.util.stream.*;
public class Apple {
    public enum Color {
        RED,GREEN
    }
    private int id;
    private Color color;
    private int weight;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Color getColor() {
        return color;
    }

    public void setColor(Color color) {
        this.color = color;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    public Apple(int id, Color color, int weight) {
        this.id = id;
        this.color = color;
        this.weight = weight;
    }
    private static final List<Apple> APPLES = create();

    private static final List<Apple> create() {
        Apple a = new Apple(1, Color.RED, 200);
        Apple a2 = new Apple(2, Color.GREEN, 250);
        Apple a3 = new Apple(3, Color.RED, 260);
        Apple a4 = new Apple(4, Color.GREEN, 230);
        return List.of(a,a2,a3,a4);
    }
    private static List<Apple> filter(Apple.Color c) {
        return APPLES.stream()
                .filter(a->a.getColor() == c)
                .toList();
    }
}

Stream<T> map()。映射Stream中元素,基于条件将元素映射为新类型元素

    private static void filter(Apple.Color c) {
         APPLES.stream()
                .map(a -> a.getWeight())
                .collect(Collectors.toList())
                .forEach(i -> System.out.println(i));
    }

只要2个集合中有匹配属性 即符合过滤条件

        Apple a1 = new Apple(1, Color.RED, 200);
        Apple a2 = new Apple(5, Color.GREEN, 240);
        List<Apple> newApples = List.of(a1,a2);
        List<Apple> oldApples = APPLES;
        List<Apple> apples2 = newApples.stream()
                .filter(a -> oldApples.stream()
                        .anyMatch((oa -> oa.getId() == a.getId())))
                .toList();

需求:将所有苹果的颜色映射为新集合

聚合为Set集合 过滤相同颜色

groupingBy(),基于给定数据以Map<K, List<T>>分组集合

需求:基于颜色分组苹果

toMap(K, V),基于给定键值,以Map<K, V>分组集合

需求:基于ID分组苹果

等价

支持Map forEach()方法,可直接获取每次遍历元素的键与值

removeIf

Collection接口中定义。移除符合函数表达式的元素。底层依然基于Iterator迭代器实现

极简洁优雅的实现了元素的移除 使代码关注于对集合的业务操作本身 而非游标/位置/移动等非业务逻辑操作

Functional Interfaces

函数式接口,能且只能包含1个抽象方法的接口

函数式接口中,仅声明定义函数的参数/参数类型/返回类型,使用时具体实现

@FunctionalInterface(java.lang.FunctionalInterface)注解,声明该接口为函数式接口,只要是只包含一个抽象方法的函数式接口,可以省略;当接口中定义多于1个抽象方法时,无法编译 Java定义了多个函数接口,供集合Stream等使用

Stream仅操作源集合中的元素,基于操作产生新集合,不改变源集合结构。即,源集合/新集合,均持有该元素对象的引用

filter(),

过滤 map(),

映射 sorted(),

排序 collect(),

聚合

操作均返回Stream,因此可以链接形成一条单向的管道 支持多线程并发处理,而无需显式创建线程

Optional

java.util.Optional<T> 为解决空引用异常引入的,用于封装单值元素的容器

即,基于Optional提供的一系列方法,操作封装在Optional容器中的,可能引起空引用的元素对象

创建容器 ofNullable() / of()

执行操作,基于容器是否为空,执行操作,无返回值 ifPresent() / ifPresentOrElse()

中间操作,将操作结果,置于新Optional容器以执行后续操作,结果为空,也会返回相同类型的空容器 filter() / map() / or()(需手动创建容器注入)

获取操作,获取容器中对象 orElse() / orElseGet() / get()

判断方法,判断当前容器是否为空 isEmpty() / isPresent()

Optional<T> Optional.of(T value),基于必不为空对象,创建optional容器,注入为空元素将抛出NullPointerException异常

Optional<T> Optional.ofNullable(T value),基于可能为空的对象,创建optional容器

public class Soundcard {
    private USB usb;

    public Soundcard(USB usb) {
        this.usb = usb;
    }

    public USB getUsb() {
        return usb;
    }

    public void setUsb(USB usb) {
        this.usb = usb;
    }
}
public class USB {
    public USB(String version) {
        this.version = version;
    }

    private String version;

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }
}
import java.util.Optional;
public class Computer {
    private Soundcard soundcard;

    public Soundcard getSoundcard() {
        return soundcard;
    }

    public Computer(Soundcard soundcard) {
        this.soundcard = soundcard;
    }

    public void setSoundcard(Soundcard soundcard) {
        this.soundcard = soundcard;
    }

    public static String getVersion0(Computer com) {
        String version = com.getSoundcard().getUsb().getVersion();
        return version == null ? "UNKNOW" : version;
    }

    private static void getIFPresent(USB usb) {
        Optional.ofNullable(usb)
                .ifPresent(u -> {
                    System.out.println(u.getVersion());
                });
        Optional.ofNullable(usb)
                .ifPresentOrElse(u -> {
                    System.out.println(u.getVersion());
                }, () -> {
                    System.out.println("usb为空");
                });
    }

    private static void filter(USB usb) {
        Optional.ofNullable(usb)
                .filter(u -> "3.0".equals(u.getVersion()))
                .ifPresent(u -> {
                    System.out.println(u.getVersion());
                });
        Optional.ofNullable(usb)
                .filter(u -> !"UNKNOW".equals(u.getVersion()))
                .or(() -> Optional.of(new USB("1.1")))
                .map(USB::getVersion)
                .ifPresent(System.out::println);
        String v2 = Optional.ofNullable(usb)
                .map(USB::getVersion)
                .orElse("UNKNOW");
        System.out.println(v2);
    }
}

  • 21
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值