Java集合(进阶)

Java集合

  • Collection集合体系结构
    • Collection
      • Collection系列集合三种遍历方式
    • List
    • 泛型
      • 泛型类
      • 泛型方法
      • 泛型接口
      • 泛型的继承和通配符
    • Set
      • HashSet
      • TreeSet
    • 总结:
  • Map(双列集合)
    • HashMap
      • LinkedHashMap
    • TreeMap
  • 可变参数
  • 集合工具类Collections
  • 集合嵌套案例
  • 不可变集合
  • 集合练习1:随机点名(1)
  • 集合练习2:随机点名(2)
  • 集合练习3:斗地主(准备牌、洗牌、发牌、看牌)

Collection集合体系结构

Java集合(进阶)_第1张图片

  • List系列集合:添加的元素是有序、可重复、有索引
  • Set系列集合:添加的元素是无序、不重复、无索引

Collection

  • Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的。
    Java集合(进阶)_第2张图片
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo1 {
    public static void main(String[] args) {
       //注意:Collection是一个接口,我们不能直接创建它的对象。所以,
        // 我们只能创建它实现类的对象。实现类:ArrayList
        Collection<String> coll = new ArrayList<>();
        //1、添加元素
        /*
        如果我们要往List系列集合中添加元素,那么方法永远是true,因为List系列
        允许重复。往Set系列集合中添加元素,在有重复元素的情况下会添加失败,返回false
         */
        coll.add("aaa");
        coll.add("bbb");
        coll.add("ccc");
        System.out.println(coll);
        //2、清空元素
        //coll.clear();
        //3、删除
        //因为Collection里面定义的是共性方法,所以此时不能通过索引进行删除
        //只能通过元素对象进行删除
        coll.remove("aaa");
        System.out.println(coll);
        //4、判断元素是否包含
        //底层是依赖equals方法进行判断是否存在。所以,如果集合中存储的是自定义对象,
        //也想通过contains方法来判断是否包含,那么在javabean类中,一定要重写equals方法
        boolean result = coll.contains("bbb");
        System.out.println(result);//true
        //5、判断集合是否为空
        boolean result2 = coll.isEmpty();
        System.out.println(result2);//false
        //6、获取集合的长度
        int size = coll.size();
        System.out.println(size);//2
    }
}

Collection系列集合三种遍历方式

1、迭代器遍历

  • 迭代器在Java中的类是iterator,迭代器是集合专用的遍历方式。
    Collection系列集合三种通用的遍历方式
    1、迭代器遍历
    2、增强for遍历
    3、lambda表达式遍历

  • Collection集合获取迭代器:在这里插入图片描述

  • iterator中的常用方法
    在这里插入图片描述

  • 迭代器遍历相关的三个方法:
    Iterator< E> iterator() : 获取一个迭代器对象
    boolean hasNext() : 判断当前指向的位置是否有元素
    E next() : 获取当前指向的的元素并移动指针

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionDemo2 {
    public static void main(String[] args) {
        //1、创建集合并添加元素
        Collection<String> coll = new ArrayList<>();
        coll.add("aaa");
        coll.add("bbb");
        coll.add("ccc");
        coll.add("ddd");
        //2、获取迭代器对象
        //迭代器就好比是一个箭头,默认指向集合的0索引处
        Iterator<String> it = coll.iterator();
        //3、利用循环不断的去获取集合中的每一个元素
        while(it.hasNext()){
            String str = it.next();//4、next方法的两件事情:获取元素并移动指针
            System.out.println(str);
        }
        //当上面循环结束之后,迭代器的指针已经指向了最后没有元素的位置
        //System.out.println(it.next());NoSuchElementException
        //迭代器遍历完毕,指针不会复位,如果要继续第二次遍历集合,只能再次获取一个新的迭代器对象
        System.out.println(it.hasNext());//false
        //循环中只能用一次next方法
        //在用迭代器遍历的时候,不能用集合的方法进行增加或者删除
        //删除可以使用迭代器提供的remove方法进行删除,添加暂时没有办法
        Iterator<String> it2 = coll.iterator();
        while (it2.hasNext()){
            String str2 = it2.next();
            if("bbb".equals(str2)){
                //coll.remove("bbb");
                it2.remove();
            }
        }
        System.out.println(coll);
    }
}

