集合

1.Collection

集合和数组的区别

  • 集合
    • 是一个容器, 用来存放多个数据
    • 集合中的元素可以是不同数据类型
    • 集合中只能存放引用数据类型, 但是如果将基本数据类型的数据存入集合是可以的, 因为进行自动装箱
    • 集合的长度是可变的, 随着元素的增删, 改变集合的长度
  • 数组
    • 是一个容器, 用来存放多个数据
    • 数组中的所有元素必须是同一种数据类型
    • 数组中的数据类型可以是基本数据类型也可以是引用数据类型
    • 数组的长度是固定的

单列集合的继承体系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GgjaXgRB-1606577206974)(img/image-20200702095155561.png)]

Collection中的常用功能

方法如下:

  • public boolean add(E e): 把给定的对象添加到当前集合中 。
  • public void clear() :清空集合中所有的元素。
  • public boolean remove(E e): 把给定的对象在当前集合中删除。
  • public boolean contains(Object obj): 判断当前集合中是否包含给定的对象。
  • public boolean isEmpty(): 判断当前集合是否为空。
  • public int size(): 返回集合中元素的个数。
  • public Object[] toArray(): 把集合中的元素,存储到数组中

2.Iterator迭代器

迭代器的使用

  • 获取迭代器对象

    Collection: 
    	Iterator<E> iterator() : 获取集合对应的迭代器
    
  • 判断是否有下一个元素

    Iterator:
    	boolean hasNext() : 判断是否有下一个元素
        E next() : 获取下一个元素
    
使用迭代器进行遍历

for循环和while循环的使用:

  • 明确循环次数 -> for
  • 不明确循环次数 -> while
// 遍历集合
// 1. 获取集合对应的迭代器
Iterator<String> it = c.iterator();
// 2. 判断是否有下一个元素
while (it.hasNext()) {
    // 3. 如果有就获取
    String s = it.next();
    System.out.println(s);
}

迭代器的原理

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qvyPRjbd-1606577206979)(img/image-20200702111303751.png)]

迭代器遍历自定义类型(Person)
// 先创建一个用来存放Person类型的集合
ArrayList<Person> list = new ArrayList<>();

// 存放Person对象  - 匿名对象
list.add(new Person("张三", 23));
list.add(new Person("李四", 24));
list.add(new Person("王五", 25));
list.add(new Person("赵六", 26));

// 获取集合对应的迭代器
Iterator<Person> it = list.iterator();
// 判断集合中是否有下一个元素
while (it.hasNext()) {
    // 获取下一个元素  (Person)
    // System.out.println(it.next().getName() + "---" + it.next().getAge());
    // 使用上面方式的话, 获取到的数据有问题
    
    // 解决方案:
    Person p = it.next();
    System.out.println(p.getName() + "---" + p.getAge());
}

3. 迭代器使用时的注意事项

(1) NoSuchElementException
  • 迭代器遍历的过程中, 已经没有元素可以获取了, 仍然继续使用next()方法
(2) ConcurrentModificationException
  • 并发修改异常
    • 产生的原因: 使用迭代器遍历集合的同时, 使用集合的方法修改了集合(可用迭代器修改)
        ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("java");
        list.add("c");
        list.add("d");

        // 迭代器遍历
        Iterator<String> it = list.iterator();

        // 判断是否有下一个元素
        while (it.hasNext()) {
            // 获取下一个元素
            String s = it.next();
            // 判断: 如果获取到的是java, 就删除
            if ("java".equals(s)) {
                // 使用集合的删除方法, 会出现并发修改异常
                // list.remove(s);
                // 使用迭代器的删除方法
                it.remove();
            }
        }

        System.out.println(list);

特殊情况, 不会出现并发修改异常:

​ 要删除的元素, 在集合的倒数第二个位置

增强for循环

  • 增强for循环: 用来遍历容器(集合, 数组)

格式

for (容器中元素的数据类型 变量名 : 容器) {
    使用变量;
}

举例

		Collection<Integer> c = new ArrayList<>();
        c.add(1);
        c.add(2);
        c.add(3);
        c.add(4);
        c.add(5);


        for (Integer i : c) {
            System.out.println(i);
        }


        int[] arr = {10, 20, 30, 40, 50};

        for (int i : arr) {
            System.out.println(i);
        }

快捷键

容器.for -> 回车
三种循环的应用场景
  • 普通for循环 : 使用索引
  • 迭代器 : 删除集合中元素, 使用迭代器自己的remove()
  • 增强for:只做遍历

List集合

