Java第一阶段(day12)集合

新增(自动封装)       查询

数据在数据库在缓存

集合:
  将多个数据(元素)存储在一起。也是一个容器。
  理论上存储任意多个"不同数据类型"的数据。 只能存储 "引用数据类型的数据"
  底层已经封装好了空间不足或者移动位置等这些操作。
  //实际开发中  存储还是相同类型的数据(避免出现类型转换的异常)
 
  
数组: 
  一个容器。存储相同数据类型指定length个数据。
  空间固定   数据类型必须相同。
  数组可以存储什么类型的数据?
      int[] String[] Bike[] "所有的类型都可以。"
  优势:
    查询/修改性能最快。index
  弊端:
    新增/删除性能比较低。
    空间固定----> 一值新增 手动扩容
    数据是连贯的---->后面的数据向左移动位置

数组  VS  集合
相同点:容器。存储很多元素/数据。
不同点:
   数组: 空间固定   数据类型必须相同
   集合: 空间可变   数据类型可以相同

1. 集合分类

Collection  VS Map

元素

元素是否可重复

Collection

存储的是单值

元素(一次存储一个)

具体看子级List/set

Map

存储的是一组元素(key-value)

key必须唯一  value可重复的

Java第一阶段(day12)集合_第1张图片

2. Collection

元素是否有序(索引位置)

元素是否可重复

List

有序

可重复

Set

无序

不可重复

2.1 常用方法

方法

功能