2、增强for遍历

  • 增强for的底层就是迭代器,为了简化迭代器的代码书写的。
  • 它是JDK5之后出现的,其内部原理就是一个iterator迭代器。
  • 所有的单列集合数组才能用增强for进行遍历。

格式:
for(元素的数据类型 变量名 : 数组或者集合){
}
例如:
for(String s : list){
System.out.println(s);
}

import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo3 {
    public static void main(String[] args) {
        //1、创建集合并添加元素
        Collection<String> coll = new ArrayList<>();
        coll.add("zhangsan");
        coll.add("lisi");
        coll.add("wangwu");
        //2、利用增强for进行遍历
        //快速生成方式:集合的名字 + for 回车
        //注意:s其实就是一个第三方变量,在循环的过程中依次表示集合中的每一个数据
        //修改增强for中的变量,不会改变集合中原本的数据
        for (String s : coll) {
            System.out.println(s);
        }
    }
}

3、Lambda表达式遍历
在这里插入图片描述

import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo3 {
    public static void main(String[] args) {
        //1、创建集合并添加元素
        Collection<String> coll = new ArrayList<>();
        coll.add("zhangsan");
        coll.add("lisi");
        coll.add("wangwu");
        //2、利用lambda表达式
        coll.forEach(s-> System.out.println(s));
        }
  }

List

List集合的特有方法:

  • Collection的方法List都继承了
  • List集合因为有索引,所以多了很多索引操作的方法。
    Java集合(进阶)_第3张图片
import java.util.ArrayList;
import java.util.List;
public class ListDemo1 {
    public static void main(String[] args) {
        //1、创建一个集合
        List<String> list = new ArrayList<>();

        //2、添加元素
        list.add("aaa");
        list.add("bbb");//索引为1
        list.add("ccc");
        //原来索引上的元素会往后移
        list.add(1,"QQQ");
        //3、删除元素
        //在调用方法时,如果方法出现了重载现象,优先调用
        //实参和形参类型一致的那个方法。
        String remove = list.remove(0);
        System.out.println(remove);//aaa
        //4、修改元素
        String result = list.set(2, "QQQ");
        System.out.println(result);//ccc
        //5、返回指定索引处的元素
        String s = list.get(0);
        System.out.println(s);//QQQ
        //3、打印集合
        System.out.println(list);
    }
}

List集合的5种遍历方式
1、迭代器
2、增强for
3、Lambda表达式

4、普通for循环

 for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);
            System.out.println(s);
        }

5、列表迭代器

//列表迭代器
        //获取一个列表迭代器对象,里面的指针默认也是0索引的
        //额外添加了一个方法:在遍历过程中,可以添加元素
        ListIterator<String> it = list.listIterator();
        while (it.hasNext()){
            String str = it.next();
            if("bbb".equals(str)){
                it.add("qqq");
            }
        }
        System.out.println(list);

泛型

  • 泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。
  • 泛型的格式:<数据类型>
  • 注意:泛型只能支持引用数据类型。
  • 泛型的好处:
    1、统一数据类型。
    2、把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确定下来。
  • 泛型的细节:
    1、泛型中不能写基本数据类型
    2、指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型
    3、如果不写泛型,类型默认是Object

泛型类

使用场景:当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类。
格式:修饰符 class 类名 <类型>{
}
泛型类:

import java.util.Arrays;
public class MyArrayList<E> {
    Object[] obj = new Object[10];
    int size;
    /*
    E:表示不确定的类型,该类型在类名后面已经定义过了。
    e:形参的名字,变量名
     */
    public boolean add(E e){
        obj[size] = e;
        size++;
        return true;
    }

    public E get(int index){
        return (E)obj[index];
    }

    @Override
    public String toString() {
        return Arrays.toString(obj);
    }
}

测试:

public class Test {
    public static void main(String[] args) {
        MyArrayList<String> list = new MyArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        System.out.println(list);

        MyArrayList<Integer> list2 = new MyArrayList<>();
        list2.add(123);
        list2.add(456);
        list2.add(789);
        int i = list2.get(0);
        System.out.println(i);
        System.out.println(list2);
    }
}

泛型方法