有序(不是排序) : 存和取的顺序是一致的

有索引 : 集合中的元素都是有索引的

可以重复 : 集合中可以存放相同的元素

1. List中的常用功能

boolean add(E e)

void add(int index, E e) : 向指定索引处, 插入元素

E remove(int index) : 删除指定索引处的元素
    返回被删除的元素

E set(int index, E e) : 修改指定索引位置的元素
    返回被修改的元素

E get(int index) : 获取指定索引处的元素
    返回指定索引处的元素

2. List的子类

(1) ArrayList
  • 底层数据结构是数组
(2) LinkedList
  • 底层数据结构是链表(双向链表)

LinkedList特有功能, 都是针对头和为进行操作
在这里插入图片描述

// 头: first    尾: last
addFirst(E e);
addLast(E e);

removeFirst();
removeLast();

getFirst();
getLast();

pop();
push();

(3) Vector
  • 底层数据结构是数组, 是线程安全的,已经被ArrayList取代了
(4) 模拟栈结构(面试题)
package com.itheima._04list;

import java.util.LinkedList;

/**
 *
 *  模拟栈结构
 *
 *  进栈
 *
 *  出栈
 *
 */
public class MyStack<E> {

    // 自己定义一个LinkedList对象
    private LinkedList<E> list = new LinkedList<>();

    /**
     * 进栈功能
     * @param e
     */
    public void in(E e) {
        list.push(e);
    }

    /**
     * 出栈
     */
    public E out() {
        return list.pop();
    }

    /**
     * 判断栈是否为空
     * @return
     */
    public boolean isEmpty() {
        return list.isEmpty();
    }


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


ArrayList

  • 通过查看ArrayList类的API:
    • java.util.ArrayList <E> :该类需要 import导入使后使用。
    • 表示一种指定的数据类型,叫做泛型,用于约束集合中存储元素的数据类型
    • 怎么用呢?
      • 在出现E的地方我们使用引用数据类型替换即可,表示我们将存储哪种引用类型的元素。
      • 例如:
        • ArrayList 表示ArrayList集合中只能存储String类型的对象
        • ArrayList 表示ArrayList集合中只能存储Student类型的对象
    • 概述:
      • ArrayList类底层是大小可变的数组的实现,存储在内的数据称为元素。也就是说ArrayList 中可以不断添加元素,其大小会自动增长。

ArrayList类构造方法

  • 介绍ArrayList的构造方法

    • ArrayList() 构造一个初始容量为 10 的空列表,当真正对数组进行添加时,才真正分配容量,每次扩容是原来的1.5倍
  • 案例演示

    ArrayList<String> list1 = new ArrayList<>();//创建一个ArrayList对象,集合中的元素类型为String类型
    ArrayList<Student> list2 = new ArrayList<>();//创建一个ArrayList对象,集合中的元素类型为Student类型
    

ArrayList类添加元素方法

  • ArrayList添加元素的方法

    • public boolean add(E e):将指定的元素追加到此集合的末尾
    • public void add(int index,E element):在此集合中的指定位置插入指定的元素
  • 案例演示:

     public static void main(String[] args) {
            ArrayList<String> array = new ArrayList<String>();
    
            //public boolean add(E e):将指定的元素追加到此集合的末尾
    //        System.out.println(array.add("hello"));
    
            array.add("hello");
            array.add("world");
            array.add("java");
    
            //public void add(int index,E element):在此集合中的指定位置插入指定的元素
            array.add(1,"javase");
    //        array.add(3,"javaee");
    
            //IndexOutOfBoundsException
    //        array.add(4,"javaee");
    
            //输出集合
            System.out.println("array:" + array);
    
        }
    

###ArrayList类常用方法

方法名说明
public boolean remove(Object o)删除指定的元素,返回删除是否成功
public E remove(int index)删除指定索引处的元素,返回被删除的元素
public E set(int index, E element)修改指定索引处的元素,返回被修改的元素
public E get(int index)返回指定索引处的元素
public int size()返回集合中的元素的个数
示例代码
public class ArrayListDemo02 {
    public static void main(String[] args) {
        //创建集合
        ArrayList<String> array = new ArrayList<String>();

        //添加元素
        array.add("hello");
        array.add("world");
        array.add("java");

        //public boolean remove(Object o):删除指定的元素,返回删除是否成功
//        System.out.println(array.remove("world"));
//        System.out.println(array.remove("javaee"));

        //public E remove(int index):删除指定索引处的元素,返回被删除的元素
//        System.out.println(array.remove(1));

        //IndexOutOfBoundsException
//        System.out.println(array.remove(3));

        //public E set(int index,E element):修改指定索引处的元素,返回被修改的元素
//        System.out.println(array.set(1,"javaee"));

        //IndexOutOfBoundsException
//        System.out.println(array.set(3,"javaee"));

        //public E get(int index):返回指定索引处的元素
//        System.out.println(array.get(0));
//        System.out.println(array.get(1));
//        System.out.println(array.get(2));
        //System.out.println(array.get(3)); //?????? 自己测试

        //public int size():返回集合中的元素的个数
        System.out.println(array.size());

        //输出集合
        System.out.println("array:" + array);
    }
}

ArrayList存储字符串并遍历

需求
  • 创建一个存储字符串的集合,存储3个字符串元素,使用程序实现在控制台遍历该集合
分析
  1. 创建集合对象
  2. 往集合中添加字符串对象
  3. 遍历集合,首先要能够获取到集合中的每一个元素,这个通过get(int index)方法实现
  4. 遍历集合,其次要能够获取到集合的长度,这个通过size()方法实现
  5. 遍历集合的通用格式
实现
import java.util.ArrayList;

/*
ArrayList存储字符串并遍历

需求
    - 创建一个存储字符串的集合,存储3个字符串元素,使用程序实现在控制台遍历该集合
分析:
    1.创建ArrayList集合对象,限制集合中元素的类型为String类型
    2.往集合对象中存储3个字符串元素  使用add()方法往集合中添加元素
    3.获取集合中元素的个数
    4.循环遍历集合中每一个元素

 */
public class Test1ArrayList {
    public static void main(String[] args) {
        // 1.创建ArrayList集合对象,限制集合中元素的类型为String类型
        ArrayList<String> list = new ArrayList<>();

        // 2.往集合对象中存储3个字符串元素  使用add()方法往集合中添加元素
        list.add("唐三");
        list.add("小舞");
        list.add("荣荣");

        // 3.获取集合中元素的个数
        //int size = list.size();

        // 4.循环遍历集合中每一个元素
        for (int i = 0;i<list.size();i++){
            // 获取元素,并打印
            System.out.println(list.get(i));
        }
    }
}

ArrayList存储学生对象并遍历

需求
  • 创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
分析
  1. 定义学生类
  2. 创建集合对象
  3. 创建学生对象
  4. 添加学生对象到集合中
  5. 遍历集合,采用通用遍历格式实现
实现
import java.util.ArrayList;

/*
ArrayList存储学生对象并遍历

需求:
    - 创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
分析:
    1.创建学生类
    2.创建3个学生对象
    3.创建ArrayList集合对象,限制集合中元素的类型为学生类类型
    4.往集合中存储那3个学生对象
    5.循环遍历集合中的元素,打印输出
 */
public class Test2ArrayList {
    public static void main(String[] args) {
        // 创建3个学生对象
        Student stu1 = new Student("萧炎",18);
        Student stu2 = new Student("薰儿",19);
        Student stu3 = new Student("美杜莎",20);

        // 创建ArrayList集合对象,限制集合中元素的类型为学生类类型
        ArrayList<Student> list = new ArrayList<>();

        // 往集合中存储那3个学生对象
        list.add(stu1);
        list.add(stu2);
        list.add(stu3);

        // 循环遍历集合中的元素,打印输出
        for (int i = 0;i<list.size();i++){
            // 获取集合元素
            Student stu = list.get(i);
            // System.out.println("stu:"+stu);// 打印地址值
            System.out.println( stu.getName()+","+ stu.getAge());
        }
    }
}

ArrayList存储学生对象并遍历升级版

需求
  • 创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合 ( 学生的姓名和年龄来自于键盘录入)
分析
  1. 定义学生类,为了键盘录入数据方便,把学生类中的成员变量都定义为String类型
  2. 创建集合对象
  3. 键盘录入学生对象所需要的数据
  4. 创建学生对象,把键盘录入的数据赋值给学生对象的成员变量
  5. 往集合中添加学生对象
  6. 遍历集合,采用通用遍历格式实现
实现
/*
    思路:
        1:定义学生类,为了键盘录入数据方便,把学生类中的成员变量都定义为String类型
        2:创建集合对象
        3:键盘录入学生对象所需要的数据
        4:创建学生对象,把键盘录入的数据赋值给学生对象的成员变量
        5:往集合中添加学生对象
        6:遍历集合,采用通用遍历格式实现
 */
public class ArrayListTest {
    public static void main(String[] args) {
        //创建集合对象
        ArrayList<Student> array = new ArrayList<Student>();

        //为了提高代码的复用性,我们用方法来改进程序
        addStudent(array);
        addStudent(array);
        addStudent(array);

        //遍历集合,采用通用遍历格式实现
        for (int i = 0; i < array.size(); i++) {
            Student s = array.get(i);
            System.out.println(s.getName() + "," + s.getAge());
        }
    }