boolean``add(E e)`

将指定的数据添加集合中

void``clear()

清空集合里面所有的元素

boolean``contains(Object o)

判断集合里面是否包含指定的数据

boolean``isEmpty()

判断集合是否是空

Iterator``iterator()

获得集合对象的迭代器对象(遍历集合元素)

boolean``remove(Object o)

删除集合里面元素

int``size()

获得集合元素个数

Object[]``toArray()

集合转数组

T[]``toArray(T[] a)

default boolean``removeIf(Predicate filter)

删除满足条件的多个元素

Stream``parallelStream()

获得集合对象的并行化Stream对象

default Stream``stream()

获得集合串行化Stream对象

default void``forEach(Consumer action)

(遍历集合元素)

Iterator

boolean hasNext()  判断指针/光标后面是否有更多的数据需要迭代】

E next() 获得指针之后的元素数据

default void remove()  
从底层集合中删除此迭代器返回的最后一个元素(可选操作)。

2.2 使用方法

 private static void demo1() {
        //创建集合对象 增删改查集合元素
        Collection collection = new ArrayList();
        //System.out.println(collection.isEmpty());
        System.out.println(collection);//[]
        //1.新增元素---> 存储不同类型的数据
        collection.add(100);
        collection.add("hello");
        collection.add("hello");
        collection.add("hello");
        collection.add(true);
        collection.add(null);
        collection.add(new UserInfo());
        collection.add(100.123);

        System.out.println(collection);
        System.out.println("集合的元素个数:" + collection.size());

        //2.删除集合元素
       /* collection.remove("hello");
        collection.remove("hello");
        collection.remove("hello");*/
        //循环 遍历集合里面的每个元素  删除满足条件的数据
        /*collection.removeIf(new Predicate() {
            @Override
            public boolean test(Object data) {
                return Objects.equals(data, "hello");
            }
        });*/

        //面向函数式编程----> 面向方法编程---->重写的那个方法
        //lamda:  ()->{}
        //类名/引用::方法名
        //collection.removeIf(a -> Objects.equals(a, "hello"));
        //collection.removeIf("hello"::equals);

        //判断集合里面是否包含指定的数据
        //System.out.println(collection.contains(100));
        //清空集合元素
        //collection.clear();

        //集合转数组
        //Object[] array = collection.toArray();
        //System.out.println(Arrays.toString(array));
        System.out.println(collection);
        System.out.println(collection.size());
    }

2.3 遍历集合元素

private static void demo2() {

    Collection collection = new ArrayList();
    collection.add(1);
    collection.add(10);
    collection.add(2);
    collection.add("abc");
    collection.add(null);

    //循环遍历输出集合里面的每个元素数据
    //增强for
    /*for(Object data:collection){
        System.out.println(data);
    }*/
 /*  //1.获得集合对象的迭代器对象
    Iterator it = collection.iterator();//集合里面的所有的数据都在it
    //2.判断指针之后是否有更多的数据需要迭代
    while (it.hasNext()) {
        //3.获得指针之后的数据
        Object data = it.next();
        System.out.println(data);
    }*/

    //1.8+  forEach
    /*collection.forEach(new Consumer() {
        @Override
        public void accept(Object obj) {
            System.out.println(obj);
        }
    });*/

    //使用lamda替换上面的匿名内部类
    //::
    // collection.forEach(o->System.out.println(o));
    collection.forEach(System.out::println);

    //遍历Collection:
    //1.增强for循环
    //2. iterator()
    //3. forEach();

2.4 集合_泛型

public static void main(String[] args) {

    //使用泛型修饰集合
    //泛型+集合: 限定集合元素数据类型。 只存储一种类型的数据。
    Collection nameCollection = new ArrayList<>();
    //泛型只在编译期间有效 在运行期间其实泛型还是Object---->反射  泛型擦除
    nameCollection.add("张三");
    nameCollection.add("李四");
    nameCollection.add("王五");
    nameCollection.add("赵六");

    //遍历集合元素
    /*for (String name : nameCollection) {
        System.out.println(name);
    }*/

    //nameCollection.forEach(System.out::println);
   /* Iterator it = nameCollection.iterator();
    while (it.hasNext()) {
        String str = it.next();
        System.out.println(str);
    }*/

    //nameCollection.remove("张三");
    //nameCollection.removeIf("张三"::equals);
    System.out.println(nameCollection);

    //集合转数组
    //无参的toArray()不推荐  返回值类型Object[]  需要进行手动类型转换
    /*Object[] array1 = nameCollection.toArray();
    for (Object o : array1) {
         String s= (String) o;
    }*/
    // T[] toArray(T[] a);
    //集合转数组: 推荐数组的空间  0
    //length>size 有很多null  很容易NPE  空间内存浪费
    //size0 并发的时候 多次将集合转数组  GC进行回收无用的数组引用
    //length==0
    String[] strings1 = nameCollection.toArray(new String[0]);
    System.out.println("strings1:" + Arrays.toString(strings1));
    
}

3. 泛型<>

  T  K  V 一般都是使用 A-Z。 限定泛型里面参数化的数据类型。

只能是引用数据类型。

作用:  实现类型的自动转换。

泛型修饰类   ----> 泛型类 
泛型修饰方法  ----> 泛型方法
泛型修饰接口 ---->  泛型接口

3.1 问题(使用变量维护不定类型的数据)

@Data
public class MyClass {

    private Integer id;
    private String name;

    //使用data维护了一个不定类型数据
    private Object data;
}

 private static void demo1() {
        //创建MyClass类对象 对属性进行取值以及赋值
        MyClass myClass = new MyClass();
        myClass.setId(1001);
        myClass.setName("zhangsan");
        myClass.setData(100);


        //每次获取数据的时候  都要手动进行类型的强制转换
        //有可能会出现类型转换的异常
        Integer id = myClass.getId();
        String name = myClass.getName();
        Integer data = (Integer) myClass.getData();
        data = data + 100;

        MyClass myClass1 = new MyClass();
        myClass1.setData("hello");
        String d = (String) myClass1.getData();
    }

可以满足使用data变量维护不同类型的数据。但是每次获得数据的时候 都要进行类型的强制转换  进而实现其他的功能

在这种情况下:

  • 就会有可能出现类型转换的异常
  • 每次都进行手动的转换  代码不是很优雅

3.2 泛型类解决

目标:  不使用类型的强制转换      实现类型的自动转换    。

使用泛型进行解决。

//MyClass 代表是一个泛型类。T:参数化类型
//不清楚T的真实的数据类型。Object
@Data
public class MyClass {

    private Integer id;
    private String name;

    //使用data维护了一个不定类型数据
    private Object data;

    //参数化数据类型---->指定好的规则  A-Z    T  E  K V
    private T data1;

    //实例方法: 对象/引用.方法
    //参数化到底是什么类型?  创建对象的时候 指定的参数化类型具体是什么类型
    public T a(T t) {
        return null;
    }
}

public static void main(String[] args) {

    //需求: "实现类型的自动转换"
    //一般类  接口是泛型类或者泛型接口   一般都会指定参数化类型
    MyClass myClass = new MyClass<>();
    myClass.setData1("hello");
    String data1 = myClass.getData1();

    MyClass myClass1 = new MyClass<>();
    myClass1.setData1(100);
    Integer data11 = myClass1.getData1();


}

3.3 泛型方法

在任意一个类里面  都可以使用泛型修饰方法。

使用参数化类型修饰方法  这个方法就是泛型方法。

//K:是一个参数化类型  不指定  默认Object
public class FanXingClass {
    private K a;

    //只是参数化类型 充当形参和返回值
    //K类型是固定的  只能是一种 创建对象的时候 指定的数据类型
    public K getA() {
        return a;
    }

    public void setA(K a) {
        this.a = a;
    }

    //泛型方法
    //T: 不定类型
    //K: 创建对象的时候类型  m1是一个实例方法
    public  void m1(T abc, K bb) {
    }

    //静态方法
    //是否可以使用参数化数据类型? 创建对象的时候指定参数化类型
    //在泛型类里面 提供了很多静态的方法  里面使用参数化类型 这些静态方法都是"泛型方法"
    public static   void m2(K aaa) {

    }

    //一个泛型类里面 有很多的静态的方法 这个类一般都是工具类 用来封装一些的数据。


}

3.4 泛型接口

//后期有很多模块:  User  Product
// 新增 删除  修改  查询
//T  就是 User  Product....
class User {
}

class Product {
}
public interface BaseDao {
    void add(T t);

    void delete();

    void update();

    T findOne();

    Collection findAll();
}

class ProductDaoImpl implements BaseDao{

    @Override
    public void add(Product product) {
        
    }

    @Override
    public void delete() {

    }

    @Override
    public void update() {

    }

    @Override
    public Product findOne() {
        return null;
    }

    @Override
    public Collection findAll() {
        return null;
    }
}
class UserDaoImpl implements BaseDao{

    @Override
    public void add(User user) {
        
    }

    @Override
    public void delete() {

    }

    @Override
    public void update() {

    }

    @Override
    public User findOne() {
        return null;
    }

    @Override
    public Collection findAll() {
        return null;
    }
}

3.5 上限和下限

//使用 T extends 具体类型 规定参数化类型的上限
//只能:Number以及Number类型所有的子类
public class DemoTest {


    //?是一个通配符 统配任意一个类型
    //下限: ?只能是String以及String的父级类型。
    public static void a(Collection collection) {

    }
}

class Test3 {
    public static void main(String[] args) {

        Collection collection = new ArrayList<>();
        DemoTest.a(collection);

    }
}

3.List

有序可重复。  index:  0-size()-1

3.1 常用实现类

数据结构

性能

安全

ArrayList

动态数组

查询/修改最快     新增/删除慢

不安全

LinkedList

双向链表

新增/删除最快     查询/修改慢

不安全

Vector

动态数组

一般

安全 sync

CopyOnWriteArrayList

动态数组

很慢

安全 sync

链表:
   单向链表: 一个元素的引用会“记着下一个元素引用” 以及当前元素数据
   双向链表:  当前元素的引用会“记着下一个元素引用”  还会记着“上一个元素的引用”   
             以及当前元素数据

3.2 常用方法

方法

作用

void``add(int index, E element)

指定索引位置新增新的数据

E``get(int index)

获得指定索引数据

ListIterator``listIterator()

遍历list集合

E``remove(int index)

删除指定索引的元素数据

E``set(int index, E element)

修改指定索引的元素数据

default void``sort(Comparator c)

排序

List``subList(int fromIndex, int toIndex)

截取

3.3 使用方法

3.4 ArrayList

public class ArrayList
extends AbstractList
implements List, RandomAccess, Cloneable, Serializable

RandomAccess: 快速随机访问接口  标记接口   查询性能最快  O(1)
    使用普通循环遍历方式  性能最快。

1. 构造方法

ArrayList() 构造一个初始容量为十的空列表。  array.length

ArrayList(int initialCapacity) //推荐  10
initialCapacity:  存储集合元素个数/负载因子+1   扩容
    
ArrayList(Collection c)

transient Object[] elementData;//就是存储ArrayList集合元素数据  
 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

2. 分析add

 protected transient int modCount = 0;//维护操作集合元素次数。(add/remove)
 private int size;//维护集合元素的个数
 private static final int DEFAULT_CAPACITY = 10;//默认容量

public boolean add(E e) {
    modCount++;
    add(e, elementData, size);
    return true;
}

private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)//size的数据超出数组length的时候  
            elementData = grow();
        elementData[s] = e;
        size = s + 1;
    }

 private Object[] grow() {
        return grow(size + 1);//1
    }

 private Object[] grow(int minCapacity) {//1
        return elementData = Arrays.copyOf(elementData,
                                           newCapacity(minCapacity));
    }

private int newCapacity(int minCapacity) {//1
        // overflow-conscious code
        int oldCapacity = elementData.length;//获得原数组的长度  0
        int newCapacity = oldCapacity + (oldCapacity >> 1);
          //计算新数组的容量 1.5
        if (newCapacity - minCapacity <= 0) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return minCapacity;
        }
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }

3. 分析get

E elementData(int index) {
    return (E) elementData[index];
}

4. 分析set

public E set(int index, E element) {
    Objects.checkIndex(index, size);
    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

5. 分析remove

public boolean remove(Object o) {
    final Object[] es = elementData;
    final int size = this.size;
    int i = 0;
    found: {//找指定元素所在的索引位置
        if (o == null) {
            for (; i < size; i++)
                if (es[i] == null)
                    break found;
        } else {
            for (; i < size; i++)
                if (o.equals(es[i]))
                    break found;
        }
        return false;
    }
    fastRemove(es, i);//带着index  带着数组  快速删除元素数据
    return true;
}

private void fastRemove(Object[] es, int i) {
    modCount++;
    final int newSize;
    if ((newSize = size - 1) > i)
        System.arraycopy(es, i + 1, es, i, newSize - i);//真正删除操作
    es[size = newSize] = null;//Gc回收无用对象
}

3.5 LinkedList

元素  数据  节点

public class LinkedList
extends AbstractSequentialList
implements List, Deque, Cloneable, Serializable

LinkedList()

1. 分析add

public boolean add(E e) {
    linkLast(e);
    return true;
}

transient Node last;//null
 transient Node first;//null
transient int size = 0;

void linkLast(E e) {//100
    
    final Node l = last;//null
    final Node newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)//第一次新增数据的时候  将第一个节点对象赋值给first
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

手动编写双向链表。

private static class Node {//维护LinkedList底层的双向链表的数据结构
    E item;//数据
    Node next;//下一个节点的引用
    Node prev;//上一个节点的引用

    Node(Node prev, E element, Node next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

void linkBefore(E e, Node succ) {// e 新的元素数据  succ: 指定索引的节点对象
    // assert succ != null;
    final Node pred = succ.prev;//获得指定索引对象的上一个  
    final Node newNode = new Node<>(pred, e, succ);
    succ.prev = newNode;
    if (pred == null)//指定索引为0的时候
        first = newNode;
    else
        pred.next = newNode;
    size++;
    modCount++;
}

2. 分析get

Node node(int index) {
    // assert isElementIndex(index);

    if (index < (size >> 1)) {
        Node x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

3. 分析set

public E set(int index, E element) {
    checkElementIndex(index);
    Node x = node(index);
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}

4.分析remove

public boolean remove(Object o) {
    if (o == null) {
        for (Node x = first; x != null; x = x.next) {
            if (x.item == null) {
                unlink(x);
                return true;
            }
        }
    } else {
        for (Node x = first; x != null; x = x.next) {
            if (o.equals(x.item)) {
                unlink(x);//解除节点与节点之间的关系
                return true;
            }
        }
    }
    return false;
}

5. 特有方法

//将元素添加到首尾
linkedList.add(0, 101);
linkedList.addFirst(1);
linkedList.addLast(400);
linkedList.push(1111);

//获得链表首尾元素
System.out.println(linkedList.get(0));// Node.item
System.out.println(linkedList.getFirst());
System.out.println(linkedList.getLast());
System.out.println(linkedList.element());
System.out.println(linkedList.peek());
System.out.println(linkedList.peekFirst());
System.out.println(linkedList.peekLast());

//删除首尾元素
System.out.println(linkedList.remove());
System.out.println(linkedList.removeFirst());
System.out.println(linkedList.removeLast());
System.out.println(linkedList.poll());
System.out.println(linkedList.pollFirst());
System.out.println(linkedList.pollLast());
System.out.println(linkedList.pop());

3.6 Vector

线程安全。 synchronized

package com.javasm;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.TimeUnit;

/**
 * @author: Lisa
 * @className: VectorDemo
 * @description:
 * @date: 2022/3/2 16:11
 * @version: 0.1
 * @since: jdk11
 */
public class VectorDemo {


    //使用一个成员变量维护存储很多数据
    //每个线程共同操作同一个集合对象
    private static Vector list = new Vector<>();

    public static void main(String[] args) {
        //并发环境
        //创建10个线程  都操作同一个集合对象 list
        List threadList = new ArrayList<>(10);

        for (int i = 0; i < 10; i++) {
            threadList.add(new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    list.add(1);
                }
            }));
        }
        threadList.forEach(Thread::start);

        threadList.forEach(thread -> {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        //等待着前面10个子线程执行完毕---->等待着子线程死亡
        //获得集合里面的元素个数
        //让主线程休眠 5

        System.out.println(list.size());//10000
    }
}

3.7 CopyOnWriteArrayList

public boolean add(E e) {
    synchronized (lock) {//底层安全的实现方式
        Object[] es = getArray();
        int len = es.length;
        es = Arrays.copyOf(es, len + 1);
        es[len] = e;
        setArray(es);
        return true;
    }
}

3.8 迭代器模式

遍历期间不能删除。

4.Set

4.1常用实现类

常用的数据结构:  数组   链表  哈希表  红黑树

数据结构

元素是否可以为null

线程安全

HashSet

哈希表(底层就是HashMap 位桶+哈希+红黑树)

ok

不安全

LinkedHashSet

链表+哈希表(LinkedHashMap)

ok

不安全

TreeSet

红黑树(TreeMap)

不可以

不安全

CopyonWriteArraySet

数组

不可以

安全

4.2 常用方法

Set接口里面的方法与父接口Collection的功能一模一样。

4.3 HashSet

常用构造

HashSet() 
构造一个新的空集合; 背景HashMap实例具有默认初始容量(16)和负载因子(0.75)。 
HashSet(int initialCapacity) 
构造一个新的空集合; 背景HashMap实例具有指定的初始容量和默认负载因子(0.75)。

元素特征:

private static void testHashSet() {
        HashSet set = new HashSet<>();
        set.add(50);
        set.add(3);
        set.add(1);
        set.add(10);
        set.add(2);
        //无序:  元素没有索引  新增的顺序与遍历的顺序不一致的
        System.out.println(set);
        //遍历set集合
        /*for (Integer num : set) {
            System.out.println(num);
        }*/
       /* Iterator it = set.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }*/
        set.forEach(System.out::println);
    }

4.4 LinkedHashSet

常用构造

LinkedHashSet(int initialCapacity) 
构造一个具有指定初始容量和默认负载因子(0.75)的新的,空的链接散列集。

元素特征:  元素有序   新增顺序与遍历输出顺序是一致。

private static void testLinkedHashSet() {

        LinkedHashSet set = new LinkedHashSet<>();
        set.add(50);
        set.add(3);
        set.add(1);
        set.add(10);
        set.add(2);
        set.add(2);
        set.add(null);
        System.out.println(set);
    }

4.5 TreeSet

TreeSet集合元素的数据类型 必须有排序规则。

1.保证集合元素数据类型实现Comparable接口  内部比较器

2.自定义提供排序规则 实现Comparator  外部比较器

常用构造

TreeSet() 
    
TreeSet(Comparator comparator)

元素特征:  元素有序。 都会按照自然顺序排列。(升序/降序)

Comparable解决排序:

//类实现Comparable:  类与接口的耦合比较高  一般不使用内部比较器。推荐使用外部比较器
public class UserInfo implements Comparable {
     @Override
    public int compareTo(UserInfo o) {
        return this.age-o.age;
    }
}

TreeSet userInfoSet = new TreeSet<>();
userInfoSet.add(new UserInfo(1, "张三", 20));
userInfoSet.add(new UserInfo(2, "李四1", 180));
userInfoSet.add(new UserInfo(3, "李四1", 181));
userInfoSet.add(new UserInfo(4, "李四1", 118));
userInfoSet.add(new UserInfo(5, "李四1", 108));
userInfoSet.forEach(System.out::println);

Comparator解决排序:

public class UserInfo {}//类很单一

private static void testSet1() {
        //TreeSet: 集合元素数据类型必须实现Comparable  排序
        //数据唯一的问题: 只看排序的规则(哪个属性参与排序  元素唯一就看那个属性)。 与重写equals+hashcode没有关系。底层的数据结构是树结构。
        /*TreeSet userInfoSet = new TreeSet<>(new Comparator() {
            @Override
            public int compare(UserInfo o1, UserInfo o2) {
                return o2.getAge()-o1.getAge();
            }
        });*/
        //TreeSet userInfoSet = new TreeSet<>((user1,user2)->user1.getAge()-user2.getAge());
       /* TreeSet userInfoSet = new TreeSet<>(Comparator.comparing(new Function() {
            @Override
            public Integer apply(UserInfo userInfo) {
                return userInfo.getAge();
            }
        }).reversed());*/
        TreeSet userInfoSet = new TreeSet<>(Comparator.comparing(UserInfo::getAge).reversed());

        userInfoSet.add(new UserInfo(1, "张三", 20));
        userInfoSet.add(new UserInfo(2, "李四1", 180));
        userInfoSet.add(new UserInfo(3, "李四1", 181));
        userInfoSet.add(new UserInfo(4, "李四1", 118));
        userInfoSet.add(new UserInfo(5, "李四1", 108));
        userInfoSet.forEach(System.out::println);
    }

4.6 CopyonWriteArraySet

public boolean addIfAbsent(E e) {
    Object[] snapshot = getArray();
    return indexOfRange(e, snapshot, 0, snapshot.length) < 0
        && addIfAbsent(e, snapshot);
}

5. Map

存储的是一组数据。 key---value   key值必须唯一   value可以重复的。

在开发中:  key的类型一般就是String/Integer/Long

5.1 常用方法

static interface  Map.Entry   维护了map集合里面的每组数据。

方法

作用

V``put(K key, V value)

新增一组元素

V``remove(Object key)

根据key删除value  没有找到key返回null

boolean``remove(Object key, Object value)

根据指定的key/value删除元素

V``replace(K key, V value)

根据指定的key修改value

boolean``replace(K key, V oldValue, V newValue)

根据指定的key/value,使用新的value进行修改

V``get(Object key)

根据key获得value

V``getOrDefault(Object key, V defaultValue)

根据key获得value 找不到特定的key 返回值的是默认值

void``forEach(BiConsumer action)

遍历map集合元素

Set>``entrySet()

获得map集合里面 每组数据  都存储set集合中

Set``keySet()

获得map里面所有的key

boolean``containsKey(Object key)

5.2 使用方法

private static void demo1() {
    //创建map集合对象
    //使用map集合维护一个用户完整信息  id=20  name=张三  age=20  pass=1234
    Map userInfoMap = new LinkedHashMap<>();
    //1.新增数据----> key值重复 等价新值会覆盖旧的数据
    userInfoMap.put("id", 1001);
    userInfoMap.put("name", "张三");
    userInfoMap.put("age", 20);
    userInfoMap.put("pass", "1234");

    //2.删除 根据key删除  key是唯一
    //System.out.println(userInfoMap.remove("pass"));
    //System.out.println(userInfoMap.remove("pass", "12345"));

    //3.修改
    //System.out.println(userInfoMap.replace("pass", "12345"));
    //System.out.println(userInfoMap.replace("pass", "1234","12345"));

    //4.查询
    //System.out.println(userInfoMap.get("name1"));
    //前提: 集合存储相同类型的数据
    //System.out.println(userInfoMap.getOrDefault("name1", ""));

    //5.判断
    System.out.println(userInfoMap.isEmpty());
    System.out.println(userInfoMap.size());
    System.out.println(userInfoMap.containsKey("name"));

    System.out.println(userInfoMap);


}

5.3 遍历map

private static void demo2() {
    Map userInfoMap = new LinkedHashMap<>();
    userInfoMap.put("id", 1001);
    userInfoMap.put("name", "张三");
    userInfoMap.put("age", 20);
    userInfoMap.put("pass", "1234");

    //遍历map集合元素
    /* userInfoMap.forEach(new BiConsumer() {
            @Override
            public void accept(String key, Object value) {
                System.out.println(key + "----" + value);
            }
        });*/
    //userInfoMap.forEach((key, value) -> System.out.println(key + "----" + value));
    //jdk1.8之前的遍历方式

    /*Set> entrySet = userInfoMap.entrySet();//推荐
        //将map集合里面的每组数据都依次的封装成一个个的entry对象 再将entry对象存储set集合中
        for (Map.Entry entry : entrySet) {// map集合中每组数据  都在entry里面
            String key = entry.getKey();
            Object value = entry.getValue();
            System.out.println(key + "----" + value);
        }*/

    /* Set keySet = userInfoMap.keySet();//获得map集合里面每个key 存储set集合中
        for (String key : keySet) {
            System.out.println(key + "----" + userInfoMap.get(key));
        }*/


}

5.4 Map_案例

private static void demo3() {
    String str = "djhdf77gggaf6t3gdshgfsa";
    //使用map集合实现: 统计每个字符出现的次数  a=2
    Map map = new HashMap<>();
    //1.获得字符串里面的每个字符
    int length = str.length();
    for (int index = 0; index < length; index++) {
        String s = String.valueOf(str.charAt(index));//获得每个字符数据
        // V merge(K key, V value, BiFunction remappingFunction)
        map.merge(s, 1, Integer::sum);
        /*//判断这个s字符是否存在于map集合中
            Integer num = map.get(s);
            if (num != null) {
                num += 1;
            } else {
                num = 1;
            }
            //第一次存储字符
            map.put(s, num);
            //之前存储过相同的字符  获得之前的次数+1*/
    }
    System.out.println(map);

}

5.5 常用实现类

数据结构

key以及value是否可以为null

线程安全

HashMap

位桶+链表+红黑树

k/v都可以为null

LinkedHashMap

链表+哈希表

都可以为null

TreeMap

红黑树

key:不能为null  value:可以

HashTable

哈希表

都不能为null

是 syn

ConcurrentHashMap

哈希表

都不能为null

是 CAS

5.6 HashMap

HashMap() 
构造一个空的 HashMap ,默认初始容量(16)和默认负载系数(0.75)。 

HashMap(int initialCapacity) 
构造一个空的 HashMap具有指定的初始容量和默认负载因子(0.75)。

static final float DEFAULT_LOAD_FACTOR = 0.75f;
final float loadFactor;//0.75
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;//16
static final int MAXIMUM_CAPACITY = 1 << 30;
static final int TREEIFY_THRESHOLD = 8;//链表转数结构的阈值
static final int UNTREEIFY_THRESHOLD = 6;//树结构转链表结构
static final int MIN_TREEIFY_CAPACITY = 64;//链表转数结构的一个条件  数组的length
transient Node[] table;//位桶 数组 
int threshold;//执行再次扩容的阈值  0  12 

public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

1. put

解决hashmap的hash冲突的方案:
   1. hash一致 key值得数据一致   新的value会覆盖掉oldvalue
   2. hash一致 key的数据不同  使用单向链表解决。
       链表元素个数>=TREEIFY_THRESHOLD-1的时候  && length>64  将链表结构转树结构
   3.使用树结构TreeNode解决。

public V put(K key, V value) {// 1  1
    return putVal(hash(key), key, value, false, true);
}

//计算key的哈希值  采取的方式高16位的位运算  目的: 减少hash的碰撞
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node[] tab; Node p; int n, i;
    // n就是的数组长度 length   i维护索引值
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)//采取位运算求索引 
        tab[i] = newNode(hash, key, value, null);
    else {//解决hash冲突的问题
        
        Node e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;// key的hash一致 且key的数据一致  
        else if (p instanceof TreeNode)
            e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
        else {
            //key的hash是一样的   但是key的数据不同的
            //使用单向链表解决hash冲突的问题
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    //链表里面的元素个数>=7  数组的length>=64
                    //会将链表结构的数据转换成树结构的数据
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key 
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;//新的value覆盖旧的value
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)//新增元素的个数>阈值 执行扩容
        resize();
    afterNodeInsertion(evict);
    return null;
}

2. resize

第一次执行put的时候。

final Node[] resize() {
        Node[] oldTab = table;//旧数组
        int oldCap = (oldTab == null) ? 0 : oldTab.length;//旧容量
        int oldThr = threshold;//旧阈值
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            //第一次put的时候  执行else逻辑
            newCap = DEFAULT_INITIAL_CAPACITY;//16
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//12
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
        Node[] newTab = (Node[])new Node[newCap];
        table = newTab;
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                        ((TreeNode)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        Node loHead = null, loTail = null;
                        Node hiHead = null, hiTail = null;
                        Node next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

在HashMap里面  何时会执行扩容?

1. put:
    1. 第一次put的时候  初始化数组空间
    2. size>threhold  会执行再次扩容
2. 链表转树结构的时候:  treefyBin()
        
扩容:  2倍

6. Stream+lamda

是jdk1.8+之后新特性。作用: 操作集合Collection元素。聚合数据

使用lamda的原因: 简化 匿名内部类(接口)操作、接口必须是个@FunctionalInterface

6.1 获得Stream实例

private static void demo1() {
        //1.获得集合对象Collection
        List list = List.of(1, 2, 3, 4);

        //2.获得Stream的实例
        //Collection集合的方法
        Stream stream = list.parallelStream();//并行化流
        Stream stream1 = list.stream();//串行化stream
        //并行  VS 串行
        //计算机计算的能力。
        //案例: 1000个数字 求最大值。
        //串行: 类似于冒泡排序 max  有序
        //并行: 同时执行多个任务。 将1000个数据  放到多个任务中(多个线程)。
        //10   每个任务: 随机分配100个数据   10个最值

        //Stream stream2 = Stream.of(1, 2, 3, 4);

        //随机数 Random
        Random random = new Random();
        //随机获得5个随机的数字  1000-10000
        IntStream intStream = random.ints(5, 1000, 10000);
        //提供对于基本数据类型封装Stream---> 减少过多的拆箱+装箱
        intStream.forEach(System.out::println);

        //Arrays
        Stream stream2 = Arrays.stream(new Integer[]{1, 2, 3});
    }

6.2 常用的功能方法

1. distnict/sort

 private static void demo2() {
        List list = new ArrayList<>(1000);
        for (long i = 10000000; i >= 0; i--) {
            list.add(i);
        }
        //需求: 对list集合元素去重并升序/降序排列
        long begin1 = System.nanoTime();
//        TreeSet treeSet = new TreeSet<>();
//        list.forEach(treeSet::add);
        long begin2 = System.nanoTime();
//        System.out.println(begin2 - begin1);//3709702827  1036846933


        //Stream类似于Iterator
        //parallelStream 性能没有 stream 快
        begin1 = System.nanoTime();
        //Stream stream = list.stream();//list集合里面的数据都在stream
        //List list1 = stream.distinct().sorted().collect(Collectors.toList());//使用Stream 聚合数据

        List list1 = list.stream().sorted().distinct().collect(Collectors.toList());

        //是否可以再次使用stream? 无法使用第二次
        //System.out.println(stream.count());

        //获得运算之后的结果
        begin2 = System.nanoTime();
        System.out.println(begin2 - begin1);
    }

2. filter

 private static void demo4() {

        List list = List.of(10, 20, 40, 60, 60, 50);
        //留下来集合元素>30的数据  存储到另外一个集合里面
        List newList = new ArrayList<>(10);
        for (Integer num : list) {
            if (num > 30) {
                newList.add(num);
            }
        }
        System.out.println(newList);

        //Stream.filter 留下来满足条件的数据
        /*list.parallelStream().filter(new Predicate() {
            @Override
            public boolean test(Integer num) {//stream每个元素
                return num>30;
            }
        })*/

        List collect = list.parallelStream().filter(num -> num > 30).distinct().sorted().collect(Collectors.toList());
        System.out.println(collect);
    }

private static void demo5() {

        List userInfoList = new ArrayList<>(10);
        Collections.addAll(userInfoList,
                new UserInfo(1, 20, "jim"),
                new UserInfo(2, 20, "tom"),
                new UserInfo(3, 20, "zhangsan")
        );

        //查询名称里面包含m一个信息
        List collect = userInfoList.parallelStream().filter(userInfo -> userInfo.getName().contains("m")).collect(Collectors.toList());
        System.out.println(collect);


    }

3. map/peek

一对一

  private static void demo6() {
        List nameList = Arrays.asList("jim", "tom", "zhangsan", "lily", "lilei", "hanmeimei");
        //将集合里面的每个元素都装大写
        /*List upperNameList = new ArrayList<>(10);
        for (String name : nameList) {
            upperNameList.add(name.toUpperCase());
        }
        System.out.println(upperNameList);*/

        //Stream  一对一: Stream里面的每个元素都要执行相同的逻辑
        /*nameList.parallelStream().map(new Function() {
            @Override
            public String apply(String s) {
                return s.toUpperCase();
            }
        });*/

        //List collect = nameList.parallelStream().map(String::toUpperCase).collect(Collectors.toList());

        List collect = nameList.parallelStream().peek(new Consumer() {
            @Override
            public void accept(String s) {
                s = s.toUpperCase();
                System.out.println(s);
            }
        }).collect(Collectors.toList());
        System.out.println(collect);

    }

private static void demo7() {
    List userInfoList = new ArrayList<>(10);
    Collections.addAll(userInfoList,
            new UserInfo(1, 21, "jim"),
            new UserInfo(2, 22, "tom"),
            new UserInfo(3, 23, "zhangsan")
    );
    //所有人年龄+1
   /* List collect = userInfoList.parallelStream().map(new Function() {
        @Override
        public UserInfo apply(UserInfo userInfo) {
            userInfo.setAge(userInfo.getAge() + 1);
            return userInfo;
        }
    }).collect(Collectors.toList());*/
   /* List collect = userInfoList.parallelStream().peek(userInfo -> userInfo.setAge(userInfo.getAge() + 1)).collect(Collectors.toList());
    collect.forEach(System.out::println);*/

    //获得所有的用户name
    List collect = userInfoList.parallelStream().map(UserInfo::getName).map(String::toUpperCase).collect(Collectors.toList());
    System.out.println(collect);
} 
  

4. flatMap

多个Stream转换成 一个Stream

 private static void demo8() {

        List list1 = List.of(1, 3, 5, 3, 8, 10);
        List list2 = List.of(11, 32, 53, 3);
        List list3 = List.of(100, 37, 58, 39, 8, 10);

        //3个数据源
        //将3个集合里面的元素都存储到一个集合中(去重/排序)
        //"将3个集合里面的所有的数据都放在一个Stream"
        //多个数据源数据存储到一个Stream
        /*Stream.of(list1, list2, list3).flatMap(new Function, Stream>() {
            @Override
            public Stream apply(List list) {
                return list.parallelStream();
            }
        })*/
        List collect = Stream.of(list1, list2, list3).flatMap(List::parallelStream).distinct().sorted().collect(Collectors.toList());
        System.out.println(collect);
    }

6.3 案例

public class Demo {


    private static List family;

    static {
        family = new ArrayList<>(10);

        //parentId=0 代表这个人就是顶级
        family.add(new Person(1, "张三丰", null, 0));


        family.add(new Person(2, "张翠山", null, 1));
        family.add(new Person(3, "张翠翠", null, 1));

        family.add(new Person(4, "张无忌", null, 2));
        family.add(new Person(5, "张天真", null, 2));

        family.add(new Person(6, "张翠花", null, 3));
        family.add(new Person(7, "张二狗", null, 3));

        family.add(new Person(8, "张三", null, 5));

        family.add(new Person(9, "张三四", null, 0));
        family.add(new Person(10, "张1", null, 9));


    }

    public static void main(String[] args) {
        //查询父节点下面的所有的子级的成员
       /* List personList = family.parallelStream()
                .filter(person -> person.getParentId().equals(0))//person集合里面的每个元素
                .peek(parent -> parent.setChild(getChild(parent)))//person1代表就是过滤转换parentId=0的对象
                .collect(Collectors.toList());
        personList.forEach(System.out::println);*/
        List list = new ArrayList<>(10);
        for (Person person : family) {
            if (person.getParentId().equals(0)) {
                person.setChild(getChildList(person));
                list.add(person);
            }
        }
        list.forEach(System.out::println);

        //找孩子---->对child属性赋值
    }

    private static List getChildList(Person parent) {
        List list = new ArrayList<>(10);
        for (Person person : family) {
            if (person.getParentId().equals(parent.getId())) {
                person.setChild(getChildList(person));
                list.add(person);
            }
        }
        return list;
    }

    private static List getChild(Person parent) {
        return family.parallelStream()
                .filter(person -> person.getParentId().equals(parent.getId()))//过滤下来二级节点的数据
                .peek(p -> p.setChild(getChild(p)))//递归
                .collect(Collectors.toList());
    }
}

7. 总结

public static void main(String[] args) {
        //1. List  VS Set
        //list: 有序可重复
        //set:无序不可重复

        //2 List集合常用的实现类?  区别?
        //ArrayList: 动态数组  查询/修改快  新增/删除慢   不安全
        //LinkedList: 双向链表  add/delete 快  get/set慢  不安全
        //Vector: 动态数组  一般  安全

        //3.Set集合常用的实现类?  区别?
        //HashSet ----> 底层就是一个HashMap   元素可以为null
        //LinkedHashSet---->底层就是一个LinkedHashMap  元素有序(新增顺序与遍历顺序一致)
        //TreeSet---->底层就是一个TreeMap  元素不可以为null(比较排序)

        //4. Map集合常用的实现类?  区别?
        //HashMap: 数组+链表+树   k/V可以为null  不安全
        //LinkedHashMap: 数组+链表+树   k/V可以为null  不安全
        //TreeMap:树  K不能为null  v可以为null  不安全
        //HashTable: 哈希表  K/V都不能为null  安全  同步方法
        //ConcurrentHashMap: 哈希表  K/V都不能为null  安全   CAS+同步代码块

        //5.HashMap底层是如何解决hash冲突的?(key的hash是一样的)
        //5.1 key的hash是一样  key的数据一致的 新的value值覆盖旧value  put
        //5.2 key的hash是一样  key的数据不同的
        //  挂在节点的next为null值得那个地方。p.next = new Node(key,hash,value,next)
        //  链表转树的前提:  TREEIFY_THRESHOLD && MIN_TREEIFY_CAPACITY
        //5.3 直接使用TreeNode解决hash冲突的问题

        //6.HashMap底层的扩容机制如何实现?  resize
        //new HashMap() 第一次put的时候 扩容  默认容量16    threshold = 0.75*16
        //再次扩容: table= 2*oldCap   threshold=2*oldThr
        //对之前旧数组里面的所有的元素执行存储:  基于提高性能 减少hash碰撞问题
        //假设有一个元素  100-next(null)  扩容之后 还是存储在原索引
        //next不为null 证明是链表 hash碰撞可能会产生很多次  将链表元素里面的数据分成2部分进行处理  一部分在lowIndex  一部分在HiIndex
        //lowIndex还是旧数组里面的原索引值  HiIndex=lowIndex+oldCap

        //7.HashMap何时会进行树结构转链表?
        //在resize的时候  进行对树结构的数据进行拆分的时候 在split的方法中
        //判断树结构里面TreeNode的节点的对象个数是否<=UNTREEIFY_THRESHOLD(6) 调用untreeify方法  将树结构转链表

        //8.HashMap里面重要的几个属性?
        //loadFactor=0.75
        //.....

}

8.练习

王者荣耀:
  英雄:
    id name 语录 List<皮肤>  price  分类
 
 //上架 优化(修改) 查看单个  查询所有英雄信息
 //查询指定类别里面所有的英雄
 
  皮肤:
    id name  price  皮肤关联英雄  皮肤状态
  // 查询指定英雄的所有皮肤
  // 上架  下架  查看所有皮肤

9.课堂练习

1. Collection常用方法

public class CollectionDemo {

    public static void main(String[] args) {
        demo1();
    }

    private static void demo3() {
        Collection collection = new ArrayList();
        collection.add(1);
        collection.add(10);
        collection.add(2);
        collection.add("abc");

        //在字符串的数据里面  拼接 “aaa”
        for (Object data : collection) {
            if (data instanceof String) {         //先判断,满足要求再转换以及拼接
                //调用concat  
                String s = (String) data;
                System.out.println(s.concat("aaa"));
            }
        }

    }


    private static void demo2() {

        Collection collection = new ArrayList();
        collection.add(1);
        collection.add(10);
        collection.add(2);
        collection.add("abc");
        collection.add(null);

        //循环遍历输出集合里面的每个元素数据
        //增强for
        /*for(Object data:collection){
            System.out.println(data);
        }*/
     /*  //1.获得集合对象的迭代器对象
        Iterator it = collection.iterator();       //集合里面的所有的数据都在it迭代器里
        //2.判断指针之后是否有更多的数据需要迭代
        while (it.hasNext()) {     //迭代器iterator里的功能方法hasNext(),判断光标/指针后面是否有更多元素需要迭代
            //3.获得指针之后的数据
            Object data = it.next();   //迭代器iterator里的功能方法next(),返回值为迭代中的下一个元素
            System.out.println(data);
        }*/

        //1.8之后的新方法(父接口Iterable提供的)  forEach
        /*collection.forEach(new Consumer() {   //集合的引用调forEach方法,形参类型Consumer(函数式接口)
            @Override
            public void accept(Object obj) {
                System.out.println(obj);
            }
        });*/

        //使用lamda替换上面的匿名内部类
        // collection.forEach(o->System.out.println(o));
        //类名/引用::方法   这里方法是println(打印并换行)   是System.out调的该方法
        collection.forEach(System.out::println);

        //遍历Collection:
        //1.增强for循环
        //2. iterator()
        //3. forEach();


        /*System.out.println(collection.size());
        System.out.println(collection.contains("abc"));

        //System.out.println(collection.remove(1));
        collection.removeIf(obj->Integer.valueOf(1).equals(obj));
        System.out.println(collection);*/


    }

    private static void demo1() {

        //创建集合对象 增删改查集合元素
        Collection collection = new ArrayList();
        //System.out.println(collection.isEmpty());
        System.out.println(collection);     //[]
        //1.新增元素---> 存储不同类型的数据
        collection.add(100);
        collection.add("hello");
        collection.add("hello");
        collection.add("hello");
        collection.add(true);
        collection.add(null);
        collection.add(new UserInfo());
        collection.add(100.123);            //Double类型

        System.out.println(collection);   //[100, hello, hello, hello, true, null, com.javasm.jihe.UserInfo@5594a1b5, 100.123]
        System.out.println("集合的元素个数:" + collection.size());

        //2.删除集合元素
       /* collection.remove("hello");          //用一下,删除一下   返回类型boolean,有就删,没有返false
        collection.remove("hello");
        collection.remove("hello");*/
        //removeIf 删除满足条件的多个数据(需要自定义规则)   底层还是循环遍历每个元素,删除满足条件的元素
        /*collection.removeIf(new Predicate() {         //removeIf的形参类型Predicate,一个函数式接口
            @Override
            public boolean test(Object data) {          //data是集合里的每个数据
                return Objects.equals(data, "hello");   //不要用data.equals(),data可能为空值,产生异常
            }
        });*/

        //面向函数式编程----> 面向方法编程---->重写的那个方法
        //lamda:  ()->{}
        //类名/引用::方法名
        //collection.removeIf((Object a)->{return Objects.equals(a, "hello");});
        //删除大括号,return    删除小括号,Object
        //collection.removeIf(a -> Objects.equals(a, "hello"));
        //重写的方法的形参类型Object和返回值类型boolean  要与调的Objects.equals(data, "hello");所一致,这里两个参数
        //改成collection.removeIf(a -> "hello".equals(a));   其返回值类型和形参与重写的一样,可再优化。 类名/引用::方法
        //collection.removeIf("hello"::equals);    //"hello"调的方法

        //判断集合里面是否包含指定的数据
        //System.out.println(collection.contains(100));
        //清空集合元素
        //collection.clear();

        //集合转数组
        //Object[] array = collection.toArray();
        //System.out.println(Arrays.toString(array));
        System.out.println(collection);
        System.out.println(collection.size());
    }
}

public class UserInfo {
}

2. 泛型

@Setter
@Getter
public class MyClass {     //一个字母,一个参数化类型,不指定,默认Object

    private Integer id;
    private String name;

    //使用data维护了一个不定类型数据,但每次拿的时候都要进行类型转换,看demo1()
    private Object data;    //类型不定的数据类型,一般叫参数化数据类型(提前制定好的规则,类比形参)

    //如下,用泛型去维护data
    private T data1;

    //实例方法: 对象/引用.方法
    //参数化到底是什么类型?  创建对象的时候 指定的参数化类型具体是什么类型
    public T a(T t) {        //这并非是泛型方法,没有标识<字母>     类型T(参数化数据类型),就看对象调它时再确定
        return null;
    }

}

//何时使用泛型?
//封装数据---->数据类型是不定的.

class Test {
    public static void main(String[] args) {

        //需求: "实现类型的自动转换"      这里用到泛型
        //一般类,接口是泛型类或者泛型接口,一般都会指定参数化类型,如下:
        MyClass myClass = new MyClass<>();
        myClass.setData1("hello");
        String data1 = myClass.getData1();

        MyClass myClass1 = new MyClass<>();
        myClass1.setData1(100);
        Integer data11 = myClass1.getData1();

    }

    private static void demo1() {
        //创建MyClass类对象 对属性进行取值以及赋值
        MyClass myClass = new MyClass();
        myClass.setId(1001);           //对Id赋值
        myClass.setName("zhangsan");
        myClass.setData(100);

        //每次获取数据的时候  都要手动进行类型的强制转换
        //有可能会出现类型转换的异常
        Integer id = myClass.getId();
        String name = myClass.getName();
        Integer data = (Integer) myClass.getData();     //这里类型强转
        data = data + 100;

        MyClass myClass1 = new MyClass();
        myClass1.setData("hello");
        String d = (String) myClass1.getData();
    }
}

泛型方法

public class FanXingClass {         //K:是一个参数化类型  不指定  默认Object
    private K a;

    public K getA() {    //只是参数化类型,充当形参和返回值.   K类型是固定的,只能是一种,创建对象的时候,指定的数据类型
        return a;
    }

    public void setA(K a) {
        this.a = a;
    }

    public  void m1(T abc, K bb) {   //泛型方法   K: 创建对象的时候类型  m1是一个实例方法
    }

    public static   void m2(K aaa) {   //在泛型类里面 提供了很多静态的方法  里面使用参数化类型 这些静态方法都是"泛型方法"

    }

    //一个泛型类里面 有很多的静态的方法 这个类一般都是工具类 用来封装一些的数据。

}

class Demo {

    public  void a(A a) {

    }
}

class Test2 {
    public static void main(String[] args) {

        FanXingClass myObj = new FanXingClass<>();
        myObj.setA("abc");

        myObj.m1(1000, "hello");
        /*myObj.m1(true);
        myObj.m1(new UserInfo());*/
        
    }
}

泛型接口

//后期有很多模块:  User  Product
//新增 删除  修改  查询
//T  就是 User  Product....
class User {
}

class Product {
}

public interface BaseDao {
    void add(T t);  //新增

    void delete();  //删除 

    void update();  //修改

    T findOne();   //查询

    Collection findAll();
}

class ProductDaoImpl implements BaseDao{

    @Override
    public void add(Product product) {

    }

    @Override
    public void delete() {

    }

    @Override
    public void update() {

    }

    @Override
    public Product findOne() {
        return null;
    }

    @Override
    public Collection findAll() {
        return null;
    }
}
class UserDaoImpl implements BaseDao{

    @Override
    public void add(User user) {

    }

    @Override
    public void delete() {

    }

    @Override
    public void update() {

    }

    @Override
    public User findOne() {
        return null;
    }

    @Override
    public Collection findAll() {
        return null;
    }
}

public class DemoTest {  //使用 规定参数化类型的上限
                                           //只能:Number以及Number类型所有的子类
   
    public static void a(Collection collection) {  //?是一个通配符 ,只写?,则通配任意一个类型
                                                                   //下限: 只能是String以及String的父级类型。
    }
}

class Test3 {
    public static void main(String[] args) {

        Collection collection = new ArrayList<>();
        DemoTest.a(collection);

    }
}

集合与泛型

public class CollectionDemo1 {

    public static void main(String[] args) {

        //使用泛型修饰集合
        //泛型+集合: 限定集合元素数据类型。 只存储一种类型的数据。
        Collection nameCollection = new ArrayList<>();
        //泛型只在编译期间有效 在运行期间其实泛型还是Object---->反射  泛型擦除
        nameCollection.add("张三");
        nameCollection.add("李四");
        nameCollection.add("王五");
        nameCollection.add("赵六");

        //遍历集合元素
        /*for (String name : nameCollection) {
            System.out.println(name);
        }*/

        //nameCollection.forEach(System.out::println);
       /* Iterator it = nameCollection.iterator();
        while (it.hasNext()) {
            String str = it.next();
            System.out.println(str);
        }*/

        //nameCollection.remove("张三");
        //nameCollection.removeIf("张三"::equals);
        System.out.println(nameCollection);

        //集合转数组
        //无参的toArray()不推荐,其返回值类型Object[],而集合里元素都是String,需要进行手动类型转换
        /*Object[] array1 = nameCollection.toArray();
        for (Object o : array1) {
             String s= (String) o;
        }*/
        //有参的toArray(T[] a),返。
        //数组长度length>集合元素个数size   数组有很多null,很容易NPE,空间内存浪费
        //size>length>0    并发的时候,多次将集合转数组,GC进行回收无用的数组引用
        //length==0
        String[] strings1 = nameCollection.toArray(new String[0]);     //集合转数组: 推荐数组的空间写0
        System.out.println("strings1:" + Arrays.toString(strings1));

    }
}

3.集合遍历期间能否删除

public class CollectionTest {

    public static void main(String[] args) {
        demo1();
    }
 private static void demo1() {
        Collection collection = new ArrayList<>();
        collection.add(100);
        collection.add(100);
        collection.add(100);
        collection.add(200);

        //collection.remove(100);    删除一个100
        collection.removeIf(Integer.valueOf(100)::equals);   //删除多个100


        //若遍历期间执行删除:jdk不是1.8的情况,此时不能使用removeIf,有三种:增强for ,迭代器,foeEach
        //增强for遍历期间能删除吗?
        /*for (Integer num : collection) {
            if (Objects.equals(num, 100)) {
                collection.remove(num);    //删除
            }                                        //此处添加break,不会报错,但只删除一个100
        }*/  //若不添加break,还是只删除一个,结果为[100,100,200],还会报错,ConcurrentModificationException
        //增强for循环底层还是用迭代器实现的
        //总结:增强for遍历期间不能删除,会触发并发修改异常,原因是底层规则导致。
        //集合里面有一个规则:底层提供了迭代器模式,fail-fast 快速失败 (底层有个变量modCount,维护该思想)
        //遍历期间执行删除: java.util.ConcurrentModificationException
     
        //迭代器能删除吗?
        //迭代器, 可以删除  不要使用集合类里面的remove  使用迭代器里面remove
        /*Iterator it = collection.iterator();
        while (it.hasNext()) {
            Integer num = it.next();
            if (Objects.equals(num, 100)) {
                it.remove();                   //collection.remove()   集合里的remove()方法不行,也会触发该机制
            }
        }*/

        //forEach在循环期间能删除吗?   底层用增强for做的,不行
        /*collection.forEach(num->{
            if (Objects.equals(num, 100)) {
                collection.remove(num);
            }
        });*/

        //总结:循环期间想删除,用迭代器或者removeIf
        //removeIf底层也是用迭代器实现的

        System.out.println(collection);
    }
}

4. 集合转数组

public class CollectionTest {

    public static void main(String[] args) {
        demo2();
    }
private static void demo2() {

    Collection collection = new ArrayList<>();
    collection.add("a");
    collection.add("b");
    collection.add("c");

    //集合转数组  collection.toArray()  一个无参的,两个带参的
    //Object[] objects = collection.toArray();      无参的,返的对象,也是字符串,但每次要转换操作,一般不用
    //String[] strings = collection.toArray(new String[0]);    带参的,返的字符串    数组长度给0
    /*collection.toArray(new IntFunction() {       //jdk1.11之后有的新方法,形参是个泛型数组
        @Override
        public String[] apply(int value) {
            return new String[0];          这里value的值就是0,可将0替换成value
        }
    });*/
    //简化:collection.toArray((int value)->new String[value]);
    //再次简化:这里不是类名/引用调方法,可将new一个String数组,数组长度为0可写成如下
    //String[] strings = collection.toArray(String[]::new);    结果为[a,b,c]

    String[] strings = collection.toArray(String[]::new);
    System.out.println(Arrays.toString(strings));
}

5. 数组转集合

public class CollectionTest {

    public static void main(String[] args) {
        demo3();
    }
private static void demo3() {
    Integer[] array = {1, 2, 3, 4};
    int[] arr = {1, 22, 1};

    //数组转集合  集合类没有该方法
    //操作数组元素的工具类Arrays有方法,但返回的是list
    List integers = Arrays.asList(array);    //这里是Integer
    List ints = Arrays.asList(arr);            //这里是int[],故数组转集合,不要给int[]型数组,集合里面只存储引用数据类型的数据

}

6. List使用方法:

public class ListDemo {

    public static void main(String[] args) {
        demo1();
    }
 private static void demo1() {

        List myList = new ArrayList<>();   //创建List集合对象
        myList.add(10);
        myList.add(1);
        myList.add(100);            //带一个参数的更好用,默认往后添
        //myList.add(1, 200);       //add()带两个参数的,指定索引去添。新增的范围: 0-size()   [10,200,1,100]
        //myList.add(0,1);
        //System.out.println(myList);   [1,200,10,1,100]

        //删除
        //myList.remove(Integer.valueOf(100));         //根据元素删,返回boolean
        //myList.remove(0);                            //根据索引删   索引范围:0-size()-1   返删除前的元素

        //修改
        //Integer oldNum = myList.set(0, 1000);        //根据索引修改,将第一个元素改成1000.  返回修改前的元素
        //System.out.println("oldNum:"+oldNum);

        //截取
        //List subList = myList.subList(0, 2);   //截取索引为0,1的元素(包头不包尾)    返回的是新的集合
        //System.out.println("myList:" + myList);
        //System.out.println("subList:" + subList);

        //System.out.println("--------------------");
        //截取之后,对新集合做增,删,修改等更新操作,都会还原到原集合上
        //subList.add(111);
        //subList.remove(0);
        //在截取之后,操作原集合,再操作截取集合,会出现异常    (只操作原集合,不操作截取后的新集合,没问题)
        //myList.add(200);
        //System.out.println("myList:" + myList);
        //System.out.println("subList:" + subList.toString());   //连输出新集合都不行,因为底层还会调用toString()

        //查询
        System.out.println(myList.get(2));     //查询指定索引的数据
    }
}

7. List遍历方式

public class ListDemo {

    public static void main(String[] args) {
        demo2();
    }

    private static void demo2() {
        //循环遍历list集合元素
        List list = new ArrayList<>();
        list.add("hello1");
        list.add("hello2");
        list.add("hello3");
        list.add("hello4");
        list.add("hello4");
        list.add(null);

        //1. 普通for循环(while,do-while都可以)  index----> 推荐 ArrayList
        //推荐用普通for循环,底层是数组
        /*int size = list.size();
        for (int index = 0; index < size; index++) {
            System.out.println(list.get(index));
        }*/

        //2.增强for  底层就是迭代器思想实现
        /*for (String s : list) {
            System.out.println(s);
        }*/

        //3.迭代器
        /*Iterator it = list.iterator();       //继承的父接口
        while (it.hasNext()) {
            System.out.println(it.next());
        }*/

        /*ListIterator listIterator = list.listIterator();
        while (listIterator.hasNext()) {
            System.out.println(listIterator.next());
        }

        System.out.println("----------------");
        //  1  2 3  4  代码从左到右走,光标开始在1左边
        while (listIterator.hasPrevious()) {               //逆向遍历,需要先正向遍历,使光标在4后面才行
            System.out.println(listIterator.previous());
        }*/

        //4.forEach
        list.forEach(System.out::println);
    }

8. List第一个实现类ArrayList

public class ArrayListDemo {

    public static void main(String[] args) {

    }

    private static void demo1() {
        //面试: 底层
        //使用普通方式创建集合对象
        ArrayList list = new ArrayList<>();    //无参构造, 初始化容器为10

        //增加
        Collections.addAll(list, "a", "b", "c", "e", "f");  //操作集合元素工具类  Collections   一次添加多个数据
        System.out.println(list);

        //删除
        //System.out.println(list.remove("b"));
        //数组维护集合的数据 index

        System.out.println(list.remove(0));
        System.out.println(list);

        /*for (int i = 1; i <= 10; i++) {
            list.add("hell0" + i);
        }
        list.add("abc");
        System.out.println(list.get(0));                // elementData[index]
        System.out.println(list.set(0, "zhangsan"));    // elementData[index] = zhangsan
        System.out.println(list);*/

    }
}

9. List第二个实现类LinkedList

public class LinkedListDemo {
    public static void main(String[] args) {
        testRemove();
    }

    private static void testRemove() {
        LinkedList linkedList = new LinkedList<>();
        linkedList.add(100);
        linkedList.add(105);
        linkedList.add(200);
        linkedList.add(300);

        //linkedList.remove(Integer.valueOf(105));
        //linkedList.remove(1);
        /*System.out.println(linkedList.remove());
        System.out.println(linkedList.removeFirst());
        System.out.println(linkedList.removeLast());
        System.out.println(linkedList.poll());
        System.out.println(linkedList.pollFirst());
        System.out.println(linkedList.pollLast());
        System.out.println(linkedList.pop());*/
        System.out.println(linkedList);

    }

    private static void testSet() {
        LinkedList linkedList = new LinkedList<>();
        linkedList.add(100);
        linkedList.add(101);
        linkedList.add(200);
        linkedList.add(300);

        System.out.println(linkedList.set(1, 105));
        System.out.println(linkedList);
    }

    private static void testGet() {

        LinkedList linkedList = new LinkedList<>();
        linkedList.add(100);
        linkedList.add(101);
        linkedList.add(200);
        linkedList.add(300);

        //System.out.println(linkedList.get(0));// Node.item
        //System.out.println(linkedList.getFirst());
        //System.out.println(linkedList.getLast());
        //System.out.println(linkedList.element());
        //System.out.println(linkedList.peek());
        //System.out.println(linkedList.peekFirst());
        //System.out.println(linkedList.peekLast());

//        System.out.println(linkedList.poll());
//        System.out.println(linkedList.pollFirst());
//        System.out.println(linkedList.pollLast());

        System.out.println(linkedList);
    }


    private static void testAdd() {
        LinkedList linkedList = new LinkedList<>();
        linkedList.add(100);
        linkedList.add(200);
        linkedList.add(300);
        linkedList.add(0, 101);
        linkedList.addFirst(1);
        linkedList.addLast(400);
        linkedList.push(1111);
        System.out.println(linkedList);
    }
}

10. 手动写一个双向链表(面试题)

private static class Node{    //维护LinkedList底层的双向链表的数据结构
    E item;   //数据
    Node next;  //下一个节点的引用
    Node prev;  //上一个节点的引用

    Node(Node prev,E element,Node next){
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

11. List第三个实现类Vector

//Vector是安全的,ArrayList是不安全的。
//下面的代码中,一个每次的结果都一样,另一个结果就不同
public class VectorDemo {

    private static Vector list = new Vector<>();   //不管用不用都会去创建一个容量为10的数组。对于ArrayList,不放数据在无参里不会在无参里创建容量,当执行新增时才会扩容

    public static void main(String[] args) {

        List threadList = new ArrayList<>(10);   //开辟10个空间  //使用一个成员变量维护存储很多数据,每个线程共同操作同一个集合对象

        for (int i = 0; i < 10; i++) {              //创建10个线程,都操作同一个集合对象list
            threadList.add(new Thread(() -> {       //将线程放在集合里
                for (int j = 0; j < 1000; j++) {    //每个线程又循环1000次
                    list.add(1);                    //每个线程都往里面放数据
                }
            }));
        }

        //上面一共11个线程,其中有一个主线程(main)。主线程里创建了10个新的线程
        threadList.forEach(Thread::start);      //启动子线程

        //等待子线程执行完毕
        threadList.forEach(thread -> {
            try {
                thread.join();                 //等待着前面10个子线程执行完毕(等待着子线程死亡)   用join()方法
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        //或者让主线程等等
        /*try {
            TimeUnit.SECONDS.sleep(5);   //让主线程休眠 5s(1s就已经很久了,cpu很快的)
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/

        System.out.println(list.size());//10000   并发走完之后,获得集合里的元素个数(主线程走完,才走新的线程)
    }
}
//只用安全的才能作为成员变量使用,不安全的只能当局部变量

12. List第四个实现类CopyOnWriteArrayList

public class CopyListDemo {

    public static void main(String[] args) {
        //底层是动态数组
        CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();
        list.add(100);
    }
}

13. Set

1. HashSet

public class SetDemo {

    public static void main(String[] args) {
           testHashSet();

 private static void testHashSet() {
        HashSet set = new HashSet<>();
        set.add(50);
        set.add(3);
        set.add(1);
        set.add(10);
        set.add(2);
        //无序: 元素没有索引  新增的顺序与遍历的顺序不一致的
        System.out.println(set);

        //遍历set集合(3种)
        /*for (Integer num : set) {
            System.out.println(num);
        }*/

       /* Iterator it = set.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }*/

        set.forEach(System.out::println);
    }
}
}

2. LinkedHashSet

private static void testLinkedHashSet() {
 //HashSet的一个子类     元素有序,新增顺序与遍历顺序一致(不同与父类的一点)

    LinkedHashSet set = new LinkedHashSet<>();
    set.add(50);
    set.add(3);
    set.add(1);
    set.add(10);
    set.add(2);
    set.add(2);
    set.add(null);
    System.out.println(set);
}

3. TreeSet

private static void testTreeSet() {
//元素特征:元素有序,都会按照自然顺序排列(从大到小或从小到大)
//故要求其集合元素的数据类型必须有排序规则,这需要以下两条要求满足其一:
    //1.保证集合元素数据类型实现Comparable接口
    //2.自定义提供排序规则,实现Comparator

    TreeSet treeSet = new TreeSet<>();
    Collections.addAll(treeSet, 100, 90, 91, 82, 88);   //若给汉字或者字符串,用第一个字符比较排序,不看后面
    System.out.println(treeSet);
    System.out.println(treeSet.first());   //拿第一个值(最大值或最小值 )
    System.out.println(treeSet.last());

  /*  TreeSet stringTreeSet = new TreeSet<>();
    Collections.addAll(stringTreeSet, "hello", "abcw", "kkk", "bcd");
    System.out.println(stringTreeSet);*/
}

在集合中存储对象的问题:(将多个用户对象存储集合中 List Set ,先不涉及TreeSet)

public class UserInfo {

    private int id;
    private String name;
    private int age;
}

private static void testList() {

    //将多个用户对象存储集合中  List
    List userInfoList = new ArrayList<>(10);     //空间给10
    userInfoList.add(new UserInfo(1, "张三", 20));    //匿名对象
    userInfoList.add(new UserInfo(1, "张三", 20));
    userInfoList.add(new UserInfo(1, "张三", 20));
    userInfoList.add(new UserInfo(1, "张三", 20));
    userInfoList.add(new UserInfo(1, "张三", 20));

    //此时集合里有5个数据
    System.out.println(userInfoList.size());

}

private static void testSet() {

    //将多个用户对象存储集合中  Set
    Set userInfoSet = new HashSet<>(16);
    userInfoSet.add(new UserInfo(1, "张三", 20));
    userInfoSet.add(new UserInfo(1, "张三", 20));
    userInfoSet.add(new UserInfo(1, "张三", 20));
    userInfoSet.add(new UserInfo(1, "张三", 20));
    userInfoSet.add(new UserInfo(1, "张三", 20));

    System.out.println(userInfoSet.size());   //也是5个数据
    userInfoSet.forEach(System.out::println);  //信息都一样
    //这样做没有符合Set集合元素特征(一样的东西,就一个就行了,不重复)
    //如何保证同一个,得保证哈希值一样,要重写哈希就得重写equals
    //总结:自定义类类型的数据存储set集合,为保证相同信息的元素就一个,要重写equals+hashcode
}

public class UserInfo {

    private int id;
    private String name;
    private int age;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        UserInfo userInfo = (UserInfo) o;
        return
                Objects.equals(name, userInfo.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

}
重写后,testSet()里就会输出一个对象。

对testList()没有影响。

若将对象放在TreeSet里:

private static void TreeSet1() {
    //先添加一个
    TreeSet userInfoSet = new TreeSet<>();
    userInfoSet.add(new UserInfo(1, "张三", 20));
    System.out.println(userInfoSet);   //添加不了,还会报异常(类型转换异常,用户类不能转成Comparable,用户类型里并没有compareTo方法)
    //原因:TreeSet集合的元素数据类型必须实现Comparable接口(它的元素要去排序)
    //这里要想让用户类存在该集合,必须要让用户类实现Comparable接口
    //存到集合里,是用户和用户比,是用户的什么和什么比,这个规则自己去定义
}

下面让用户类实现Comparable接口,并重写compareTo方法。

同时在TreeSet1()里放两个用户,让他们去比较:

@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo implements Comparable {        //这里是泛型接口,存放用户类的比较对象,用户和用户比,故存放用户

    private int id;
    private String name;
    private int age;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        UserInfo userInfo = (UserInfo) o;
        return
                Objects.equals(name, userInfo.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }


    @Override
    public int compareTo(UserInfo o) {       //重写比较规则
        return this.age-o.age;              //age是int型,不能调方法,故可用相减
    }
}

private static void TreeSet1() {

    TreeSet userInfoSet = new TreeSet<>();
    userInfoSet.add(new UserInfo(1, "张三", 20));
    userInfoSet.add(new UserInfo(2, "李四", 18));
    System.out.println(userInfoSet);  //[UserInfo{id=2, name='李四', age=18}, UserInfo{id=1, name='张三', age=20}]

}

其实:

private static void TreeSet1() {

    TreeSet userInfoSet = new TreeSet<>();
    userInfoSet.add(new UserInfo(1, "张三", 20));
    userInfoSet.add(new UserInfo(2, "李四1", 18));
    userInfoSet.add(new UserInfo(3, "李四1", 18));
    userInfoSet.add(new UserInfo(4, "李四1", 18));
    userInfoSet.add(new UserInfo(5, "李四1", 18));
    System.out.println(userInfoSet);  //[UserInfo{id=2, name='李四1', age=18}, UserInfo{id=1, name='张三', age=20}]
    //这里得原因:树的分支,set集合的保证元素唯一,唯一是结合排序规则走的。
    //故,TreeSet集合,在里面重写equals和hashCode没有意义。(底层和他俩没关系)
    //再次总结:TreeSet只看排序的规则(哪个属性参与排序  元素唯一就看那个属性)。与重写equals+hashcode没有关系。底层的数据结构是树结构。
}

不过:用类实现Comparable: 类与接口的耦合比较高 一般不使用内部比较器。(若不实现接口,相应的排序规则也没有了)

此时推荐使用外部比较器。

@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
//类实现Comparable:  类与接口的耦合比较高  一般不使用内部比较器。推荐使用外部比较器
public class UserInfo  {        

    private int id;
    private String name;
    private int age;
    
    }

private static void testSet1() {

    //外部比较器
   /* TreeSet userInfoSet = new TreeSet<>(new Comparator() {  //匿名内部类
        @Override
        public int compare(UserInfo o1, UserInfo o2) {
            return o2.getAge()-o1.getAge();
        }
    });*/
    //用lamda简化
    //TreeSet userInfoSet = new TreeSet<>((user1,user2)->user1.getAge()-user2.getAge());

    //还可以写成这样:
    /* TreeSet userInfoSet = new TreeSet<>(Comparator.comparing(new Function() {
        @Override
        public Integer apply(UserInfo userInfo) {
            return userInfo.getAge();
        }
    }).reversed());*/         //默认升序,想降序,在后面加.reversed()  反转一下
    //用lamda简化:
    TreeSet userInfoSet = new TreeSet<>(Comparator.comparing(UserInfo::getAge).reversed());

    userInfoSet.add(new UserInfo(1, "张三", 20));
    userInfoSet.add(new UserInfo(2, "李四1", 17));
    userInfoSet.add(new UserInfo(3, "李四1", 16));
    userInfoSet.add(new UserInfo(4, "李四1", 15));
    userInfoSet.add(new UserInfo(5, "李四1", 19));
    userInfoSet.forEach(System.out::println);                    //遍历输出
}

4. CopyonWriteArraySet 略

14. Iterator底层

//循环遍历期间能否删除元素?
public class ListDemo {

    public static void main(String[] args) {
        demo1();
    }

    private static void demo1() {

        /*Integer[] array = {100, 200, 300, 400};
        List list = List.of(array);*/             //1.9之后也可用List自己的方法快速往里放数据,也是数组转集合的方法
                                                           //其实of方法返的是一个视图
        ArrayList list = new ArrayList<>(10);
        Collections.addAll(list, 100, 200, 200, 300, 400);  //快速往里放数据

        //删除集合中所有的200的数据,用removeIf 或者遍历list:
        //遍历List的方式:普通for 增强for 迭代器 forEach
        System.out.println(list);
        //普通for      遍历期间可以删除
        /*for (int index = 0; index < list.size(); index++) {   //size必须动态去拿,不能写死,否则越界
            if (Integer.valueOf(200).equals(list.get(index))) {
                list.remove(index);           //删除这个,它的下一个元素自动左移,且size会减一
                if (Integer.valueOf(200).equals(list.get(index))) {       //保证相邻的元素也被删除
                    index--;                                      //如果再拿一次,又是200,索引要减一下
                }
            }
        }*/

        //增强for,底层是迭代器实现的。  遍历期间不可删除
        //forEach,遍历期间也不能删除元素
        //迭代器在遍历期间可以删除

        //增强for的底层模拟
        Iterator it = list.iterator();     //先拿迭代器
        for (; it.hasNext(); ) {
            Integer num = it.next();
            if (Integer.valueOf(200).equals(num)) {
                //list.remove(num);               //用集合的remove 只能删倒数第二个,删其他会报异常
                it.remove();        //用迭代器里的remove就可以全部删除。  但要明白删除集合元素,底层永远都是调用的集合类里面的remove的方法
            }
        }
        System.out.println(list);

        //Iterator模式: 循环遍历集合元素
        //集合类里提供了一个属性,叫modCount,与循环遍历功能相关。  这个属性并不是必须的,jdk的所有的集合框架中,都提供了这样的一个机制
        //该机制是在循环遍历期间,执行新增,删除等功能,可能会触发规则里的快速失败的机制
    }
}

15. Map使用方法

private static void demo1() {
    //创建map集合对象
    //使用map集合维护一个用户完整信息  id=20  name=张三  age=20  pass=1234
    //上面的id,20   name,张三   age,20  pass,1234都是key和value的一组键值对
    Map userInfoMap = new LinkedHashMap<>();
    //1.新增数据----> key值重复 等价新值会覆盖旧的数据
    userInfoMap.put("id", 1001);       //这些全部体现了多态
    userInfoMap.put("name", "张三");
    userInfoMap.put("age", 20);
    userInfoMap.put("pass", "1234");

    //2.删除 根据key删除,因为key是唯一
    //System.out.println(userInfoMap.remove("pass"));     返回值是删除的数据   若找不到要删除的元素,返null
    //System.out.println(userInfoMap.remove("pass", "12345"));   两个参,一个key,一个value。返回类型boolean。当两个参数完全匹配,才能删除成功

    //3.修改
    //System.out.println(userInfoMap.replace("pass", "12345"));   返回修改前的数据  修改的key不存在,返null
    //System.out.println(userInfoMap.replace("pass", "1234","12345"));  带三个参的和remove带两个参的一样

    //4.查询
    //System.out.println(userInfoMap.get("name1"));    key不存在,返null,null值若调equals,有风险出现空指针
    //为避免出现空指针,可用getOrDefault(),若找不到返""(空字符)。 前提: 集合存储相同类型的数据(value),这里有Integer。
    //System.out.println(userInfoMap.getOrDefault("name1", ""));

    //5.判断
    System.out.println(userInfoMap.isEmpty());
    System.out.println(userInfoMap.size());
    System.out.println(userInfoMap.containsKey("name"));    //这里的参数可以是key,也可以是value,但拿value没意义

    System.out.println(userInfoMap);
    
}

16. 遍历Map集合元素

private static void demo2() {
    //不能用快捷方式构建Map集合,即不能用Collections.addAll()。它的形参是collection
    //Map里有Map.of()方法
    Map userInfoMap = new LinkedHashMap<>();
    userInfoMap.put("id", 1001);
    userInfoMap.put("name", "张三");
    userInfoMap.put("age", 20);
    userInfoMap.put("pass", "1234");

    //遍历map集合元素
    //增强for不行,其里面是集合元素类型,这里两个类型,没法写
    //迭代器不行,只有collection集合里才有迭代器
    //用forEach方法(1.8之后),底层还是用迭代器(除了普通for,循环遍历底层都是用迭代器写的)
   /* userInfoMap.forEach(new BiConsumer() {
        @Override
        public void accept(String key, Object value) {
            System.out.println(key + "----" + value);
        }
    });*/
    //用lamda简化:
    //userInfoMap.forEach((key, value) -> System.out.println(key + "----" + value));
    //不能用::简化,重写的形参类型(两个)和调的形参类型(一个)不一样。

    //jdk1.8之前的遍历方式
    /*Set> entrySet = userInfoMap.entrySet();   //推荐使用  返回值set集合
    //底层是将map集合里面的每组数据都依次的封装成一个个的entry对象 再将entry对象存储set集合中
    //set集合里存的Map集合里的每一组数据。故遍历Map集合转成遍历Set集合,它遍历方式就多了,增强for,迭代器都可以
    for (Map.Entry entry : entrySet) {    // map集合中每组数据  都在entry里面
        String key = entry.getKey();
        Object value = entry.getValue();
        System.out.println(key + "----" + value);
    }*/

    //另一种方法:userInfoMap.keySet()   返回值类型,set集合
   /* Set keySet = userInfoMap.keySet();      //获得map集合里面每个key 存储set集合中
    for (String key : keySet) {
        System.out.println(key + "----" + userInfoMap.get(key));
    }*/
}

17. Map案例

private static void demo3() {

    String str = "djhdf77gggaf6t3gdshgfsa";
    //使用map集合实现: 统计每个字符出现的次数
    //key和value是什么类型?key要求唯一    字符是惟一的,故String,character都行。   value可为次数,用Integer
    Map map = new HashMap<>();
    //1.获得字符串里面的每个字符
    int length = str.length();     //拿字符串长度
    for (int index = 0; index < length; index++) {
        String s = String.valueOf(str.charAt(index));    //获得每个字符数据   同时char转String
        // V merge(K key, V value, BiFunction remappingFunction)
        //BiFunction是一个函数式接口
       /* map.merge(s, 1, new BiFunction() {
            @Override
            public Integer apply(Integer integer, Integer integer2) {
                return integer+integer2;
            }
        });*/
        //简化如下:
        map.merge(s, 1, Integer::sum);

        //获得每个字符之后的操作也可这样:
        /*//判断这个s字符是否存在于map集合中
        Integer num = map.get(s);   //拿value,value是次数
        if (num != null) {        //若之前没放过,value为null,不走if
            num += 1;
        } else {
            num = 1;
        }
        map.put(s, num);      //第一次存储字符
        //之前存储过相同的字符  获得之前的次数+1*/
    }
    System.out.println(map);

}

18. Map的实现类

 private static void demo7() {

        //在并发中,使用ConcurrentHashMap
        ConcurrentHashMap map = new ConcurrentHashMap<>();
        map.put(1, 10);
        map.put(10, 10);
        map.put(11, 10);
        map.put(122, 10);
        map.put(23, 10);
        //CAS 性能比较高
        System.out.println(map);
    }

    private static void demo6() {

        //hashtable比较老的一个类
        //安全的方式: public synchronized V put(K key, V value) {
        Hashtable hashtable = new Hashtable<>();
        hashtable.put(1, 10);
        hashtable.put(10, 10);
        hashtable.put(11, 10);
        hashtable.put(122, 10);
        hashtable.put(23, 10);
        hashtable.put(null, 10);
        System.out.println(hashtable);

    }

    private static void demo5() {

        //会使用自定义类对象充当map的key吗?  绝对不会   (就是写,就下面)  开发中,Map的类型就String/Interer/long
       /* TreeMap treeMap = new TreeMap<>(new Comparator() {
            @Override
            public int compare(UserInfo o1, UserInfo o2) {
                return 0;
            }
        });*/
        //用lamda简化:
        TreeMap treeMap = new TreeMap<>((user1, user2) -> 0);
        treeMap.put(new UserInfo(), 100);
        System.out.println(treeMap);
    }

    static class UserInfo { /*implements Comparable {

        @Override
        public int compareTo(UserInfo o) {
            return 0;
        }*/
    }

    private static void demo4() {

        //TreeSet: 元素不能为null  元素有序 (按照自然顺序排列)   底层就是用TreeMap写的
        //TreeMap: k不能为null  v可以为null  key的数据的元素类型必须默认实现Comparable  要么提供Comparator
        TreeMap treeMap = new TreeMap<>();
        treeMap.put(1, 10);
        treeMap.put(10, 10);
        treeMap.put(11, 10);
        treeMap.put(122, 10);
        treeMap.put(23, 10);
        System.out.println(treeMap);
    }

    private static void demo3() {

        //新增顺序与遍历顺序一致
        LinkedHashMap linkedHashMap = new LinkedHashMap<>();
        linkedHashMap.put(1, 10);
        linkedHashMap.put(10, 10);
        linkedHashMap.put(11, 10);
        linkedHashMap.put(122, 10);
        linkedHashMap.put(23, 10);
        linkedHashMap.put(null, null);
        System.out.println(linkedHashMap);

    }

    private static void demo2() {

        HashMap map = new HashMap<>();
        map.put("Ma", 100);
        map.put("Ma", 200);
        map.put("NB", 300);
        map.get("NB");

        //"Ma"与"NB"的哈希值一样,从而计算得索引一样,key数值却不同,产生哈希冲突,此时用单链表解决

        /*int code1 = "Ma".hashCode();
        int code2 = "NB".hashCode();

        System.out.println(code1);
        System.out.println(code2);
        System.out.println(code1 ^ (code1 >>> 16));
        System.out.println(code2 ^ (code2 >>> 16));*/
    }

    private static void demo1() {

        HashMap map = new HashMap<>();
        map.put(1, 1);
        map.put(1, 10);                //modCount有没有运算?   没有

        //key数据一致,hash冲突。新的数据替换oldvalue

        //(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
       /*  Integer num = Integer.valueOf(1);
        int hashCode = num.hashCode();
        System.out.println(hashCode ^ (hashCode >>> 16));*/
    }
}

19. 集合嵌套

private static void demo9() {

    //List里套Map
    //创建多个用户的信息  使用集合维护多个用户    不要用户类
    //name=jim    id=1001  age=20
    //Map.of(k1,v1,k2,v2....)   一个Map就是一个用户
    Map user1 = Map.of("id", "1001", "name", "jim1", "age", "20");
    Map user2 = Map.of("id", "1002", "name", "jim3", "age", "20");
    Map user3 = Map.of("id", "1003", "name", "jim2", "age", "20");

    List> list = List.of(user1, user2, user3);
    list.forEach(System.out::println);
}

private static void demo8() {

    //Map里套List
    //城市级联: 通过指定的省份获得这个省份下所有的城市的信息----> 通过key获得value
    //初始化省份及省份下城市信息城市
    //始终记住,Map里的key就用三种类型String/Interer/long
    Map> provinceMap = new HashMap<>(16);  //给了个16的初始容量
    List cityList = List.of("郑州市", "开封市", "洛阳市");
    provinceMap.put("河南省", cityList);
    cityList = List.of("石家庄", "衡水", "邯郸");
    provinceMap.put("河北省", cityList);

    //查询河北省下的所有的城市信息
    List list = provinceMap.get("河北省");
    list.forEach(System.out::println);
}

20. Collections 这是一个类 (与collection没关系,collection是一个接口)

private static void demo13() {

    //Arrays可转数组,转集合
    List list = Arrays.asList("a", "b", "c");
    list.forEach(System.out::println);  //遍历这个集合没有问题,即查看没问题

    //更新集合元素数据(add/remove),将报异常   修改没事(空间固定,没变化)   因为asList,返的是一个view(只能看,不允许更新)
    list.add(0,"hello");
    list.forEach(System.out::println);

    //一个方法返的是集合,该集合能不能执行更新和修改就看底层返不返view,类似的下面也返view
    //Map.of().entrySet()    Map.of().keySet
}


private static List list = Collections.synchronizedList(new ArrayList<>());   //将线程不安全的集合类转换成一个线程安全集合类
private static void demo12() {

    //很多集合类都是一些线程不安全的集合类
    List threadList = new ArrayList<>(10);
    for (int i = 0; i < 10; i++) {
        threadList.add(new Thread(() -> {
            for (int j = 0; j < 1000; j++) {
                list.add(1);
            }
        }));
    }
    threadList.forEach(Thread::start);

    threadList.forEach(thread -> {
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    //等待着前面10个子线程执行完毕---->等待着子线程死亡
    //获得集合里面的元素个数
    //让主线程休眠 5

    System.out.println(list.size());//10000
}

private static void demo11() {

    Set set = new HashSet<>();    //完全无序
    Collections.addAll(set, 10, 2, 32, 41, 5);

    Integer max = Collections.max(set);       //获得最大值
    System.out.println(max);
    System.out.println(Collections.min(set));   //获得最小值
}

private static void demo10() {
    //java.util.Collections  提供很多静态方法(因为工具类),操作集合元素数据
    //Collections.addAll(Collection c, E...e);    只能对collection集合批量添加
    //Collections.shuffle(list);   打乱集合元素顺序
    //Collections.sort(list)   对list集合元素进行升序排列

    List list = new ArrayList<>(10);
    Collections.addAll(list, 10, 2, 32, 41, 5);   //对list集合给数据

    //如果想降序排序,则自定义规则,用Collections.sort()带两个参数的
    /*Collections.sort(list, new Comparator() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2.compareTo(o1);
        }
    });*/

    //Collections.sort(list,Comparator.comparing(Integer::intValue).reversed());   反转
    //list.sort(Comparator.comparing(Integer::intValue).reversed());   list集合里也提供了comparator
    System.out.println(list);

}

21. Stream

1. 获得Stream实例:

private static void demo1() {
    //1.获得集合对象Collection
    List list = List.of(1, 2, 3, 4);     //List在java.util包里,别导错包

    //2.获得Stream的实例
    //法一:Collection集合的方法 (集合里提供的两个方法)   若用集合里的方法,要先获得集合对象
    Stream stream = list.parallelStream();     //并行化流
    Stream stream1 = list.stream();       //串行化stream
    //并行  VS 串行   都是指计算机计算的能力。
    //案例: 1000个数字 求最大值。
    //串行: 类似于冒泡排序 max  有序
    //并行: 同时执行多个任务。 将1000个数据  放到多个任务中(多个线程)。   每个任务随机分配数据,每个任务都会获得一个最值,最后在这10个里面求最大值
    //串行有序,并行无序。    串行效率低于并行

    //法二:Stream本身是一个接口,接口里也提供了静态方法,如下,也可得到Stream的实例
    //Stream stream2 = Stream.of(1, 2, 3, 4);

    //法三:随机数 Random类
    //先创建Random实例,在其里面也提供了获得Stream实例的方法
    Random random = new Random();
    //随机获得5个随机的数字,范围是1000-10000。   正常写需要循环,用流如下:
    IntStream intStream = random.ints(5, 1000, 10000);
    //提供了对于基本数据类型封装Stream,目的:减少过多的拆箱和装箱处理
    intStream.forEach(System.out::println);

    //法四:Arrays   将集合的元素放到Stream对象 
    Stream stream2 = Arrays.stream(new Integer[]{1, 2, 3});
}

2. Stream常用的功能方法

private static void demo2() {

    /*List list = List.of(10,78,90,199,1,9,100,1,90,10);
    //对List集合元素去重并升序/降序排列   常规做法:遍历,然后将元素放到另一个集合里    性能不高
    TreeSet treeSet = new TreeSet<>();
    list.forEach(treeSet::add);
    System.out.println(treeSet);*/

    //若往集合List里放元素,再对list集合元素去重并升序/降序排列   当元素个数足够大时,其性能更低
    List list = new ArrayList<>(1000);    //容量给了1000
    for (long i = 10000000; i >= 0; i--) {
        list.add(i);
    }
    long begin1 = System.nanoTime();
    //TreeSet treeSet = new TreeSet<>();
    //ist.forEach(treeSet::add);
    long begin2 = System.nanoTime();
    //System.out.println(begin2 - begin1);//3709702827  1036846933

     //若用Stream去对List里的元素去重并升序/降序排列
    //Stream stream = list.stream();      //list集合里面的数据都在stream
    //List list1 = stream.distinct().sorted().collect(Collectors.toList());  //使用Stream 聚合数据
    //stream里的  去重distinct()  排序sorted()  收集collect(Collectors.toList())转List,转Set结合需求去做
    //使用过Stream,还能再次使用吗?  无法使用第二次   Stream功能类似于Iterator,迭代器从左向右拿数据,拿完之后不能再回去,只能拿光标之后的数据
    //Stream用完之后就关闭了,一般用它,都是链式的操作这些功能,一行代码写完,一次搞定。方法的顺序无所谓。

    //获得运算之后的结果
    begin2 = System.nanoTime();
    System.out.println(begin2 - begin1);
    //parallelStream性能没有stream快
}

3. Stream常用的功能方法

private static void demo3() {

    System.out.println();
    String str = "2543716424";
    //求字符串里的最小的字符数据
    char[] chars = str.toCharArray();     //拿到字符内容
    /*Arrays.sort(chars);
    System.out.println(chars[0]);
   ;*/

    //用Stream去做
    List list = new ArrayList<>(10);   //装进集合
    for (char aChar : chars) {
        list.add(aChar);
    }
    System.out.println(list.stream().sorted().findFirst());  //再用流
    
}

4. Stream_Filter1

private static void demo4() {

    List list = List.of(10, 20, 40, 60, 60, 50);
    //留下来集合元素>30的数据  存储到另外一个集合里面
    List newList = new ArrayList<>(10);
    for (Integer num : list) {
        if (num > 30) {
            newList.add(num);
        }
    }
    System.out.println(newList);

    //Stream.filter 留下来满足条件的数据 (过滤)
    /*list.parallelStream().filter(new Predicate() {
        @Override
        public boolean test(Integer num) {    //num就是stream流里的  每个元素
            return num>30;
        }
    })*/

    List collect = list.parallelStream().filter(num -> num > 30).distinct().sorted().collect(Collectors.toList());
    System.out.println(collect);
}

5. Stream_Filter2

@Setter
@Getter
@AllArgsConstructor
@ToString
class UserInfo {
    private Integer id;
    private Integer age;
    private String name;
}

private static void demo5() {

    List userInfoList = new ArrayList<>(10);
    Collections.addAll(userInfoList,
            new UserInfo(1, 20, "jim"),
            new UserInfo(2, 20, "tom"),
            new UserInfo(3, 20, "zhangsan")
    );

    /*userInfoList.parallelStream().filter(new Predicate() {
        @Override
        public boolean test(UserInfo userInfo) {
            return userInfo.getName().contains("m");
        }
    });*/
    //查询名称里面包含m的用户信息
    List collect = userInfoList.parallelStream().filter(userInfo -> userInfo.getName().contains("m")).collect(Collectors.toList());
    System.out.println(collect);
}

6. Stream_Map和Stream_peek 一对一的方法 具体用哪个看场景

private static void demo7() {

    List userInfoList = new ArrayList<>(10);
    Collections.addAll(userInfoList,
            new UserInfo(1, 21, "jim"),
            new UserInfo(2, 22, "tom"),
            new UserInfo(3, 23, "zhangsan")
    );

    //所有人年龄+1    对于这种引用型,用peek就更方便
   /* List collect = userInfoList.parallelStream().map(new Function() {
        @Override
        public UserInfo apply(UserInfo userInfo) {
            userInfo.setAge(userInfo.getAge() + 1);
            return userInfo;
        }
    }).collect(Collectors.toList());*/

   /* List collect = userInfoList.parallelStream().peek(userInfo -> userInfo.setAge(userInfo.getAge() + 1)).collect(Collectors.toList());
    collect.forEach(System.out::println);*/  //peek没有返回值,但集合元素里装的是用户对象

    //获得所有的用户name,将name转大写
    List collect = userInfoList.parallelStream().map(UserInfo::getName).map(String::toUpperCase).collect(Collectors.toList());
    System.out.println(collect);
}


private static void demo6() {
    List nameList = Arrays.asList("jim", "tom", "zhangsan", "lily", "lilei", "hanmeimei");
    //将集合里面的每个元素都转大写
    /*List upperNameList = new ArrayList<>(10);    //常规做法:创建新集合,遍历旧集合的同时转大写,装入新集合
    for (String name : nameList) {
        upperNameList.add(name.toUpperCase());
    }
    System.out.println(upperNameList);*/

    //Stream里的map方法  一对一: Stream里面的每个元素都要执行相同的逻辑
/*    nameList.parallelStream().map(new Function() {   //转之前是String,转之后还是String
        @Override
        public String apply(String s) {
            return s.toUpperCase();
        }
    });*/

    //用String里的map方法一行写完
    //List collect = nameList.parallelStream().map(String::toUpperCase).collect(Collectors.toList());

    //用String里的peek方法
    List collect = nameList.parallelStream().peek(new Consumer() {
        @Override
        public void accept(String s) {      //peek方法返回值类型为void,重写的代码没啥意义,就只能用map
            s = s.toUpperCase();
            System.out.println(s);
        }
    }).collect(Collectors.toList());
    System.out.println(collect);

} 
  

7. Stream_fiatMap 一对多

private static void demo8() {

    List list1 = List.of(1, 3, 5, 3, 8, 10);
    List list2 = List.of(11, 32, 53, 3);
    List list3 = List.of(100, 37, 58, 39, 8, 10);
    //3个数据源  将3个集合里面的元素都存储到一个集合中(结果还要去重/排序)
    //常规方法:用三个for循环,每次将一个集合的元素放到最终的集合中

    //Stream做法:"将3个集合里面的所有的数据都放在一个Stream"      Stream.of()  现在里面是三个集合对象
    //多个数据源数据存储到一个Stream(多个集合或者流数据转到一个流里)     Stream.flatMap()
    /*Stream.of(list1, list2, list3).flatMap(new Function, Stream>() {
        @Override
        public Stream apply(List list) {
            return list.parallelStream();
        }
    })*/
    List collect = Stream.of(list1, list2, list3).flatMap(List::parallelStream).distinct().sorted().limit(5).collect(Collectors.toList());
    System.out.println(collect);
}

8. Stream_reduce

private static void demo9() {

    //reduce()  做聚合的方法(求总和,最大,最小,平均值等)
    List list = List.of(100, 89, 90);
    //成绩的总和   常规做法:遍历求和

    //用Stream去做
    Optional optional = list.parallelStream().reduce(new BinaryOperator() {
        @Override
        public Integer apply(Integer num1, Integer num2) {
            return num1 + num2;
        }
    });    //Optional是工具类,一般是用来执行null值运算
    System.out.println(optional.get());

    //用lamda替换:
    Integer result = list.parallelStream().reduce(Integer::sum).get();
    System.out.println(result);

    //求最小值:
    //Integer result = list.parallelStream().reduce(Math::min).get();
    //System.out.println(result);

    //reduce()带两个参数的,如下:每个元素加10再求总和
    //Integer result = list.parallelStream().reduce(10, Integer::sum);
    //System.out.println(result);
}

9. Stream案例 ---家谱(必须使用Stream方法)

Java第一阶段(day12)集合_第2张图片
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Person {

    private Integer id;        //家族成员id
    private String name;      //家族成员name
    private List child;    //这个人下面的所有子级
    private Integer parentId;      //这个人父级的id

}

public class Demo {

    private static List family;
    //初始化家谱
    static {
        family = new ArrayList<>(10);

        //parentId=0 代表这个人就是顶级
        family.add(new Person(1, "张三丰", null, 0));

        family.add(new Person(2, "张翠山", null, 1));
        family.add(new Person(3, "张翠翠", null, 1));

        family.add(new Person(4, "张无忌", null, 2));
        family.add(new Person(5, "张天真", null, 2));

        family.add(new Person(6, "张翠花", null, 3));
        family.add(new Person(7, "张二狗", null, 3));

        family.add(new Person(8, "张三", null, 5));

        family.add(new Person(9, "张三四", null, 0));
        family.add(new Person(10, "张1", null, 9));


    }

    public static void main(String[] args) {

        //需求:查询父节点下面的所有的子级的成员
        /*family.parallelStream().filter(new Predicate() {
            @Override
            public boolean test(Person person) {
                return person.getParentId().equals(0);
            }
        });*/             //filter(person -> person.getParentId().equals(0))  找到张三丰,再找张三丰的子级,相当于对子级赋值

        /*family.parallelStream().filter(person -> person.getParentId().equals(0)).peek(new Consumer() {
            @Override
            public void accept(Person person) {
                person.setChild();
            }
        });    */      //找子级,一对一操作

       /* List personList = family.parallelStream()
                .filter(person -> person.getParentId().equals(0))      //person集合里面的每个元素
                .peek(parent -> parent.setChild(getChild(parent)))     //person1代表就是过滤转换parentId=0的对象
                .collect(Collectors.toList());
        personList.forEach(System.out::println);*/

        //普通方式的写法:
        List list = new ArrayList<>(10);
        for (Person person : family) {
            if (person.getParentId().equals(0)) {
                person.setChild(getChildList(person));
                list.add(person);
            }
        }

        list.forEach(System.out::println);
        //找孩子---->对child属性赋值
    }

    private static List getChildList(Person parent) {
        List list = new ArrayList<>(10);
        for (Person person : family) {
            if (person.getParentId().equals(parent.getId())) {
                person.setChild(getChildList(person));
                list.add(person);
            }
        }
        return list;
    }

    private static List getChild(Person parent) {
        return family.parallelStream()
                .filter(person -> person.getParentId().equals(parent.getId()))   //过滤下来二级节点的数据
                .peek(p -> p.setChild(getChild(p)))    //递归
                .collect(Collectors.toList());
    }
}

10.课后作业

1. 创建好员工基类和程序员、项目经理子类的基础上,创建一个测试类。 创建一个ArrayList集合对象,要求保存两个程序员和两个项目经理的对象,并循环调用show的方法显示详细信息。

创建一个HashMap对象,要求保存两个程序员对象和两个项目经理对象,使用员工的编号做为主键,并循环调用show的方法显示详细信息。

1.1旧版

@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Employee {
    private Integer id;
    private String name;
    private Double salary;        //BigDecimal修饰钱最合适

    public void show(){
        System.out.println(toString());
    }
}

public class PM extends Employee{
    public PM(Integer id, String name, Double salary) {
        super(id, name, salary);
    }

    public PM() {
    }
}

public class SE extends Employee{
    public SE() {
    }

    public SE(Integer id, String name, Double salary) {
        super(id, name, salary);
    }
}

public class Exercise1 {
    public static void main(String[] args) {
        demo1();
        demo2();
    }

    private static void demo2() {
        List employeeList = new ArrayList<>(10);
        Collections.addAll(employeeList,new SE(1,"se",4000.0),new PM(2,"pm",7000D));

        Map employeeMap = new HashMap<>();
        for (Employee employee : employeeList) {
            employeeMap.put(employee.getId(),employee);
        }

        //遍历Map集合   forEach  entrySet   keySet
        //employeeMap.forEach((id,emp)->emp.show());    法一

        Set> entrySet = employeeMap.entrySet();
        for (Map.Entry entry : entrySet) {
            System.out.println(entry.getKey()+"=="+entry.getValue());
        }
    }

    private static void demo1() {
        //集合的元素是:类类型
        List employeeList = new ArrayList<>(10);
        Collections.addAll(employeeList,new SE(1,"se",4000.0),new PM(2,"pm",7000D));

        //遍历集合
        //employeeList.forEach(Employee::show);   法一

       /* Iterator it = employeeList.iterator();    //法二
        while (it.hasNext()) {
            it.next().show();          //Employee对象调用show方法
        }*/

    }
}

1.2新版

@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Emp {
    private Integer id;
    private String name;
    private BigDecimal salary;
}

class SE extends Emp {
    public SE() {
    }
    public SE(Integer id, String name, BigDecimal salary) {
        super(id, name, salary);
    }
}

class PM extends Emp {
    public PM() {
    }
    public PM(Integer id, String name, BigDecimal salary) {
        super(id, name, salary);
    }
}

class Test1 {
    
    public static void main(String[] args) {

        SE se = new SE(1, "se", new BigDecimal("47754"));
        PM pm = new PM(2, "pm", new BigDecimal("843754"));
        
        //ArrayList集合对象
       /* List empList = new ArrayList<>(10);
        Collections.addAll(empList, se, pm);*/
        List empList = List.of(se, pm);

        Map empMap = new HashMap<>(16);

        //循环遍历list集合: for 增强for forEach 迭代器
        empList.forEach(emp -> empMap.put(emp.getId(), emp));

        //遍历map集合: forEach  entrySet  keySet
        Set> entrySet = empMap.entrySet();
        for (Map.Entry entry : entrySet) {
            System.out.println(entry.getKey() + "---" + entry.getValue());
        }
        //List empList1 = Arrays.asList(se, pm);
        
    }
}

2. 基于集合,传入字符串时间,返回该时间是周几,中文显示(星期一)

1.旧版

public class Exercise3 {
    public static void main(String[] args) throws ParseException {
        //demo1("2022-3-3");
        //demo2("2022-3-3");
        demo3("2022-03-03");
    }

    private static void demo3(String dateStr) {
        List list = Arrays.asList("周日","周一","周二","周三","周四","周五","周六");
        LocalDate localDate = LocalDate.parse(dateStr);
        DayOfWeek week = localDate.getDayOfWeek();
        System.out.println(list.get(week.getValue()));
    }

    private static void demo2(String dateStr) throws ParseException {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date = dateFormat.parse(dateStr);
        List list = Arrays.asList("周日","周一","周二","周三","周四","周五","周六");

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);

        int day = calendar.get(Calendar.DAY_OF_WEEK);
        System.out.println(day);
        System.out.println(list.get(day-1));

    }

    private static void demo1(String dateStr) throws ParseException {
        //字符串时间转换成日期对象   Date/Calendar(1.8之前)   LocalDate/LocalDateTime(1.8之后)
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date = dateFormat.parse(dateStr);
        //System.out.println(date.getDay());  //4   可看成一周的第几天   (该方法过时,但可以用)

        List list = Arrays.asList("周日","周一","周二","周三","周四","周五","周六");
        System.out.println(list.get(date.getDay()));  //周四
    }
}

2.新版

private static String getWeekName(@NonNull String dateStr) {

        //字符串转LocalDate
        LocalDate localDate = LocalDate.parse(dateStr);
        //获得这一天这周的第几天
        DayOfWeek dayOfWeek = localDate.getDayOfWeek();

        List weekList = List.of("星期一", "星期2", "星期3", "星期4", "星期5", "星期6", "星期7");
        //根据所索引获得星期几的数据

        return weekList.get(dayOfWeek.getValue() - 1);
    }
}

@SneakyThrows
private static String getWeekName1(String str) {
    //pattern:  y M d H m s a E
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    Date date = dateFormat.parse(str);
    dateFormat = new SimpleDateFormat("E");
    return dateFormat.format(date);
}

3. 创建Student类,属性包括id[1-40], score[0-100],所有属性随机生成。创建Set集合,保存20个对象,找到分数最高与最低的学生

@Setter
@Getter
@AllArgsConstructor
@ToString
class Student {
    private Integer id;
    private String name;
    private Integer score;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Objects.equals(id, student.id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
}

//从功能实现上:  可以使用TreeSet
private static void demo1() {
    //属性包括id[1-40], score[0-100],所有属性随机生成。创建Set集合,保存20个对象,找到分数最高与最低的学生
   /* TreeSet studentTreeSet = new TreeSet<>(new Comparator() {
        @Override
        public int compare(Student stu1, Student stu2) {
            return stu1.getScore().compareTo(stu2.getScore());
        }
    });*/
    //TreeSet studentTreeSet = new TreeSet<>((stu1,stu2)->stu1.getScore().compareTo(stu2.getScore()));
   /* TreeSet studentTreeSet = new TreeSet<>(Comparator.comparing(new Function() {
        @Override
        public Double apply(Student student) {
            return student.getScore();
        }
    }));*/

    //在TreeSet使用哪个属性的值进行排序 因此数据的重复的问题 只看排序的那个属性的数据
    TreeSet studentTreeSet = new TreeSet<>(Comparator.comparing(Student::getId));//根据学生的成绩进行升序排列
    ThreadLocalRandom random = ThreadLocalRandom.current();
    for (int i = 1; studentTreeSet.size() < 20; i++) {
        studentTreeSet.add(new Student(random.nextInt(1, 41), "张三" + i, random.nextInt(101)));
    }

    System.out.println(studentTreeSet.size());
    studentTreeSet.forEach(System.out::println);
    System.out.println();
    Student min = studentTreeSet.first();
    System.out.println(min);
    Student max = studentTreeSet.last();
    System.out.println(max);

}

//id要唯一  score也要排序
private static void demo2() {
    HashSet hashSet = new HashSet<>(20);
    ThreadLocalRandom random = ThreadLocalRandom.current();
    for (int i = 1; hashSet.size() < 20; i++) {
        hashSet.add(new Student(random.nextInt(1, 41), "张三" + i, random.nextInt(101)));
    }
    System.out.println(hashSet.size());
    hashSet.forEach(System.out::println);
    System.out.println();
    //本来HashSet元素的数据是无序 根本无法去排序
    //使用HashSet只是满足了id的唯一性  但是无法使用score进行排序? Comparable  Comparator
    Student max = Collections.max(hashSet, Comparator.comparing(Student::getScore));
    Student min = Collections.min(hashSet, Comparator.comparing(Student::getScore));
    System.out.println(max);
    System.out.println(min);

}

4. 已知有十六支男子足球队参加2008 北京奥运会。写一个程序,把这16 支球队随机分为4 个组。采用List集合和随机数 2008 北京奥运会男足参赛国家: 科特迪瓦,阿根廷,澳大利亚,塞尔维亚,荷兰,尼日利亚,日本,美国,中国,新西 兰,巴西,比利时,韩国,喀麦隆,洪都拉斯,意大利

private static Map> countryGroup() {
    String countryStr = "科特迪瓦,阿根廷,澳大利亚,塞尔维亚,荷兰,尼日利亚,日本,美国,中国,新西兰,巴西,比利时,韩国,喀麦隆,洪都拉斯,意大利";
    Map> result = new HashMap<>(16);
    String[] countryArray = countryStr.split(",");
    //数组转集合
    List countryList = Arrays.asList(countryArray);// view
    countryList = new ArrayList<>(countryList);

    //1.国家参与1次分组
    //2.如何获得随机的一个国家名称?

    Random random = new Random();
    for (int i = 1; i <= 3; i++) {
        List list = new ArrayList<>(10);
        for (int j = 0; j < 4; j++) {
            list.add(countryList.remove(random.nextInt(countryList.size())));
        }
        result.put(i, list);
    }
    result.put(4, countryList);

    return result;
}

5. 设计Account 类如下:

private long id;

private double balance;

private String password; 要求完善设计,使得该Account 对象能够自动分配自增id。给定一个List 如下:

List list = new ArrayList<>();

list.add(new Account(10.00, “1234”));

list.add(new Account(15.00, “5678”));

list.add(new Account(0, “1010”)); 要求把List 中的内容放到一个Map 中,该Map 的键为id,值为相应的Account 对象。 最后遍历这个Map,打印所有Account 对象的id 和余额

@Setter
@Getter
@ToString
class Account {
    private Long id;
    private Double balance;
    private String password;
    private static long idIndex = 1001;

    public Account(Double balance, String password) {
        this.id = idIndex++;
        this.balance = balance;
        this.password = password;
    }
}

private static void demo3() {
    List list = new ArrayList<>();
    list.add(new Account(10.00, "1234"));
    list.add(new Account(15.00, "5678"));
    list.add(new Account(0D, "1010"));

    Map map = new HashMap<>(16);
    for (Account account : list) {
        map.put(account.getId(), account);
    }
    map.forEach((k, v) -> System.out.println(k + "---" + v));

}

你可能感兴趣的:(第一阶段,java,开发语言,后端)