适用场景:方法中形参类型不确定时
方案1:使用类名后面定义的泛型。(所有方法都能用)
方案2:在方法申明上定义自己的泛型。(只有本方法能用)
格式:
修饰符 <类型> 返回值类型 方法名(类型 变量名){
}
泛型方法:

import java.util.ArrayList;
public class ListUtil {
    private ListUtil(){}
    //类中定义一个静态方法addAll,用来添加多个集合的元素。
    /*
    参数1:集合
    参数2:要添加的元素
     */
    public static<E> void addAll(ArrayList<E> list,E e1,E e2,E e3,E e4){
        list.add(e1);
        list.add(e2);
        list.add(e3);
        list.add(e4);
    }
    public void show(){
        System.out.println("-------");
    }
}

测试:

import java.util.ArrayList;
public class Test {
    public static void main(String[] args) {
        ArrayList<String> list1 = new ArrayList<>();
        ListUtil.addAll(list1,"aaa","bbb","ccc","ddd");
        System.out.println(list1);

        ArrayList<Integer> list2 = new ArrayList<>();
        ListUtil.addAll(list2,1,2,3,4);
        System.out.println(list2);
    }
}

泛型接口

格式:
修饰符 interface 接口名<类型>{
}
使用方式:
方式1:实现类给出具体类型。
方式2:实现类延续泛型,创建对象时再确定。

public class MyArrayList3<E> implements List<E>{
}

泛型的继承和通配符

  • 泛型不具备继承性,但是数据具备继承性
import java.util.ArrayList;
public class GenericsDemo {
    public static void main(String[] args) {
        //创建集合对象
        ArrayList<Ye> list1 = new ArrayList<>();
        ArrayList<Fu> list2 = new ArrayList<>();
        ArrayList<Zi> list3 = new ArrayList<>();

        ArrayList<Student> list4 = new ArrayList<>();

        method(list1);
        method(list2);
        method(list3);
        //method(list4);不可以使用method方法
    }
    /*
    ? 表示不确定的类型,可以进行类型的限定
    ? extends E: 表示可以传递E或者E所有的子类类型
    ? super E: 表示可以传递E或者E所有的父类类型
         */
    /*
    应用场景:
    1、如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型类,泛型方法、泛型接口。
    2、如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以用泛型的通配符。
    泛型的通配符:
    关键点:可以限定类型的范围。
     */
    public static void method(ArrayList<? extends Ye> list) {

    }

}
    class Ye{

    }
    class Fu extends Ye{

    }
    class Zi extends Fu{

    }
    class Student{

    }

Set

Set系列集合

  • 无序:存取顺序不一致
  • 不重复:可以去除重复
  • 无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素

Set集合的实现类

  • HashSet:无序、不重复、无索引
  • LinkedHashSet:有序、不重复、无索引
  • TreeSet:可排序、不重复、无索引

Set接口中的方法上基本上与Collection的API一致。

例如:使用Set系列集合,添加字符串,并使用多种方式遍历

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class SetDemo1 {
    public static void main(String[] args) {
        //1、创建一个Set集合的对象
        Set<String> s = new HashSet<>();
        //2、添加元素
        //如果当前元素是第一次添加,那么可以添加成功,返回true
        //如果当前元素是第二次添加,那么添加失败,返回false
        s.add("张三");
        s.add("张三");
        s.add("李四");
        s.add("王五");
        //3、打印集合
        //无序
        //System.out.println(s);//[李四, 张三, 王五]

        //迭代器遍历
        Iterator<String> it = s.iterator();
        while (it.hasNext()){
            String str = it.next();
            System.out.println(str);
        }
        //增强for
        for (String str : s) {
            System.out.println(str);
        }
        //Lambda表达式
        s.forEach(str-> System.out.println(str));
    }
}

HashSet

HashSet底层原理

  • HashSet集合底层采取哈希表存储数据
  • 哈希表是一种对于增删改查数据性能都较好的结构

哈希表组成

  • JDK8之前:数组+链表
  • JDK8之后:数组+链表+红黑树

哈希值

  • 根据hashCode方法算出来的int类型的整数
  • 该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算
  • 一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值

对象的哈希值特点

  • 如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
  • 如果已经重写hashCode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
  • 在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样(哈希碰撞)