    /*
        两个明确:
            返回值类型:void
            参数:ArrayList<Student> array
     */
    public static void addStudent(ArrayList<Student> array) {
        //键盘录入学生对象所需要的数据
        Scanner sc = new Scanner(System.in);

        System.out.println("请输入学生姓名:");
        String name = sc.nextLine();

        System.out.println("请输入学生年龄:");
        String age = sc.nextLine();

        //创建学生对象,把键盘录入的数据赋值给学生对象的成员变量
        Student s = new Student();
        s.setName(name);
        s.setAge(age);

        //往集合中添加学生对象
        array.add(s);
    }
}

集合综合案例

案例介绍

按照斗地主的规则,完成洗牌发牌的动作。
具体规则:

使用54张牌打乱顺序,三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。

####案例分析

  • 准备牌:

    牌可以设计为一个ArrayList,每个字符串为一张牌。
    每张牌由花色数字两部分组成,我们可以使用花色集合与数字集合嵌套迭代完成每张牌的组装。
    牌由Collections类的shuffle方法进行随机排序。

  • 发牌

    将每个人以及底牌设计为ArrayList,将最后3张牌直接存放于底牌,剩余牌通过对3取模依次发牌。

  • 看牌

    直接打印每个集合。

6.3 代码实现
import java.util.ArrayList;
import java.util.Collections;

public class Poker {
    public static void main(String[] args) {
        /*
        * 1: 准备牌操作
        */
        //1.1 创建牌盒 将来存储牌面的 
        ArrayList<String> pokerBox = new ArrayList<String>();
        //1.2 创建花色集合
        ArrayList<String> colors = new ArrayList<String>();

        //1.3 创建数字集合
        ArrayList<String> numbers = new ArrayList<String>();

        //1.4 分别给花色 以及 数字集合添加元素
        colors.add("♥");
        colors.add("♦");
        colors.add("♠");
        colors.add("♣");

        for(int i = 2;i<=10;i++){
            numbers.add(i+"");
        }
        numbers.add("J");
        numbers.add("Q");
        numbers.add("K");
        numbers.add("A");
        //1.5 创造牌  拼接牌操作
        // 拿出每一个花色  然后跟每一个数字 进行结合  存储到牌盒中
        for (String color : colors) {
            //color每一个花色 guilian
            //遍历数字集合
            for(String number : numbers){
                //结合
                String card = color+number;
                //存储到牌盒中
                pokerBox.add(card);
            }
        }
        //1.6大王小王
        pokerBox.add("小☺");
        pokerBox.add("大☠");	  
        // System.out.println(pokerBox);
        //洗牌 是不是就是将  牌盒中 牌的索引打乱 
        // Collections类  工具类  都是 静态方法
        // shuffer方法   
        /*
         * static void shuffle(List<?> list) 
         *     使用默认随机源对指定列表进行置换。 
         */
        //2:洗牌
        Collections.shuffle(pokerBox);
        //3 发牌
        //3.1 创建 三个 玩家集合  创建一个底牌集合
        ArrayList<String> player1 = new ArrayList<String>();
        ArrayList<String> player2 = new ArrayList<String>();
        ArrayList<String> player3 = new ArrayList<String>();
        ArrayList<String> dipai = new ArrayList<String>();	  

        //遍历 牌盒  必须知道索引   
        for(int i = 0;i<pokerBox.size();i++){
            //获取 牌面
            String card = pokerBox.get(i);
            //留出三张底牌 存到 底牌集合中
            if(i>=51){//存到底牌集合中
                dipai.add(card);
            } else {
                //玩家1   %3  ==0
                if(i%3==0){
                  	player1.add(card);
                }else if(i%3==1){//玩家2
                  	player2.add(card);
                }else{//玩家3
                  	player3.add(card);
                }
            }
        }
        //看看
        System.out.println("令狐冲:"+player1);
        System.out.println("田伯光:"+player2);
        System.out.println("绿竹翁:"+player3);
        System.out.println("底牌:"+dipai);  
	}
}

一. Set接口

List : 有序, 有索引, 可以重复

Set :

无序 : 存和取的顺序不一致

没有索引

不可以重复(保证元素的唯一)

Set集合的学习重点, 放在学习如何保证元素的唯一

1. HashSet