import java.util.Objects;
public class HashSetDemo1 {
    public static void main(String[] args) {
        Student1 s1 = new Student1("zhangsan",23);
        Student1 s2 = new Student1("lisi",23);

        System.out.println(s1.hashCode());//-1461067292
        System.out.println(s2.hashCode());//102983077
        //哈希碰撞
        System.out.println("abc".hashCode());//96354
        System.out.println("acD".hashCode());//96354
    }
}

LinkedHashSet底层原理

  • 有序、不重复、无索引。
  • 这里的有序指的是保证存储和取出的元素顺序一致
  • 原理:底层数据结构依然是哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序。
import java.util.LinkedHashSet;
public class LinkedHashSetDemo {
    public static void main(String[] args) {
        //创建四个学生对象
        //要重写hashCode方法和equals方法
        Student1 s1 = new Student1("zhangsan",23);
        Student1 s2 = new Student1("lisi",24);
        Student1 s3 = new Student1("wangwu",25);
        Student1 s4 = new Student1("zhangsan",23);
        //创建集合对象
        LinkedHashSet<Student1> lhs = new LinkedHashSet<>();
        //添加元素
        System.out.println(lhs.add(s1));//true
        System.out.println(lhs.add(s2));//true
        System.out.println(lhs.add(s3));//true
        System.out.println(lhs.add(s4));//false
        //打印集合
        System.out.println(lhs);
        //[Student1{name = zhangsan, age = 23}, 
        // Student1{name = lisi, age = 24}, Student1{name = wangwu, age = 25}]
    }
}

TreeSet

TreeSet的特点

  • 不重复、无索引、可排序
  • 可排序:按照元素的默认规则(由小到大)排序。
  • TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好。

例如:利用TreeSet存储整数并进行排序

import java.util.Iterator;
import java.util.TreeSet;
public class TreeSetDemo1 {
    public static void main(String[] args) {
        //1、创建TreeSet集合对象
        TreeSet<Integer> ts = new TreeSet<>();
        //2、添加元素
        ts.add(4);
        ts.add(5);
        ts.add(1);
        ts.add(3);
        ts.add(2);
        //3、打印集合
        System.out.println(ts);//[1, 2, 3, 4, 5]
        //遍历集合
        Iterator<Integer> it = ts.iterator();
        while (it.hasNext()){
            Integer i = it.next();
            System.out.println(i);
        }
        System.out.println("------------------");
        //增强for
        for (Integer t : ts) {
            System.out.println(t);
        }
        System.out.println("------------------");
        //Lambda表达式
        ts.forEach(i-> System.out.println(i));
    }
}

TreeSet集合默认的规则

  • 对于数值类型:Integer,Double,默认按照从小到大的顺序进行排序。
  • 对于字符、字符串类型:按照ASCII码表中的数字升序进行排序。

排序规则:

  • 方式1:
    默认的排序规则/自然排序:
    Student1实现Comparable接口,重写里面的抽象方法,再指定比较规则
  • 方式2:
    比较器排序:
    创建TreeSet对象的时候,传递比较器Comparator指定规则

使用规则:默认使用第一种,如果第一种不能满足当前需求,就使用第二种。

import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetDemo3 {
    public static void main(String[] args) {
        //采取第二种排序方式:比较器排序
        //1、创建集合
        //o1:表示当前要添加的元素
        //o2:表示已经在红黑树存在的元素
        //方法返回值特点:
        //负数:表示当前要添加的元素是小的,存左边
        //正数:表示当前要添加的元素是大的,存右边
        //0:表示当前要添加的元素已经存在,舍弃
        TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                //按照长度排序
                int i = o1.length() - o2.length();
                //如果一样长则按照首字母排序
                i = i == 0 ? o1.compareTo(o2) : i;
                return i;
            }
        });
        //2、添加元素
        ts.add("c");
        ts.add("ab");
        ts.add("df");
        ts.add("qwer");
        //3、打印集合
        System.out.println(ts);//[c, ab, df, qwer]
    }
}

总结:

1、如果想要集合中的元素可重复

  • 用ArrayList集合,基于数组的。(用的最多)

2、如果想要集合中的元素可重复,而且当前的增删操作明显多于查询

  • 用LinkedList集合,基于链表的。

3、如果想要对集合中的元素去重

  • 用HashSet集合,基于哈希表的。(用的最多)

4、如果想要对集合中的元素去重,而且保证存取顺序

  • 用LinkedHashSet集合,基于哈希表和双链表,效率低于HashSet。

5、如果想要对集合中的元素进行排序

  • 用TreeSet集合,基于红黑树。后续也可以用List集合实现排序

Map(双列集合)

双列集合的特点:

  1. 双列集合一次需要存一对数据,分别为键和值
  2. 键不能重复,值可以重复
  3. 键和值是一一对应的,每一个键只能找到自己对应的值
  4. 键+值这个整体我们称之为“键值对”或者“键值对对象”,在java中叫做“Entry对象”

Map是双列集合的顶层接口,它的功能是全部双列集合都可以继承使用的
Java集合(进阶)_第4张图片

import java.util.HashMap;
import java.util.Map;
public class MapDemo1 {
    public static void main(String[] args) {
        //1、创建Map集合的对象
        Map<String,String> m = new HashMap<>();
        //2、添加元素
        //put方法细节:添加/覆盖
        //在添加数据的时候,如果键不存在,那么直接把键值对对象添加到Map集合当中,方法返回null
        //在添加数据的时候,如果键是存在的,那么会把原有的键值对对象覆盖,会把被覆盖的值进行返回
        String value1 = m.put("0", "000");
//        System.out.println(value1);//null
        m.put("1","100");
        m.put("2","200");
//        String value2 = m.put("1", "300");
//        System.out.println(value2);//100
        //删除
//        String result = m.remove("1");
//        System.out.println(result);//100
        //清空
//        m.clear();
        //判断是否包含
        boolean KeyResult = m.containsKey("0");
        System.out.println(KeyResult);//true

        boolean valueResult = m.containsValue("100");
        System.out.println(valueResult);//true

        boolean result = m.isEmpty();
        System.out.println(result);//false

        int size = m.size();
        System.out.println(size);//3
        //3、打印集合
        System.out.println(m);//{0=000, 1=300, 2=200}
    }
}

Map集合的遍历方式:
1、通过键找值:

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapDemo2 {
    public static void main(String[] args) {
        //Map集合的第一种遍历方式
        //1、创建Map集合对象
        Map<String,String> map = new HashMap<>();
        //2、添加元素
        map.put("0","000");
        map.put("1","100");
        map.put("2","200");
        //3、通过键找值
        //3.1获取所有的键,把这些键放到一个单列集合当中
        Set<String> keys = map.keySet();
        //3.2遍历单列集合,得到每一个键
        for (String key : keys) {
            //System.out.println(key);
            //3.3利用Map集合中的键获取对应的值 get
            String value = map.get(key);
            System.out.println(key + " = " + value);
        }
    }
}

2、通过键值对对象进行遍历:

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapDemo3 {
    public static void main(String[] args) {
        //Map集合的第二种遍历方式
        //1、创建Map集合的对象
        Map<String,String> map = new HashMap<>();
        //2、添加元素
        map.put("0","000");
        map.put("1","100");
        map.put("2","200");
        //3.Map集合的第二种遍历方式
        //通过键值对对象进行遍历
        //3.1通过一个方法获取所有的键值对对象,返回一个Set集合
        Set<Map.Entry<String,String>> entries = map.entrySet();
        //3.2遍历entries这个集合,取得到里面的每一个键值对对象
        for (Map.Entry<String, String> entry : entries) {
            //3.3利用entry调用get方法获取键和值
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "=" + value);
        }
    }
}

3、通过Lambda表达式遍历
在这里插入图片描述

import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
public class MapDemo4 {
    public static void main(String[] args) {
        //Map集合的第三种遍历方式
        //1、创建Map集合
        Map<String,String> map = new HashMap<>();
        //2、添加元素
        map.put("0","000");
        map.put("1","100");
        map.put("2","200");
        //3、利用Lambda表达式
        //底层:
        //forEach其实就是利用第二种遍历方式进行遍历,依次得到每一个键和值
        //再调用accept方法
        map.forEach(new BiConsumer<String, String>() {
            @Override
            public void accept(String key, String value) {
                System.out.println(key + "=" + value);
            }
        });
        System.out.println("---------------------");
        map.forEach((key,value) ->System.out.println(key + "=" + value));
    }
}

HashMap