  • 存储Java已经提供好的数据类型
  • 可以保证元素的唯一
HashSet<String> set = new HashSet<>();

set.add("潮汐海灵");
set.add("潮汐海灵");
set.add("丽桑卓");
set.add("丽桑卓");
set.add("劫");
set.add("劫");

System.out.println(set); //[潮汐海灵, 劫, 丽桑卓]

2. HashSet存储自定义类型(根据hashCode()返回值判断是否存入)

在这里插入图片描述

如果存入元素的hashCode()返回值是不同, 元素会直接存入集合

如何存入元素的hashCode()和集合中已有的hashCode()相同, 会调用equals()进行比较

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

        // 存放Hero类型的Set集合
        HashSet<Hero> set = new HashSet<>();

        // 添加元素
        set.add(new Hero("潮汐海灵", "大鲨鱼"));
        set.add(new Hero("潮汐海灵", "大鲨鱼"));
        set.add(new Hero("亚索", "哈萨key"));
        set.add(new Hero("亚索", "哈萨key"));
        set.add(new Hero("伊泽瑞尔", "精准弹幕"));
        set.add(new Hero("伊泽瑞尔", "精准弹幕"));

        // 遍历打印
        for (Hero hero : set) {
            System.out.println(hero); // 去重之后的结果
        }

    }
}
// =====================================================================
public class Hero {
    private String name;
    // 技能
    private String skill;

    public Hero() {
    }

    public Hero(String name, String skill) {
        this.name = name;
        this.skill = skill;
    }

    // get/set省略


    /*
        重写之后比较的是属性值
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Hero hero = (Hero) o;
        return Objects.equals(name, hero.name) &&
                Objects.equals(skill, hero.skill);
    }

    /*
        使用属性, 进行一系列的计算(为了属性不同, 让hashCode()一定不同), 最终得到一个值
     */
    @Override
    public int hashCode() {
        return Objects.hash(name, skill);
    }

    @Override
    public String toString() {
        return "Hero{" +
                "name='" + name + '\'' +
                ", skill='" + skill + '\'' +
                '}';
    }
}

总结: 如果使用HashSet集合存储自定义对象, 想保证元素的唯一, 需要重写hashCode()和equals()方法

3. LinkedHashSet

LinkedHashSet是HashSet集合的子类

Set集合中惟一一个有序的集合

特点

  • 可以保证元素的唯一
  • 由于是链表, 可以保证有序(怎么存就怎么取)

4. TreeSet

TreeSet集合能够排序, 是因为存入集合的元素自身具备比较功能

特点

  • 可以保证元素的唯一
  • 可以对元素进行从小到大的排序(自然排序)
TreeSet中的二叉树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l0B9v0uj-1606620931630)(img/image-20200703104112465.png)]

Collections

用来操作集合的工具类

1. 可变参数

ackage com.free.collections;
/*
    可变参数
        格式: 数据类型 ... 变量名
        特点: 如果方法的形参是可变参数, 调用方法时可以传入对应数据类型的任意个参数

        可变参数, 其实就是一个数组

        注意事项: 形参中如果有可变参数, 将可变参数放到参数列表的最后
 */
public class Demo01 {

    public static void main(String[] args) {
        System.out.println(getSum());
        System.out.println(getSum(1));
        System.out.println(getSum(1, 2));
        System.out.println(getSum(1,2,3));
        System.out.println(getSum(1,2,3,4,5,6));
        System.out.println(getSum(1,2,3,4,5,6,7,8,9,10));
    }

    public static int getSum(int... a) { // a : 数组
        int sum = 0;

        // 遍历
        for (int i : a) {
            sum += i;
        }

        return sum;
    }






    // 问题, 方法的参数列表中, 可能会传入同一种数据类型的任意个参数
    /*public static int getSum(int a, int b) {
        return a + b;
    }

    public static int getSum(int a, int b , int c) {
        return a + b + c;
    }*/
    // ...

}

2. 常用功能

  • addAll
public static <T> boolean addAll(Collection<T> c, T... elements)
    将elements可变参数中的所有内容, 添加到c集合中
    
    由于形参定义的是Collection集合, 随意这个方法可以添加所有Colleciton的子类(ArrayList, HashSet ...)
  • shuffle
public static void shuffle(List<?> list) : 随机置换
    注意: 只能操作List集合, 不能操作Set集合
  • sort