HashMap的特点

  1. HashMap是Map里面的一个实现类。
  2. 没有额外需要学习的特有方法,直接使用Map里面的方法就可以了。
  3. 特点都是由键决定的:无序、不重复、无索引
  4. HashMap跟HashSet底层原理是一模一样的,都是哈希表结构

HashMap总结

  1. HashMap底层是哈希表结构的
  2. 依赖hashCode方法和equals方法保证键的唯一
  3. 如果键存储的是自定义对象,需要重写hashCode和equals方法,如果值存储自定义对象,不需要重写hashCode和equals方法

需求:创建一个HashMap集合,键是学生对象(Student),值是籍贯(String)。存储三个键值对元素,并遍历。
要求:同姓名,同年龄认为是同一个学生
核心点:HashMap的键位置如果存储的是自定义对象,需要重写hashCode和equals方法

import java.util.HashMap;
import java.util.Set;
public class HashMapDemo1 {
    public static void main(String[] args) {
        //1、创建HashMap的对象
        HashMap<Student,String> hm = new HashMap<>();
        //2、创建三个学生对象
        Student s1 = new Student("zhangsan",23);
        Student s2 = new Student("lisi",24);
        Student s3 = new Student("wangwu",25);
        Student s4 = new Student("wangwu",25);
        //3、添加元素
        hm.put(s1,"江苏");
        hm.put(s2,"浙江");
        hm.put(s3,"福建");
        hm.put(s4,"山东");
        //4、遍历集合
        Set<Student> keys = hm.keySet();
        for (Student key : keys) {
            String value = hm.get(key);
            System.out.println(key + "=" + value);
        }
    }
}

LinkedHashMap

  • 由键决定:有序,不重复、无索引
  • 这里的有序指的是保证存储和取出的元素顺序一致
  • 原理:底层数据结构是哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序。
import java.util.LinkedHashMap;
public class LinkedHashMapDemo1 {
    public static void main(String[] args) {
        //1、创建集合
        LinkedHashMap<Integer, String> lhm = new LinkedHashMap<Integer, String>();
        //2、添加元素
        lhm.put(3,"c");
        lhm.put(1,"a");
        lhm.put(1,"b");
        lhm.put(4,"d");
        //3、打印集合
        System.out.println(lhm);//{3=c, 1=b, 4=d}
    }
}

TreeMap

  • TreeMap跟TreeSet底层原理一样,都是红黑树结构
  • 由键决定特性:不重复、无索引、可排序
  • 可排序:对键进行排序
  • 注意:默认按照键的从小到大进行排序,也可以自己规定键的排序规则

代码书写两种排序规则

  • 实现Comparable接口,指定比较规则。
  • 创建集合时传递Comparable比较器对象,指定比较规则。
import java.util.Comparator;
import java.util.TreeMap;
//需求:
//1、按键值升序输出(默认升序)
//2、按键值降序输出(重写compare方法)
public class TreeMapDemo1 {
    public static void main(String[] args) {
        //1、创建集合
        TreeMap<Integer, String> tm = new TreeMap<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                //o1:当前要添加的元素
                //o2:表示已经在红黑树中存在的元素
                return o2 - o1;
            }
        });
        //2、添加元素
        tm.put(5,"e");
        tm.put(4,"d");
        tm.put(3,"c");
        tm.put(2,"b");
        tm.put(1,"a");
        //3、打印集合
        System.out.println(tm);
    }
}

可变参数

  • 格式:属性类型…名字
  • 作用:在形参中接收多个数据
  • int…args
  • 底层:可变参数底层就是一个数组
  • 细节:1、在方法的形参中最多只能写一个可变参数
    2、在方法当中,如果除了可变参数以外,还有其它的形参,那么可变参数要写在最后
public class ArgsDemo1 {
    public static void main(String[] args) {
        int sum = getsum(1, 2, 3,4,5,6);
        System.out.println(sum);//21
    }
    public static int getsum(int...args){
        int sum = 0;
        for (int i : args) {
            sum += i;
        }
        return sum;
    }
}

集合工具类Collections

  • 作用:Collections不是集合,而是集合的工具类。
  • Collections常用的API
    在这里插入图片描述
    Java集合(进阶)_第5张图片
import java.util.ArrayList;
import java.util.Collections;
public class CollectionsDemo1 {
    public static void main(String[] args) {
        //addAll批量添加元素
        //1、创建集合对象
        ArrayList<String> list = new ArrayList<>();
        //2、批量添加元素
        Collections.addAll(list,"abc","bcd","qwer","df");
        //3、打印集合
        System.out.println(list);//[abc, bcd, qwer, df]
        //shuffle打乱
        Collections.shuffle(list);
        System.out.println(list);//[df, qwer, bcd, abc]
    }
}

集合嵌套案例

import java.util.*;
public class Test4 {
    public static void main(String[] args) {
        //1、创建集合
        HashMap<String, ArrayList<String>> hm = new HashMap<>();
        //2、创建单列集合
        ArrayList<String> list1 = new ArrayList<String>();
        ArrayList<String> list2 = new ArrayList<String>();
        ArrayList<String> list3 = new ArrayList<String>();
        Collections.addAll(list1, "1","2","3","4","5");
        Collections.addAll(list2, "10","20","30","40","50");
        Collections.addAll(list3, "100","200","300","400","500");
        //3、添加集合
        hm.put("一位数",list1);
        hm.put("两位数",list2);
        hm.put("三位数",list3);
        //4、遍历集合
        Set<Map.Entry<String, ArrayList<String>>> entries = hm.entrySet();
        for (Map.Entry<String, ArrayList<String>> entry : entries) {
            String key = entry.getKey();
            ArrayList<String> value = entry.getValue();
            StringJoiner sj = new StringJoiner(", ","","");
            for (String list : value) {
                    sj.add(list);
            }
            System.out.println(key + " = " + sj);
        }
    }
}

输出结果:

三位数 = 100, 200, 300, 400, 500 
两位数 = 10, 20, 30, 40, 50 
一位数 = 1, 2, 3, 4, 5

不可变集合

  • 不可变集合应用场景
    1、如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践。
    2、或者当集合对象被不可信的库调用时,不可变形式是安全的。

  • 创建不可变集合的书写格式
    在List、Set、Map接口中,都存在静态的of方法,可以获取一个不可变的集合。
    Java集合(进阶)_第6张图片
    注意:这个集合不能添加,不能修改,不能删除。

import java.util.Iterator;
import java.util.List;
public class ImmutableDemo1 {
    public static void main(String[] args) {
        //1、创建集合并添加元素
        List<String> list = List.of("p1","p2","p3");
        //2、遍历集合
        Iterator<String> it = list.iterator();
        while (it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }
    }
}

细节:
1、当我们要获取一个不可变的Set集合时,里面的参数一定要保证唯一性。
2、对于Map集合来说,键是不能重复的。
3、Map里面的of方法,参数是有上限的,最多只能传递20个参数,10个键值对,超过10个同ofEntries方法。

创建Map的不可变集合,键值对的数量超过10个:

import java.util.*;
public class ImmutableDemo2 {
    public static void main(String[] args) {
       //1、创建一个普通的Map集合
        HashMap<String,String> hm =new HashMap<>();
        //2、添加元素
        hm.put("1","100");
        hm.put("2","200");
        hm.put("3","300");
        //简化1:
        Map<String, String> map = Map.copyOf(hm);//(JDK10以上)
        //简化2:
        //Map map = Map.ofEntries(hm.entrySet().toArray(new Map.Entry[0]));
//        //3、利用上面的数据来获取一个集合
//        //获取到所有的键值对对象(Entry对象)
//        Set> entries = hm.entrySet();
//        //把entries变成一个数组
//        Map.Entry[] arr1 = entries.toArray(new Map.Entry[0]);
//       //toArray方法在底层会比较集合的长度跟数组的长度两者的大小
//        //如果集合长度 > 数组的长度:数据在数组中放不下,此时会根据实际数据的个数,重新创建数组
//        //如果集合长度 <= 数组的长度:数据在数组中放得下,此时不会创建新数组,而是直接使用
//        Map.Entry[] arr2 = entries.toArray(arr1);
//        不可变的Map集合
//        Map map = Map.ofEntries(arr2);
        map.put("bbb",222);不可添加数据
    }
}

集合练习1:随机点名(1)

需求:随机点名,其中男生的概率70%,女生30%