public static <T extends Comparable<T>> void sort(List<T> list) :
	排序
    集合泛型的数据类型, 必须是Comparable的实现类
        
	Comparable: 自然排序 -> 从小到大的升序排序

3. 比较器

public static <T> void sort(List<T> list, Comparator<T> c)
   	
(1) 集合元素是Integer类型的排序
		ArrayList<Integer> list = new ArrayList<>();
		
		// 添加元素
		list.add(70);
        list.add(50);
        list.add(20);
        list.add(50);
        list.add(40);

        // 匿名内部类:  new 接口名() {}  ->  实现了该接口的实现类对象
        Collections.sort(list, new Comparator<Integer>() {
            /*
                返回值的作用
                    1 - 2 -> 升序
                    2 - 1 -> 降序
             */

            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });


        System.out.println(list);
[70, 50, 50, 40, 20]
(2) 集合元素是String类型的排序
package com.itheima._03comparator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

/*

    String字符串中比较器排序的使用

 */
public class Demo02 {
    public static void main(String[] args) {

        ArrayList<String> list = new ArrayList<>();

        Collections.addAll(list, "aaa", "b", "cc");

        // 自然排序: 首字母升序
        // Collections.sort(list);
        // 首字母降序
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                // 字符串对象.compareTo(另一个字符串对象)
                // return o1.compareTo(o2); // 字典顺序升序
                // return o2.compareTo(o1); // 字典顺序降序

                // return o1.length() - o2.length(); // 长度升序
                return o2.length() - o1.length(); // 长度降序
            }
        });

        System.out.println(list);


    }
}

(3) 集合元素是自定义类型的简单排序
package com.itheima._03comparator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

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

        ArrayList<Hero> list = new ArrayList<>();

        list.add(new Hero("a奈文摩尔", 1));
        list.add(new Hero("b雷克萨", 3));
        list.add(new Hero("b雷克萨", 4));
        list.add(new Hero("c奥蕾莉亚", 2));

        // 如果两种排序功能都存在, 使用比较器排序
        // 不报错: Integer, String
        Collections.sort(list, new Comparator<Hero>() {
            @Override
            public int compare(Hero o1, Hero o2) {
                return o2.getName().compareTo(o1.getName());
            }
        });

        System.out.println(list);

    }
}

(4) 集合元素是自定义类型的进阶排序
package com.itheima._03comparator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/*

    先按照姓名降序, 如果姓名相同, 按照排名升序

 */
public class Demo04 {
    public static void main(String[] args) {

        ArrayList<Hero> list = new ArrayList<>();

        list.add(new Hero("a奈文摩尔", 1));
        list.add(new Hero("b雷克萨", 4));
        list.add(new Hero("b雷克萨", 2));
        list.add(new Hero("b雷克萨", 3));
        list.add(new Hero("b雷克萨", 1));
        list.add(new Hero("c奥蕾莉亚", 2));

        Collections.sort(list, new Comparator<Hero>() {
            @Override
            public int compare(Hero o1, Hero o2) {
                // 如果姓名相同, 比较rank
                // o2.getName().compareTo(o1.getName() 返回 0, 说明姓名相同
                int num = o2.getName().compareTo(o1.getName());
                /*if (num == 0) {
                    return o1.getRank() - o2.getRank();
                } else { // 姓名不同
                    return num;
                }*/

                return num == 0 ? o1.getRank() - o2.getRank() : num;
            }
        });

        System.out.println(list);

    }
}

(5) TreeSet也可以使用比较器
public TreeSet(Comparator<? super E> comparator)

有Studnet类 [name:String , age:int, math:int, english:int, chinese:int, totalScore:int]

  1. 先按照总成绩降序排序
  2. 总成绩相同, 按照语文降序
  3. 总成绩和语文都相同, 按照数学降序
  4. 总成绩, 语文, 数学相同, 按照年龄降序

Map集合

1. Map集合的特点

  • 将键映射到值的对象, 双列集合, 以键值对的形式存在
  • Map集合中的键是唯一的
  • 一个键最多对应一个值(可能没有值(null), 一个值)

键值对形式存在

键是唯一的

2. 双列集合的继承体系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pmyzhrmo-1606620931633)(img/image-20200703144211465.png)]

3. Map中的常用方法

Map<K,V>
K : key 键
V : value 值
(1) 增删改查
  • 增加/修改
V put(K key, V value) : 向集合中添加一组键值对, 
						如果键相同, 值覆盖
                            
      返回值: 返回的是替换掉的值
  • 删除