import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
public class Test2 {
    public static void main(String[] args) {
        //1、创建集合
        ArrayList<Integer> list = new ArrayList<>();
        //2、向集合中添加数据
        Collections.addAll(list,1,1,1,1,1,1,1,0,0,0);
        //3、打乱集合
        Collections.shuffle(list);
        System.out.println(list);
        //4、从list集合中随机抽取0或1
        Random r = new Random();
        int index = r.nextInt(list.size());//指定范围
        int number = list.get(index);//得到指定的索引
        System.out.println(number);
        //5、创建两个集合添加男生和女生
        ArrayList<String> boyList = new ArrayList<>();
        ArrayList<String> girlList = new ArrayList<>();
        Collections.addAll(boyList,"b1","b2","b3","b4","b5");
        Collections.addAll(girlList,"g1","g2","g3","g4","g5");
        //6、获取被抽取的学生
        if(number == 1){
            int boyindex = r.nextInt(boyList.size());
            String name = boyList.get(boyindex);
            System.out.println(name);
        }else {
            int girlindex = r.nextInt(girlList.size());
            String name = girlList.get(girlindex);
            System.out.println(name);
        }
    }
}

集合练习2:随机点名(2)

需求:班级里有5个学生,开始随机点名,点到的同学不会被再次点到,共点名5轮

import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
public class Test3 {
    public static void main(String[] args) {
        //1、创建集合
        ArrayList<String> list = new ArrayList<>();
        ArrayList<String> list1 = new ArrayList<>();
        //2、添加元素
        Collections.addAll(list, "p1", "p2", "p3", "p4", "p5");
        int size = list.size();//获取集合中元素个数
        //3、开始随机点名
        for (int i = 1; i <= 5; i++) {
            System.out.println("第"+ i +"轮点名开始了");
            Random r = new Random();
            for (int j = 0; j < size; j++) {
                int index = r.nextInt(list.size());
                String name = list.get(index);
                list1.add(name);//集合中的元素添加到list1中
                System.out.println(name);//输出姓名
                list.remove(index);//点完名就删了
            }
            list.addAll(list1);//把list1的元素全部赋值给list
            list1.clear();//清空list1集合
        }
    }
}

集合练习3:斗地主(准备牌、洗牌、发牌、看牌)

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.TreeSet;
public class PokerGame2 {
        //1、创建集合
        static HashMap<Integer, String> hm = new HashMap<>();
        static ArrayList<Integer> list = new ArrayList<>();//用来存储键值
        static {
            String[] color = {"♠", "♥", "♦", "♣"};
            String[] number = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
            //2、添加元素到集合
            int serialNumber = 1;
            for (String n : number) {//先添加遍历数字
                for (String c : color) {//再遍历颜色
                    hm.put(serialNumber, c + n);//添加序号(键)和牌(值)
                    list.add(serialNumber);
                    serialNumber++;
                }
            }
            //添加大王和小王
            hm.put(serialNumber, "小王");
            list.add(serialNumber);
            serialNumber++;
            hm.put(serialNumber, "大王");
            list.add(serialNumber);
            System.out.println(hm);
            System.out.println(list);
        }
    public PokerGame2() {
            //洗牌
            Collections.shuffle(list);
            //3、创建四个集合()
            TreeSet<Integer> lord = new TreeSet<>();
            TreeSet<Integer> player1 = new TreeSet<>();
            TreeSet<Integer> player2 = new TreeSet<>();
            TreeSet<Integer> player3 = new TreeSet<>();
            //发牌
            for (int i = 0; i < list.size(); i++) {
                int poker = list.get(i);
                if (i <= 2) {
                    lord.add(poker);
                    continue;
                }
                if (i % 3 == 0) {
                    player1.add(poker);
                } else if (i % 3 == 1) {
                    player2.add(poker);
                } else {
                    player3.add(poker);
                }
            }
            //看牌
            lookpoker("底牌", lord);
            lookpoker("玩家1", player1);
            lookpoker("玩家2", player2);
            lookpoker("玩家3", player3);
        }
        public void lookpoker (String name, TreeSet < Integer > ts){
            System.out.print(name + ": ");
            //遍历TreeSet集合得到每一个序号,再拿着序号去Map集合中去找真正的牌
            for (int serialNumber : ts) {
                String poker = hm.get(serialNumber);
                System.out.print(poker + " ");
            }
            System.out.println();
        }
}

你可能感兴趣的:(Java学习,java,jvm,servlet)