V remove(Object key) : 删除指定键对应的, 键值对
      返回值: 返回被删除掉的值
  • 查询
V get(Object key) : 根据键, 获取值
(2) 其他操作
  • 长度
int size() : 获取集合中, 键值对 的个数
  • 判断是否包含
boolean containsKey(Object key) : 判断集合中是否包含指定键
boolean containsValue(Object value) : 判断集合中是否包含指定值
  • 获取所有键
Set<K> keySet() : 获取Map集合中所有键存在的Set集合
    
    返回值: 返回存放所有键的Set集合
  • 获取所有值
Collection<V> values() : 获取Map集合中所有值存在的Colletion集合

4. Map集合的遍历

(1) 键找值
  1. 获取所有的键
  2. 通过键获取值
        HashMap<String, String> map = new HashMap<>();

        map.put("C罗", "葡萄牙");
        map.put("梅西", "阿根廷");
        map.put("德布劳内", "比利时");
        map.put("内马尔", "巴西");
        map.put("武磊", "中国");

        // 获取所有键 -> Set<K>
        Set<String> keySet = map.keySet();
        // 遍历Set集合, 获取每一个键
        for (String key : keySet) {
            // 根据键获取值
            String value = map.get(key);
            System.out.println(key + "=" + value);
        }
(2) 键值对
Set<Map.Entry<K,V>> entrySet() : 获取所有键值对 所在的 Set集合
Set<键值对>
        HashMap<String, String> map = new HashMap<>();

        map.put("C罗", "葡萄牙");
        map.put("梅西", "阿根廷");
        map.put("德布劳内", "比利时");
        map.put("内马尔", "巴西");
        map.put("武磊", "中国");

        // Set集合存放String -> Set<String>
        // Set集合中存放的是  键值对
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        // 遍历Set集合, 获取每一个键值对
        // Map.Entry<String, String> -> 键值对
        for (Map.Entry<String, String> entry : entrySet) {
            // 键值对类型中有两个功能: getKey(), getValue()
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "=" + value);
        }

5. HashMap存储自定义类型

如果HashMap存储自定义对象作为键, 想保证键的唯一, 需要重写hashCode()和equals()

HashMap 的 7 种遍历方式+性能分析!

6. LinkedHashMap

特点

  • 保证键的唯一
  • 有序(怎么存就怎么取)

7. TreeMap

特点

  • 保证键的唯一
  • 可以根据键进行排序

8. 集合的嵌套

  • ArrayList集合嵌套HashMap
  • 在这里插入图片描述
package com.free._04map;

import java.util.ArrayList;
import java.util.HashMap;

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

        // 创建ArrayList集合, 集合中存放HashMap
        ArrayList<HashMap<String , String>> list = new ArrayList<>();

        // 创建HashMap
        HashMap<String, String> map1 = new HashMap<>();
        map1.put("姓名", "张三");
        map1.put("年龄", "23");
        map1.put("性别", "男");

        HashMap<String, String> map2 = new HashMap<>();
        map2.put("姓名", "李四");
        map2.put("年龄", "24");
        map2.put("性别", "女");

        HashMap<String, String> map3 = new HashMap<>();
        map3.put("姓名", "王五");
        map3.put("年龄", "25");
        map3.put("性别", "男");

        // 将Map集合添加到List中
        list.add(map1);
        list.add(map2);
        list.add(map3);

        // 遍历List集合, 获取到每一个元素, 元素是HashMap类型
        for (HashMap<String, String> map : list) {
            System.out.println("~~~~~~~~~~~~~");
            // 遍历HashMap
            for (String key : map.keySet()) {
                System.out.println(key + "=" + map.get(key));
            }
        }
    }
}

9. Map集合的练习

  • 需求: 键盘录入一个字符串, 获取字符串中每一个字符出现的次数
  • 键盘录入: abcdabcdab -> a(3)b(3)c(2)d(2)

package com.free._01test;

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

/*
    键盘录入字符串, 获取字符串中每个字符出现的次数

      "abcdabc" -> a(2)b(2)c(2)d(1)
 */
public class Test01 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        // 获取键盘录入的字符串
        System.out.println("请输入字符串:");
        String s = sc.nextLine();
        // 创建Map集合
        HashMap<Character, Integer> map = new HashMap<>();

        // 遍历字符串, 获取到每一个字符
        //toCharArray() 将此字符串转换为新的字符数组。 
        for (char c : s.toCharArray()) {
            // 如果字符不存在, 值为1
            /*if (!map.containsKey(c)) {
                // 如果这个字符不存在, 那一定是第一次获取到这个字符串, 值写成1
                map.put(c, 1);
            } else {
                // 获取到之前的值
                int times = map.get(c);
                // 集合中已经存在这个键
                map.put(c, times + 1);
            }*/

            // 三元运算符
            map.put(c, !map.containsKey(c) ? 1 : map.get(c) + 1);

        }

        // 创建字符串缓冲区(StringBuilder)
        StringBuilder sb = new StringBuilder();
        // 遍历map集合
        for (Map.Entry<Character, Integer> entry : map.entrySet()) {
            sb.append(entry.getKey()).append("(").append(entry.getValue()).append(")");
        }

        System.out.println(sb);

    }
}

四. HashMap集合的源码解析

  • jdk7以及以前 : 数组 + 链表
  • jdk8开始: 数组 + 链表 + 红黑树
public class HashMap<K,V> {
    /**
     * The default initial capacity - MUST be a power of two.
     * 默认初始容量 : 16
     * 1 << n  :  1 * 2的n次方
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
    
    // 最大容量
    static final int MAXIMUM_CAPACITY = 1 << 30;
    
    /**
     * The load factor used when none specified in constructor.
     * 负载因子(加载因子) : 0.75
     * 当占用的容量, 超过当前最大容量的0.75倍, 就扩容
     * 
     * 16 * 0.75 = 12 -> 超过12就扩容
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    
    /*
    * 链表的长度大于等于8的时候, 将链表转换成红黑树
    * 还有一个条件: 容器中元素的个数大于等于64
    */
    static final int TREEIFY_THRESHOLD = 8;
    
    static final int MIN_TREEIFY_CAPACITY = 64;
    
    /*
    * 如果红黑树的长度小于6, 会退化成链表
    */
    static final int UNTREEIFY_THRESHOLD = 6;
    
}

位移算法。 例如:4<<2
4的二进制是:0000 0100
<<表示往左移两位:00 010000
只要把4转换成二进制,往左移两位,再转换成10进制得出结果既是:16
更简单的计算方法就是 4<< n 等效于 4 乘以 2的 N 次方

斗地主排序

在这里插入图片描述

package com.free._01test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.TreeSet;

/*
    斗地主排序
 */
public class Test03 {
    public static void main(String[] args) {
        // 创建HashMap集合, 键是编号(从小到大, 牌的大小顺序), 值是牌
        HashMap<Integer, String> map = new HashMap<>();
        // 创建ArrayList集合, 存放的就是Map集合中的键
        ArrayList<Integer> list = new ArrayList<>();

        // 发牌
        // 定义一个int类型的变量, 当做牌的大小顺序(编号)
        int count = 1;

        // 创建用来存放点数的容器 - 数组
        String[] numbers = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};

        // 创建用来存放花色的容器
        String[] colors = {"♦", "♣", "♥", "♠"};

        // 拼接牌
        // 先遍历点数
        for (String number : numbers) {
            // 再遍历花色
            for (String color : colors) {
                String poker = color + number;
                // 存入Map集合
                map.put(count, poker);
                // count存入ArrayList集合
                list.add(count);
                count++;
            }
        }
        // 添加大小王
        map.put(count, "♖");
        list.add(count++);

        map.put(count, "♕");
        list.add(count);

        // 洗牌
        Collections.shuffle(list);

        // 发牌
        TreeSet<Integer> me = new TreeSet<>();
        TreeSet<Integer> gaoJin = new TreeSet<>();
        TreeSet<Integer> zhouXingXing = new TreeSet<>();
        TreeSet<Integer> left = new TreeSet<>();

        for (int i = 0; i < list.size(); i++) {
            if (i >= list.size() - 3) {
                // 注意: 添加的是i索引位置的元素
                left.add(list.get(i));
            } else if (i % 3 == 0) {
                me.add(list.get(i));
            } else if (i % 3 == 1) {
                gaoJin.add(list.get(i));
            } else {
                zhouXingXing.add(list.get(i));
            }
        }

        // 看牌
        lookPoker(map, left, "底牌:");
        lookPoker(map, me, "我:");
        lookPoker(map, gaoJin, "高进:");
        lookPoker(map, zhouXingXing, "周星星:");

    }

    public static void lookPoker(HashMap<Integer, String> map, TreeSet<Integer> set, String name) {
        StringBuilder sb = new StringBuilder(name);

        // 遍历Set集合
        for (Integer key : set) {
            String poker = map.get(key);
            sb.append(poker).append(" ");
        }

        System.out.println(sb);
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值