Java集合基础大总结

Java集合大总结

java集合关系图

在这里插入图片描述

引用地址:https://blog.csdn.net/sdhgood/article/details/38849477

在这里插入图片描述
在这里插入图片描述

引用地址:https://blog.csdn.net/weixin_44494373/article/details/105898218

java集合的概述

集合:集合是java中提供的一种容器,可以用来存储多个数据。

集合和数组既然都是容器,它们有啥区别呢?

  • 数组的长度是固定的。集合的长度是可变的。
  • 数组中存储的是同一类型的元素,可以存储基本数据类型值。集合存储的都是对象。而且对象的类型可以不一致。在开发中一般当对象多的时候,使用集合进行存储。

java.util.Collection接口

介绍

所有单列集合的最顶层的接口,里边定义了所有单列集合共性的方法任意的单列集合都可以使用Collection接口中的方法

共性的方法

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

注意

java.util.Collection类是zh一个接口,想要创建只能使用多态,使用多态需要注意:多态子类如果重写了父类的方法,优先子类;如果调用子类特有的方法,父类没有,会报错。

示例

package com.itheima.demo01Collection;

import java.util.Collection;
import java.util.HashSet;

public class Demo01Collection {
    public static void main(String[] args) {
        //创建集合对象,可以使用多态
        //Collection<String> coll = new ArrayList<>();
        Collection<String> coll = new HashSet<>();
        System.out.println(coll);//重写了toString方法  []

        /*
            public boolean add(E e): 把给定的对象添加到当前集合中 。
            返回值是一个boolean值,一般都返回true,所以可以不用接收
         */
        boolean b1 = coll.add("张三");
        System.out.println("b1:"+b1);//b1:true
        System.out.println(coll);//[张三]
        coll.add("李四");
        coll.add("李四");
        coll.add("赵六");
        coll.add("田七");
        System.out.println(coll);//[张三, 李四, 赵六, 田七]

        /*
            public boolean remove(E e): 把给定的对象在当前集合中删除。
            返回值是一个boolean值,集合中存在元素,删除元素,返回true
                                集合中不存在元素,删除失败,返回false
         */
        boolean b2 = coll.remove("赵六");
        System.out.println("b2:"+b2);//b2:true

        boolean b3 = coll.remove("赵四");
        System.out.println("b3:"+b3);//b3:false
        System.out.println(coll);//[张三, 李四, 田七]

        /*
            public boolean contains(E e): 判断当前集合中是否包含给定的对象。
            包含返回true
            不包含返回false
         */
        boolean b4 = coll.contains("李四");
        System.out.println("b4:"+b4);//b4:true

        boolean b5 = coll.contains("赵四");
        System.out.println("b5:"+b5);//b5:false

        //public boolean isEmpty(): 判断当前集合是否为空。 集合为空返回true,集合不为空返回false
        boolean b6 = coll.isEmpty();
        System.out.println("b6:"+b6);//b6:false

        //public int size(): 返回集合中元素的个数。
        int size = coll.size();
        System.out.println("size:"+size);//size:3

        //public Object[] toArray(): 把集合中的元素,存储到数组中。
        Object[] arr = coll.toArray();
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }

        //public void clear() :清空集合中所有的元素。但是不删除集合,集合还存在
        coll.clear();
        System.out.println(coll);//[]
        System.out.println(coll.isEmpty());//true
    }
}
运行结果

在这里插入图片描述

集合的遍历

java.util.Iterator接口

介绍

在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口java.util.IteratorIterator接口也是Java集合中的一员,但它与CollectionMap接口有所不同,Collection接口与Map接口主要用于存储元素,而Iterator主要用于迭代访问(即遍历)Collection中的元素,因此Iterator对象也被称为迭代器(对集合进行遍历)

常用方法
boolean hasNext(): 判断集合中还有没有下一个元素可以迭代,有就返回true,没有就返回false
E next(): 返回迭代的下一个元素
注意

Iterator迭代器是一个接口,我们无法直接使用,需要使用Iterator接口的实现类对象,获取实现类的方式比较特殊。Collection接口中有一个方法,叫iterator(),这个方法返回的就是迭代器的实现类对象。
Iterator iterator(): 返回在此collection的元素上进行迭代的迭代器

迭代器的使用步骤(重点)

1.使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)
2.使用Iterator接口中的方法hasNext判断还有没有下一个元素
3.使用Iterator接口中的方法next取出集合中的下一个元素

示例
package com.itheima.demo02Iterator;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class Demo01Iterator {
    public static void main(String[] args) {
        Collection<String> coll = new ArrayList<>();
        coll.add("姚明");
        coll.add("科比");
        coll.add("麦迪");
        coll.add("詹姆斯");
        coll.add("艾弗森");

        /*
        * 1.使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)
        * 注:Iterator<E>接口也是有泛型的,迭代器的泛型跟着集合走,集合是什么泛型,迭代器就是什么泛型
        * */
        //多态 接口             实现类对象
        Iterator<String> it = coll.iterator();

        /*
        * 发现使用迭代器取出集合中元素的代码是一个重复的过程
        * 所以我们可以使用循环优化
        * 不知道集合中有多少元素,使用while循环
        * 循环结束的条件,hasNext方法返回值false
        * */

        while(it.hasNext()) {
            String s = it.next();
            System.out.println(s);
        }

        System.out.println("==============================");

        for(Iterator<String> it2 = coll.iterator();it2.hasNext();) {
            String s = it2.next();
            System.out.println(s);
        }
        // 推荐使用while循环

        //2.使用Iterator接口中的方法hasNext判断还有没有下一个元素
        //注意:因为上面已经对it进行遍历,此时it.hasNext()方法返回一定是false
        boolean b = it.hasNext();
        System.out.println(b);  //true

        //3.使用Iterator接口中的方法next取出集合中的下一个元素
        String s = it.next();
        System.out.println(s);  //姚明

        b = it.hasNext();
        System.out.println(b);
        s = it.next();
        System.out.println(s);

        b = it.hasNext();
        System.out.println(b);
        s = it.next();
        System.out.println(s);

        b = it.hasNext();
        System.out.println(b);
        s = it.next();
        System.out.println(s);

        b = it.hasNext();
        System.out.println(b);
        s = it.next();
        System.out.println(s);

        b = it.hasNext();
        System.out.println(b);  // 没有元素,返回false
        s = it.next();  // 没有元素,再取出元素会抛出NoSuchElementException没有元素异常
        System.out.println(s);
    }
}
运行结果

在这里插入图片描述

内存图

在这里插入图片描述

增强for循环

介绍

底层使用的也是迭代器,使用for循环的格式,简化了迭代器的书写。是JDK 1.5之后出现的新特性

public interface Collection extends Iterable:故所有的单列集合都可以使用增强for循环,实现public interface Iterable这个接口允许对象成为 “foreach” 语句的目标,即可以使用增强for循环,作用是用来遍历集合和数组

格式
for(集合/数组的数据类型 变量名: 集合名/数组名) {
            sout(变量名);
}
注意

1.增强for循环必须有被遍历的目标,目标只能是Collection或者是数组。
2.增强for循环仅仅为遍历操作出现。

示例
package com.itheima.demo02Iterator;

import java.util.ArrayList;

public class Demo02Foreach {
    
    public static void main(String[] args) {
        demo01();
        demo02();
    }
    
    //使用增强for循环遍历集合
    private static void demo02() {
        ArrayList<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        list.add("ddd");
        for(String str : list) {
            System.out.println(str);
        }

    }

    //使用增强for循环遍历数组
    private static void demo01() {
        int[] arr = {1, 2, 3, 4, 5};
        for(int i : arr) {
            System.out.println(i);
        }
    }
}

泛型

泛型概述

我们都知道集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升成Object类型。当我们在取出每一个对象,并且进行相应的操作,这时必须采用类型转换。

观察下面代码:

package com.itheima.demo03Generic;

import java.util.ArrayList;
import java.util.Iterator;

public class Demo01Generic {
    public static void main(String[] args) {
        show01();
    }

    /*
    * 创建集合对象,不使用泛型
    * 好处:
    *     集合不使用泛型,默认类型是Object类型,可以存储任意类型的数据
    * 弊端:
    *     不安全,会引发异常
    * */
    private static void show01() {
        ArrayList list = new ArrayList();
        list.add("abc");
        list.add(1);

        //使用迭代器遍历集合
        //获取迭代器
        Iterator it = list.iterator();
        //使用迭代器中的hasNext和next方法遍历集合
        while(it.hasNext()) {
            //取出元素也是Object类型
            Object obj = it.next();
            System.out.println(obj);

            String s = (String) obj;
            System.out.println(s.length());
        }
    }
}
运行结果

在这里插入图片描述

问题

程序在运行时发生了问题java.lang.ClassCastException。为什么会发生类型转换异常呢?我们来分析下:由于集合中什么类型的元素都可以存储。导致取出时强转引发运行时 ClassCastException。

分析

想要使用String类特有的方法,length获取字符串的长度,不能使用。因为多态 Object obj = “abc”; 父类不能使用子类特有的方法
会抛出ClassCastException类型转换异常,不能把Integer类型转换为String类型。

解决

怎么来解决这个问题呢?

ArrayList虽然可以存储各种对象,但实际上通常ArrayList只存储同一类型对象。例如都是存储字符串对象。因此在JDK5之后,新增了泛型(Generic)语法,让你在设计API时可以指定类或方法支持泛型,这样我们使用API的时候也变得更为简洁,并得到了编译时期的语法检查。

泛型:可以在类或方法中预支地使用未知的类型。

tips:一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型。

使用泛型的好处

上一节只是讲解了泛型的引入,那么泛型带来了哪些好处呢?

  • 将运行时期的ClassCastException,转移到了编译时期变成了编译失败。
  • 避免了类型强转的麻烦。

使用泛型后的代码:

package com.itheima.demo03Generic;

import java.util.ArrayList;
import java.util.Iterator;

public class Demo01Generic {
    public static void main(String[] args) {
        show02();
    }
    
    /*
    * 创建集合对象,使用泛型
    * 好处:
    *     1.避免了类型转换的麻烦,存储的是什么类型,取出的就是什么类型
    *     2.把运行期的异常(代码运行之后会抛出的异常),提升到了编译期(写代码的时候回报错)
    * 弊端:
    *     泛型是什么类型,只能存储什么类型的数据
    * */
    private static void show02() {
        ArrayList<String> list = new ArrayList<>();
        list.add("abc");
//        list.add(1) //报错

        //使用迭代器遍历集合
        Iterator<String> it = list.iterator();
        while(it.hasNext()) {
            String s = it.next();
            System.out.println(s + "->" + s.length());
        }
    }

}

tips:泛型是数据类型的一部分,我们将类名与泛型合并一起看做数据类型。

泛型的定义与使用

我们在集合中会大量使用到泛型,这里来完整地学习泛型知识。

泛型,用来灵活地将数据类型应用到不同的类、方法、接口当中。将数据类型作为参数进行传递。

定义和使用含有泛型的类
定义格式
修饰符 class 类名<代表泛型的变量> { }

例如,API中的ArrayList集合:

class ArrayList<E>{ 
    public boolean add(E e){ }

    public E get(int index){ }
   	....   
}
使用泛型: 即什么时候确定泛型。

在创建对象的时候确定泛型

例如,ArrayList<String> list = new ArrayList<String>();

此时,变量E的值就是String类型,那么我们的类型就可以理解为:

class ArrayList<String>{ 
     public boolean add(String e){ }

     public String get(int index){  }
     ...
}
举例自定义泛型类和使用

定义

package com.itheima.demo03Generic;
/*
* 定义一个含有泛型的类,模拟ArrayList集合
* */
public class GenericClass<E> {
    private E name;

    public E getName() {
        return name;
    }

    public void setName(E name) {
        this.name = name;
    }
}

使用

package com.itheima.demo03Generic;

public class Demo02GenericClass {
    public static void main(String[] args) {
        //不写泛型默认是Object类型
        GenericClass gc = new GenericClass();
        gc.setName("只能是字符串");
        Object obj = gc.getName();

        //创建GenericClass对象,泛型使用Integer类型
        GenericClass<Integer> gc2 = new GenericClass<>();
        gc2.setName(1);

        Integer name = gc2.getName();
        System.out.println(name);

        //创建GenericClass对象,泛型使用String类型
        GenericClass<String> gc3 = new GenericClass<>();
        gc3.setName("小明");

        String name2 = gc3.getName();
        System.out.println(name2);

    }
}

运行结果

在这里插入图片描述

定义和使用含有泛型的方法
定义格式

定义含有泛型的方法:泛型定义在方法的修饰符和返回值类型之间

修饰符 <代表泛型的变量> 返回值类型 方法名(参数(使用泛型)) { }

例如:

package com.itheima.demo03Generic;

public class GenericMethod {
    //定义一个含有泛型的方法
    public <M> void method01(M m) {
        System.out.println(m);
    }

    //定义一个含有泛型的静态方法
    public static <S> void method02(S s) {
        System.out.println(s);
    }
}
使用格式

含有泛型的方法,在调用方法的时候确定泛型的数据类型。传递什么类型的参数,泛型就是什么类型。

package com.itheima.demo03Generic;
/*
* 测试含有泛型的方法
* */
public class Demo03GenericMethod {
    public static void main(String[] args) {
        //创建GenericMethod对象
        GenericMethod gm = new GenericMethod();

        /*
        * 调用含有泛型的方法method01
        * 传递什么类型,泛型就是什么类型
        * */

        gm.method01(10);
        gm.method01("abc");
        gm.method01(8.8);
        gm.method01(true);

        gm.method02("静态方法,不建议创建对象使用");

        //静态方法,通过类名.方法名(参数)可以直接使用
        GenericMethod.method02("静态方法");
        GenericMethod.method02(1);
    }
}
运行结果

在这里插入图片描述

定义和使用含有泛型的接口
定义格式
修饰符 interface接口名 <代表泛型的变量> { }

例如:

package com.itheima.demo03Generic;
/*
* 定义含有泛型的接口
* */
public interface GenericInterface<I> {
    public abstract void method(I i);
}
使用格式

第一种使用方式:定义接口的实现类,实现接口,指定接口的泛型

package com.itheima.demo03Generic;

public class GenericInterfaceImpl1 implements GenericInterface<String> {

    @Override
    public void method(String s) {
        System.out.println(s);
    }
}

jdk源码实现示例

//java.util.Iterator接口
public interface Iterator<E> {
    E next();
}
//Scanner类实现了Iterator接口,并指定接口的泛型为String,所以重写了next方法泛型默认是String
public final class Scanner implements Iterator<String> {
    public String next() {}
}

第二种使用方式:接口使用什么泛型,实现类就使用什么泛型,类跟着接口走

就相当于定义一个含有泛型的类,创建对象的时候确定泛型的类型。

package com.itheima.demo03Generic;

public class GenericInterfaceImpl2<I> implements GenericInterface<I> {
    @Override
    public void method(I i) {
        System.out.println(i);
    }
}

jdk源码实现示例

public interface List<E> {
    boolean add(E e);
	E get(int index);
}
public class ArrayList<E> implements List<E> {
    public boolean add(E e) {};
    public E get(int index) {};
}
测试代码
package com.itheima.demo03Generic;
/*
* 测试含有泛型的接口
* */
public class Demo04GenericInterface {
    public static void main(String[] args) {
        //创建GenericInterfaceImpl1对象
        GenericInterfaceImpl1 gi1 = new GenericInterfaceImpl1();
        gi1.method("字符串");

        //创建GenericInterfaceImpl2对象,此时了确定泛型的类型
        GenericInterfaceImpl2<Integer> gi2 = new GenericInterfaceImpl2<>();
        gi2.method(10);

        GenericInterfaceImpl2<Double> gi3 = new GenericInterfaceImpl2<>();
        gi3.method(8.899);
    }
}
运行结果

在这里插入图片描述

泛型的通配符

当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。

通配符基本使用

泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。

package com.itheima.demo03Generic;

import java.util.ArrayList;
import java.util.Iterator;

public class Demo05Generic {
    public static void main(String[] args) {
        ArrayList<Integer> list01 = new ArrayList<>();
        list01.add(1);
        list01.add(2);

        ArrayList<String> list02 = new ArrayList<>();
        list02.add("a");
        list02.add("b");

        printArray(list01);
        printArray(list02);

        //定义的时候不能使用泛型
//        ArrayList<?> list03 = new ArrayList<>();
//        list03.add("sjacj");
//        list03.add(12);
    }
    /*
    * 定义一个方法,能遍历所有类型的ArrayList集合
    * 这时候我盟不知道ArrayList集合使用什么数据类型,可以用泛型的通配符?此时只能接受数据,不能往该集合中存储数据。
    * 注:
    *   泛型是没有继承概念的
    * */
    public static void printArray(ArrayList<?> list) {
        //使用迭代器遍历集合
        Iterator<?> it = list.iterator();
        while(it.hasNext()) {
            //it.next()方法,取出的元素是Object,可以接收任意的数据类型
            Object o = it.next();
            System.out.println(o);
        }
    }
}
运行结果

在这里插入图片描述

注意

1、泛型不存在继承关系 Collection list = new ArrayList();这种是错误的。

2、泛型的通配符不能创建对象使用,只能作为方法的参数使用。此时只能接受数据,不能往该集合中存储数据。

其实上面那个方法也可以这样写,效果一样

public static <T> void printArray(ArrayList<T> list) {
    //使用迭代器遍历集合
    Iterator<?> it = list.iterator();
    while(it.hasNext()) {
        //it.next()方法,取出的元素是Object,可以接收任意的数据类型
        Object o = it.next();
        System.out.println(o);
    }
}
通配符高级使用—受限泛型

之前设置泛型的时候,实际上是可以任意设置的,只要是类就可以设置。但是在JAVA的泛型中可以指定一个泛型的上限下限

  • 泛型的上限限定

    格式:? extends E

    意义:代表使用的泛型只能是E类型的子类/本身

  • 泛型的下限限定

    格式:? super E

    意义:代表使用的泛型只能是E类型的父类/本身

比如:现已知Object类,String 类,Number类,Integer类,其中Number是Integer的父类

package com.itheima.demo03Generic;

import java.util.ArrayList;
import java.util.Collection;

public class Demo06Generic {
    public static void main(String[] args) {
        Collection<Integer> list1 = new ArrayList<>();
        Collection<String> list2 = new ArrayList<>();
        Collection<Number> list3 = new ArrayList<>();
        Collection<Object> list4 = new ArrayList<>();

        getElement1(list1);
//        getElement1(list2); //报错
        getElement1(list3);
//        getElement1(list4); //报错

//        getElement2(list1); //报错
//        getElement2(list2); //报错
        getElement2(list3);
        getElement2(list4);

        /*
        * 类与类之间的继承关系
        * Integer extends Number extends Object
        * String extends Object
        * */

    }
    //泛型的上限:此时的泛型?,必须是Number类型或Number类的子类
    private static void getElement1(Collection<? extends Number> coll) {}
    //泛型的下限:此时的泛型?,必须是Number类型或Number类的父类
    private static void getElement2(Collection<? super Number> coll) {}

}

集合综合案例

案例介绍

按照斗地主的规则,完成洗牌发牌的动作。
具体规则:使用54张牌打乱顺序,三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。

案例分析

  • 准备牌:

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

  • 发牌

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

  • 看牌

    直接打印每个集合。

代码实现

package com.itheima.demo04Test;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

/*
* 斗地主综合案例:
*        1.准备牌
*        2.洗牌
*        3.发牌
*        4.看牌
* */
public class DouDiZhu {
    public static void main(String[] args) {
        //1.准备牌
        //定义一个存储54张牌的ArrayList集合,泛型用String
        ArrayList<String> poker = new ArrayList<>();
        //定义两个数组,一个数组用来存储牌的花色,一个数组用来存储牌的序号
        String[] colors = {"♥", "♣", "♠", "♦"};
        String[] numbers = {"2", "A", "K", "Q", "J", "3", "4", "5", "6", "7", "8", "9", "10"};
        //先把大王和小王存储到poker集合中
        poker.add("大王");
        poker.add("小王");
        //循环嵌套遍历两个数组,组装52张牌
        for (String color : colors) {
            for (String number : numbers) {
                poker.add(color + number);
            }
        }
        System.out.println(poker);

        //2.洗牌
        //使用集合工具类Collections中的方法
        //static void shuffle(List<?> list) 使用默认随机源对指定列表进行置换
        Collections.shuffle(poker);
        System.out.println(poker);

        //3.发牌
        //定义四个集合,存储玩家的牌和底牌
        ArrayList<String> player01 = new ArrayList<>();
        ArrayList<String> player02 = new ArrayList<>();
        ArrayList<String> player03 = new ArrayList<>();
        ArrayList<String> diPai = new ArrayList<>();

        /*
        * 遍历poker集合,获取每一张牌
        * 使用poker集合的索引%3给3个玩家轮流发牌
        * 剩余3张牌给底牌
        * 注:
        *   先判断底牌(i>=51),否则牌就发没了
        * */
        for (int i = 0; i < poker.size(); i++) {
            //获取每一张牌
            String p = poker.get(i);
            //轮流发牌
            if(i >= 51) {
                //先给底牌发牌
                diPai.add(p);
            } else if (i % 3 == 0) {
                player01.add(p);
            } else if (i % 3 == 1) {
                player02.add(p);
            } else if (i % 3 == 2) {
                player03.add(p);
            }
        }

        //4.看牌
        System.out.println("刘德华:" + player01);
        System.out.println("周润发:" + player02);
        System.out.println("周星驰:" + player03);
        System.out.println("底牌:" + diPai);
    }
}

运行结果

在这里插入图片描述

数据结构

数据结构有什么用

当你用着java里面的容器类很爽的时候,你有没有想过,怎么ArrayList就像一个无限扩充的数组,也好像链表之类 的。好用吗?好用,这就是数据结构的用处,只不过你在不知不觉中使用了。

现实世界的存储,我们使用的工具和建模。每种数据结构有自己的优点和缺点,想想如果Google的数据用的是数 组的存储,我们还能方便地查询到所需要的数据吗?而算法,在这么多的数据中如何做到最快的插入,查找,删除,也是在追求更快。

我们java是面向对象的语言,就好似自动档轿车,C语言好似手动档吉普。数据结构呢?是变速箱的工作原理。你 完全可以不知道变速箱怎样工作,就把自动档的车子从 A点 开到 B点,而且未必就比懂得的人慢。写程序这件事, 和开车一样,经验可以起到很大作用,但如果你不知道底层是怎么工作的,就永远只能开车,既不会修车,也不能 造车。当然了,数据结构内容比较多,细细的学起来也是相对费功夫的,不可能达到一蹴而就。我们将常见的数据 结构:堆栈、队列、数组、链表和红黑树 这几种给大家介绍一下,作为数据结构的入门,了解一下它们的特点即 可。

常见的数据结构

数据存储的常用结构有:栈、队列、数组、链表和红黑树。

  • stack,又称堆栈,它是运算受限的线性表,其限制是仅允许在表的一端进行插入和删除操作,不允许在其 他任何位置进行添加、查找、删除等操作。

简单的说:采用该结构的集合,对元素的存取有如下的特点

  • 先进后出(即,存进去的元素,要在后它后面的元素依次取出后,才能取出该元素)。例如,子弹压进弹 夹,先压进去的子弹在下面,后压进去的子弹在上面,当开枪时,先弹出上面的子弹,然后才能弹出下面的子弹。
  • 栈的入口、出口的都是栈的顶端位置。
    在这里插入图片描述

这里两个名词需要注意:

  • 压栈:就是存元素。即,把元素存储到栈的顶端位置,栈中已有元素依次向栈底方向移动一个位置。

  • 弹栈:就是取元素。即,把栈的顶端位置元素取出,栈中已有元素依次向栈顶方向移动一个位置。

队列
  • 队列queue,简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入, 而在表的另一端进行删除。

简单的说,采用该结构的集合,对元素的存取有如下的特点:

  • 先进先出(即,存进去的元素,要在后它前面的元素依次取出后,才能取出该元素)。例如,小火车过山洞,车头先进去,车尾后进去;车头先出来,车尾后出来。队列的入口、出口各占一侧。例如,下图中的左侧为入口,右侧为出口。

在这里插入图片描述

数组
  • 数组Array,是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。就像是一排出 租屋,有100个房间,从001到100每个房间都有固定编号,通过编号就可以快速找到租房子的人。

简单的说,采用该结构的集合,对元素的存取有如下的特点:

  • 查找元素快:通过索引,可以快速访问指定位置的元素

在这里插入图片描述

  • 增删元素慢

    • 指定索引位置增加元素:需要创建一个新数组,将指定新元素存储在指定索引位置,再把原数组元素根据索引,复制到新数组对应索引的位置。

在这里插入图片描述

  • 指定索引位置删除元素:需要创建一个新数组,把原数组元素根据索引,复制到新数组对应索引的位 置,原数组中指定索引位置元素不复制到新数组中。

在这里插入图片描述

链表
  • 链表linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时i动态生成。每 个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。我们常说的 链表结构有单向链表与双向链表,那么这里给大家介绍的是单向链表

在这里插入图片描述

简单的说,采用该结构的集合,对元素的存取有如下的特点:

  • 多个结点之间,通过地址进行连接。例如,多个人手拉手,每个人使用自己的右手拉住下个人的左手,依次类推,这样多个人就连在一起了。

在这里插入图片描述

  • 查找元素慢:想查找某个元素,需要通过连接的节点,依次向后查找指定元素

  • 增删元素快:

    • 增加元素:只需要修改连接下个元素的地址即可。

在这里插入图片描述

  • 删除元素:只需要修改连接下个元素的地址即可。

在这里插入图片描述

红黑树
  • 二叉树binary tree ,是每个结点不超过2的有序树(tree)

简单的理解,就是一种类似于我们生活中树的结构,只不过每个结点上都最多只能有两个子结点。

二叉树是每个节点最多有两个子树的树结构。顶上的叫根结点,两边被称作“左子树”和“右子树”。 如图:

在这里插入图片描述

我们要说的是二叉树的一种比较有意思的叫做红黑树,红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。

红黑树的约束:

  1. 节点可以是红色的或者黑色的

  2. 根节点是黑色的

  3. 叶子节点(特指空节点)是黑色的

  4. 每个红色节点的子节点都是黑色的

  5. 任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同

红黑树的特点:

速度特别快,趋近平衡树,查找叶子元素最少和最多次数不多于二倍

java.util.List接口 extends Collection接口

List接口的特点

  1. 它是一个元素存取有序的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、 22、33的顺序完成的)。

  2. 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)。

  3. 集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。

tips:我们已经学习过List接口的子类java.util.ArrayList类,该类中的方法都是来自List中定 义。

List接口带索引的方法(特有)

List作为Collection集合的子接口,不但继承了Collection接口中的全部方法,而且还增加了一些根据元素索引来操 作集合的特有方法,如下:

public void add(int index, E element) 在集合的指定位置插入指定元素
public E get(int index) 返回集合中指定位置的元素
public E remove(int index) 移除集合中指定位置的元素,返回的是被移除的元素
public E set(int index, E element) 用指定元素替换集合中指定位置的元素,返回值是更新前的元素

注意

操作索引的时候,一定要防止索引越界异常

  • IndexOutOfBoundsException:索引越界异常,集合会报

  • ArrayIndexOutOfBoundsException:数组索引越界异常

  • StringIndexOutOfBoundsException:字符串索引越界异常

List的子类

java.util.ArrayList集合 implements List接口

java.util.ArrayList 集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为 查询数据、遍历数据,所以 ArrayList 是最常用的集合。

**许多程序员开发时非常随意地使用ArrayList完成任何需求,并不严谨,这种用法是不提倡的。 **

示例
package com.itheima.demo01List;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Demo01List {
    public static void main(String[] args) {
        //创建一个List集合对象,多态
        List<String> list = new ArrayList<>();
        //使用add方法往集合中添加元素
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("a");
        //打印集合
        System.out.println(list); //[a, b, c, d, a] 不是地址,重写了toString方法

        //public void add(int index, E element) 在集合的指定位置插入指定元素
        //在c和d之间添加一个itheima
        list.add(3,"itheima");
        System.out.println(list); //[a, b, c, itheima, d, a]

        // public E remove(int index) 移除集合中指定位置的元素,返回的是被移除的元素
        //移除c元素
        String removeE = list.remove(2);
        System.out.println("被移除的元素:" + removeE); //被移除的元素:c
        System.out.println(list); //[a, b, itheima, d, a]

        //public E set(int index, E element) 用指定元素替换集合中指定位置的元素,返回值是更新前的元素
        //把最后一个a,替换为A
        String setE = list.set(4,"A");
        System.out.println("被替换的元素:" + setE); //被替换的元素:a
        System.out.println(list); //[a, b, itheima, d, A]

        //List集合遍历有3种方式
        //使用普通的for循环
        for (int i = 0; i < list.size(); i++) {
            //public E get(int index) 返回集合中指定位置的元素
            String s = list.get(i);
            System.out.println(s);
        }
        System.out.println("=======================");

        //使用迭代器
        Iterator<String> it = list.iterator();
        while(it.hasNext()) {
            String s = it.next();
            System.out.println(s);
        }
        System.out.println("=======================");

        //使用增强for循环遍历
        for (String s : list) {
            System.out.println(s);
        }

//        String s = list.get(5);
//        System.out.println(s); //IndexOutOfBoundsException: Index 5 out of bounds for length 5
    }
}
运行结果

在这里插入图片描述

java.util.LinkedList集合 implements List接口

java.util.LinkedList 集合数据存储的结构是链表结构。查询慢,增删快。

LinkedList是一个双向链表,如图:

在这里插入图片描述

实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。这些方法我们作为了解即可:

public void addFirst(E e) 将指定元素插入此列表的开头
public void addLast(E e) 将指定元素添加到此列表的结尾
public void push(E e) 将元素推入此列表所表示的堆栈

public E getFirst() 返回此列表的第一个元素
public E getLast() 返回此列表的最后一个元素

public E removeFirst() 移除并返回此列表的第一个元素
public E removeLast() 移除并返回此列表的最后一个元素
public E pop() 从此列表所表示的堆栈处弹出一个元素

public boolean isEmpty() 如果链表不含元素,则返回true

注意:LinkedList是List的子类,List中的方法LinkedList都是可以使用,这里就不做详细介绍,我们只需要了解LinkedList的特有方法即可。使用LinkedList集合特有的方法,不能使用多态。在开发时,LinkedList集合也可以作为堆栈,队列的结构使用。(了解即可)

示例
package com.itheima.demo01List;

import java.util.LinkedList;
import java.util.List;

public class Demo02LinkedList {
    public static void main(String[] args) {
        show01();
        show02();
        show03();
    }
    /*
    * public E removeFirst() 移除并返回此列表的第一个元素
    * public E removeLast() 移除并返回此列表的最后一个元素
    * public E pop() 从此列表所表示的堆栈处弹出一个元素 此方法相当于removeFirst
    * */
    private static void show03() {
        //创建LinkedList集合对象
        LinkedList<String> linked = new LinkedList<>();
        //使用add方法往集合中添加元素
        linked.add("a");
        linked.add("b");
        linked.add("c");
        System.out.println(linked);

//        String first = linked.removeFirst();
        String first = linked.pop();
        System.out.println("被移除的第一个元素:" + first);
        String last = linked.removeLast();
        System.out.println("被移除的最后一个元素:" + last);
        System.out.println(linked); //[b]
    }

    /*
    * public E getFirst() 返回此列表的第一个元素
    * public E getLast() 返回此列表的最后一个元素
    * */
    private static void show02() {
        //创建LinkedList集合对象
        LinkedList<String> linked = new LinkedList<>();
        //使用add方法往集合中添加元素
        linked.add("a");
        linked.add("b");
        linked.add("c");

        linked.clear(); //清空集合中的元素,在获取集合中元素会抛出NoSuchElementException

        //public boolean isEmpty() 如果链表不含元素,则返回true
        if(!linked.isEmpty()){
            String first = linked.getFirst();
            System.out.println(first); //a
            String last = linked.getLast();
            System.out.println(last);  //c
        }

    }

    /*
    * public void addFirst(E e) 将指定元素插入此列表的开头
    * public void addLast(E e) 将指定元素添加到此列表的结尾
    * public void push(E e) 将元素推入此列表所表示的堆栈
    * */
    private static void show01() {
        //创建LinkedList集合对象
        LinkedList<String> linked = new LinkedList<>();
        //使用add方法往集合中添加元素
        linked.add("a");
        linked.add("b");
        linked.add("c");
        System.out.println(linked); //[a, b, c]

        //public void addFirst(E e) 将指定元素插入此列表的开头
        //public void push(E e) 将元素推入此列表所表示的堆栈
        //这两方法等效
//        linked.addFirst("www");
        linked.push("www");
        System.out.println(linked); //[www, a, b, c]

        //public void addLast(E e) 将指定元素添加到此列表的结尾 此方法等效于add()
        linked.addLast("com");
        System.out.println(linked); //[www, a, b, c, com]

    }
}
运行结果

在这里插入图片描述

ArrayList和LinkedList比较
ArrayList
  • ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类。

  • ArrayList集合底层是数组,初始容量为0,在第一次添加元素的时候,会对集合进行扩容,扩容之后,集合容量为10;之后,当向集合中添加元素达到集合的上限(也就是minCapacity大于elementData.length)时,会对集合再次扩容,扩容为原来的3/2。因为ArrayList集合底层是数组,存储地址是连续的,所以查找比较快,但是插入和删除时由于需要把其它的元素顺序移动,所以比较耗时。

    注意:此实现不是同步的。如果多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。

  • ArrayList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问,实现了Cloneable接口,能被克隆。

LinkedList
  • LinkedList 底层的数据结构是基于双向循环链表的,且头结点中不存放数据。存在一种数据结构——节点,节点实例保存业务数据,前一个节点的位置信息和后一个节点位置信息。实现了双端队列 Deque 接口,链表节点的存储地址是不连续的,每个存储地址通过指针关联,在查找时需要进行指针遍历节点,所以查找比较慢,而在插入和删除时比较快

java.util.Set接口 extends Collection接口

Set接口的特点

java.util.Set 接口和 java.util.List 接口一样,同样继承自 Collection 接口,它与 Collection 接口中的方法基本一致,并没有对 Collection 接口进行功能上的扩充,只是比 Collection 接口更加严格了。与 List 接口不同的是, Set 接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。

1.不允许存储重复的元素。

2.没有索引,没有带索引的方法,也不能使用普通的for循环遍历,但是可以使用迭代器、增强for遍历。

Set的子类

java.util.HashSet集合 implements Set接口

java.util.HashSet 是 Set 接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不一致)。 java.util.HashSet 底层的实现其实是一个 java.util.HashMap 支持。

HashSet的特点

HashSet 底层是一个哈希表结构(查询的速度非常的快),根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。存储元素都是无序的,保证元素唯一性 的方式依赖于: hashCode 与 equals 方法。

示例
package com.itheima.demo02Set;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Demo01Set {
    public static void main(String[] args) {
        Set<Integer> set = new HashSet<>();
        //使用add方法往集合中添加元素
        set.add(1);
        set.add(2);
        set.add(3);
        set.add(1);
        //使用迭代器遍历set集合
        Iterator<Integer> it = set.iterator();
        while(it.hasNext()) {
            int a = it.next();
            System.out.println(a); //1,2,3
        }

        //使用增强for遍历set集合
        System.out.println("=============================");
        for (Integer a : set) {
            System.out.println(a);
        }
    }
}
运行结果

在这里插入图片描述

说明集合中不能存储重复元素

HashSet集合存储数据的结构(哈希表)

什么是哈希表呢?

JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,哈 希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。

简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下图所示。
在这里插入图片描述

为了方便理解结合一个存储流程图来说明
在这里插入图片描述

总而言之,JDK1.8引入红黑树大程度优化了HashMap的性能,那么对于我们来讲保证HashSet集合元素的唯一,其实就是根据对象的hashCode和equals方法来决定的。如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式。

关于为什么重写equals()方法后也要重写hashCode()方法可以看这篇文章

https://blog.csdn.net/qq_46601365/article/details/115873407

HashSet存储自定义类型元素

给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一。

创建自定义Person类

package com.itheima.demo02Set;

import java.util.Objects;

public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

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

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

测试类

package com.itheima.demo02Set;

import java.util.HashSet;

/*
* 要求:
*     同名同年龄的人,视为同一个人,只能存储一次
* */
public class Demo03HashSetSavePerson {
    public static void main(String[] args) {
        HashSet<Person> set = new HashSet<>();
        Person p1 = new Person("小美女",18);
        Person p2 = new Person("小美女",18);
        Person p3 = new Person("小美女",19);
        System.out.println(p1.hashCode()); 
        System.out.println(p2.hashCode()); 

        System.out.println(p1);
        System.out.println(p2);
        System.out.println(p1 == p2); 
        System.out.println(p1.equals(p2)); 

        set.add(p1);
        set.add(p2);
        set.add(p3);
        System.out.println(set);
    }
}

运行结果

在这里插入图片描述

java.util.LinkedHashSet集合 extends HashSet集合 implements Set接口

我们知道HashSet保证元素唯一,可是元素存放进去是没有顺序的,那么我们要保证有序,怎么办呢? 在HashSet下面有一个子类 java.util.LinkedHashSet ,它是链表和哈希表组合的一个数据存储结构。

LinkedHashSet的特点

LinkedHashSet底层是一个哈希表(数组+链表/红黑树)+链表:多了一条链表(记录元素的存储顺序),保证元素有序

示例
package com.itheima.demo02Set;

import java.util.HashSet;
import java.util.LinkedHashSet;

public class Demo04LinkedHashSet {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        boolean b1 = set.add("www");
        boolean b2 = set.add("abc");
        boolean b3 = set.add("abc");
        boolean b4 = set.add("itcast");
        System.out.println(b1);
        System.out.println(b2); 
        System.out.println(b3); //强行add返回false
        System.out.println(b4);
        System.out.println(set); //[abc, www, itcast] 无序,不允许重复

        LinkedHashSet<String> Linked = new LinkedHashSet<>();
        boolean b5 = Linked.add("www");
        boolean b6 = Linked.add("abc");
        boolean b7 = Linked.add("abc");
        boolean b8 = Linked.add("itcast");
        System.out.println(b5);
        System.out.println(b6);
        System.out.println(b7); //强行add返回false
        System.out.println(b8);
        System.out.println(Linked); //[www, abc, itcast] 有序,不允许重复
    }
}
运行结果

在这里插入图片描述

可变参数

介绍

JDK1.5之后,如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化成如下格式

修饰符 返回值类型 方法名(参数类型... 形参名){ }

其实这个书写完全等价与

修饰符 返回值类型 方法名(参数类型[] 形参名){ }

只是后面这种定义,在调用时必须传递数组,而前者可以直接传递数据即可。JDK1.5以后。出现了简化操作。 用在参数上,称之为可变参数。

同样是代表数组,但是在调用这个带有可变参数的方法时,不用创建数组(这就是简单之处),直接将数组中的元素作为实际参数进行传递,其实编译成的class文件,将这些元素先封装到一个数组中,在进行传递。这些动作都在编译.class文件时,自动完成了。

示例

package com.itheima.demo04VarArgs;

public class Demo01VarArgs {
    public static void main(String[] args) {
        //int i = add();
        //int i = add(10);
        //int i = add(10,20);
        int i = add(10,20,30,40,50,60,70,80,90,100);
        System.out.println(i);

        method("abc",5.5,10,1,2,3,4);
    }
    
    /*
    * 可变参数的注意事项
    *    1.一个方法的参数列表,只能有一个可变参数
    *    2.如果方法的参数有多个,那么可变参数必须写在参数列表末尾
    * */
    /*public static void method(int...a,String...b) {

    }*/
    /*public static void method(String b,double c,int d,int...a) {

    }*/
    //可变参数的特殊(终极)写法
    public static void method(Object...obj) {

    }
    
    /*
    * 定义计算(0-n)整数和的方法
    * 已知:计算整数的和,数据类型已经确定int
    * 但是参数的个数不知道要计算几个整数的和,就可以使用可变参数
    * add(); 就会创建一个长度为0的数组,new int[0]
    * add(10); 就会创建一个长度为1的数组,存储传递过来的参数 new int[]{10}
    * add(10,20); 就会创建一个长度为2的数组,存储传递过来的参数 new int[]{10,20}
    * add(10,20,30,40,50,60,70,80,90,100); 就会创建一个长度为10的数组,存储传递过来的参数 new int[]{10,20,30,40,50,60,70,80,90,100}
    * */
    public static int add(int...arr) {
        //System.out.println(arr); //[I@5fd0d5ae 底层是一个数组
        //System.out.println(arr.length); //0,1,2,10
        //定义一个初始化的变量,记录累加求和
        int sum = 0;
        //遍历数组,获取数组中每一个元素
        for (int i : arr) {
            //累加求和
            sum += i;
        }
        //把求和结果返回
        return sum;
    }
    
//    //定义一个方法,计算两个int整数的和
//    public static int add(int a,int b,int c) {
//        return a+b+c;
//    }
//    //定义一个方法,计算两个int整数的和
//    public static int add(int a,int b) {
//        return a+b;
//    }
    
}

运行结果

在这里插入图片描述

java.utils.Collections是集合工具类

常用方法

public static boolean addAll(Collection c, T… elements):往集合中添加一些元素。

public static void shuffle(List<?> list):打乱集合顺序。

public static void sort(List list):将集合中元素按照默认规则排序。

public static void sort(List list,Comparator<? super T>):将集合中元素按照指定规则排序。

示例一

package com.itheima.demo05Collections;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

/*
* public static <T> boolean addAll(Collection<T> c, T... elements):往集合中添加一些元素
* public static void shuffle(List<?> list):打乱顺序:打乱集合顺序
* */
public class Demo01Collections {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        //往集合中添加多个元素
        /*list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");*/

        //public static <T> boolean addAll(Collection<T> c, T... elements)
        //往集合中添加一些元素
        Collections.addAll(list,"a","b","c","d","e");

        System.out.println(list); //[a, b, c, d, e]

        //public static void shuffle(List<?> list)
        //打乱顺序:打乱集合顺序
        Collections.shuffle(list);
        System.out.println(list);
    }
}
运行结果

在这里插入图片描述

示例二

Person类
package com.itheima.demo05Collections;

public class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    //重写排序规则
    @Override
    public int compareTo(Person o) {
        //return 0; //认为元素都是相同的
        //自定义比较的规则,比较两个人的年龄
        //return this.getAge() - o.getAge(); //年龄升序排序
        return o.getAge() - this.getAge(); //年龄降序排序
    }
}
测试类
package com.itheima.demo05Collections;

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

/*
* public static <T> void sort(List<T> list):将集合中元素按照默认规则排序
* 注:
*     sort(List<T> list)使用前提:
*     被排序的集合里面存储的元素,
*     必须实现Comparable接口,重写接口中的方法compareTo方法定义排序规则
* Comparable接口的排序规则:
*     自己(this)-参数:升序
*     参数-自己(this):降序
* */
public class Demo02Sort {
    public static void main(String[] args) {
        ArrayList<Integer> list01 = new ArrayList<>();
        list01.add(1);
        list01.add(3);
        list01.add(2);
        System.out.println(list01); //[1, 3, 2]

        //public static <T> void sort(List<T> list):将集合中元素按照默认规则排序
        Collections.sort(list01); //默认是升序

        System.out.println(list01); //[1, 2, 3]

        ArrayList<String> list02 = new ArrayList<>();
        list02.add("a");
        list02.add("c");
        list02.add("b");
        System.out.println(list02); //[a, c, b]

        Collections.sort(list02);
        System.out.println(list02); //[a, b, c]

        ArrayList<Person> list03 = new ArrayList<>();
        list03.add(new Person("张三",18));
        list03.add(new Person("李四",20));
        list03.add(new Person("王五",15));
        System.out.println(list03); //[Person{name='张三', age=18}, Person{name='李四', age=20}, Person{name='王五', age=15}]

        Collections.sort(list03);
        System.out.println(list03);
    }
}
运行结果

在这里插入图片描述

示例三

Student类
package com.itheima.demo05Collections;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
测试类
package com.itheima.demo05Collections;

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

/*
* public static <T> void sort(List<T> list, Comparator<? super T>):将集合中的元素按照指定规则排序
*    Comparator和Comparable的区别
*     Comparable:自己(this)和别人(参数)比较,
*         自己需要实现Comparable接口,重写比较规则compareTo方法
*     Comparator:相当于找一个第三方的裁判,比较两者
* Comparator的排序规则:
*      integer - t1 升序
*      t1 - integer 降序
* */
public class Demo03Sort {
    public static void main(String[] args) {
        ArrayList<Integer> list01 = new ArrayList<>();
        list01.add(1);
        list01.add(3);
        list01.add(2);
        System.out.println(list01); //[1, 3, 2]

        Collections.sort(list01, new Comparator<Integer>() {
            @Override
            public int compare(Integer integer, Integer t1) {
                //return integer - t1; //升序
                return t1 - integer; //降序
            }
        });
        System.out.println(list01); //[3, 2, 1]

        ArrayList<Student> list02 = new ArrayList<>();
        list02.add(new Student("a迪丽热巴",18));
        list02.add(new Student("古力娜扎",20));
        list02.add(new Student("杨幂",17));
        list02.add(new Student("b杨幂",18));
        System.out.println(list02);

        /*Collections.sort(list02, new Comparator<Student>() {
            @Override
            public int compare(Student student, Student t1) {
                //按照年龄升序排序
                return student.getAge() - t1.getAge();
            }
        });*/

        //扩展,了解即可
        Collections.sort(list02, new Comparator<Student>() {
            @Override
            public int compare(Student student, Student t1) {
                //按照年龄升序排序
                int result = student.getAge() - t1.getAge();
                //如果两个人年龄一样,再使用姓名的第一个字比较
                if(result == 0) {
                    result = student.getName().charAt(0) - t1.getName().charAt(0);
                }
                return result;
            }
        });
        System.out.println(list02);
    }
}
运行结果

在这里插入图片描述

简述Comparable和Comparator两个接口的区别

Comparable:强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo方法被称为它的自然比较方法。只能在类中实现compareTo()一次,不能经常修改类的代码实现自己想要的排序。实现此接口的对象列表(和数组)可以通过Collections.sort(和Arrays.sort)进行自动排序,对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。

Comparator:强行对某个对象进行整体排序。可以将Comparator传递给sort方法(如Collections.sort或Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用Comparator来控制某些数据结构(如有序set或有序映射)的顺序,或者为那些没有自然顺序的对象collection提供排序。

说到排序了,简单的说就是两个对象之间比较大小,那么在JAVA中提供了两种比较实现的方式,一种是比较死板的采用java.lang.Comparable 接口去实现,一种是灵活的当我需要做排序的时候在去选择的java.util.Comparator接口完成。

我们采用的 public static void sort(List list) 这个方法完成的排序,实际上要求了被排序的类型需要实现Comparable接口完成比较的功能,在String类型上如下:

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {}

String类实现了这个接口,并完成了比较规则的定义,但是这样就把这种规则写死了,那比如我想要字符串按照第 一个字符降序排列,那么这样就要修改String的源代码,这是不可能的了,那么这个时候我们可以使用 public static void sort(List list,Comparator<? super T> ) 方法灵活的完成,这个里面就涉及到了Comparator这个接口,位于位于java.util包下,排序是comparator能实现的功能之一,该接口代表一个比较器,比较器具有可比性!顾名思义就是做排序的,通俗地讲需要比较两个对象谁排在前谁排在后,那么比较的方法就是:

public int compare(String o1, String o2) :比较其两个参数的顺序。 

两个对象比较的结果有三种:大于,等于,小于。

如果要按照升序排序, 则o1 小于o2,返回(负数),相等返回0,01大于02返回(正数) 如果要按照 降序排序 则o1 小于o2,返回(正数),相等返回0,01大于02返回(负数)。

java.util.Map<k,v>接口

Collection 中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。

Map 中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。

Collection 中的集合称为单列集合, Map 中的集合称为双列集合。

需要注意的是, Map 中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。

Map接口的特点

1.Map集合是一个双列集合,一个元素包含两个值(一个key,一个value)
2.Map集合中的元素,key和value的数据类型可以相同,也可以不同
3.Map集合中的元素,key是不允许重复的,value是可以重复的
4.Map集合中的元素,key和value是一一对应的

Map接口中的常用方法

public V put(K key, V value) : 把指定的键与指定的值添加到Map集合中。

public V remove(Object key) : 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。

public V get(Object key) : 根据指定的键,在Map集合中获取对应的值。

public boolean containsKey(Object key) : 判断集合中是否包含指定的键。

public Set keySet() : 获取Map集合中所有的键,存储到Set集合中。

public Set<Map.Entry<K,V>> entrySet() : 获取到Map集合中所有的键值对对象的集合(Set集合)。

Map的子类

java.util.HashMap<k,v>集合 implements Map<k,v>接口
HashMap集合的特点

1.HashMap集合底层是一个哈希表:查询速度特别快
JDK1.8之前:数组+单向链表
JDK1.8之后:数组+单向链表/红黑树(链表长度超过8):提高查询的速度
2.HashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致

示例一
package com.itheima.demo06Map;

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

public class Demo01Map {
    public static void main(String[] args) {
        show01();
        show02();
        show03();
        show04();
    }
    /*
    * boolean containsKey(Object key) 判断集合中是否包含指定的键
    * 包含返回true,不包含返回false
    * */
    private static void show04() {
        //创建Map集合对象
        Map<String,Integer> map = new HashMap<>();
        map.put("赵丽颖",168);
        map.put("杨颖",165);
        map.put("林志玲",178);

        boolean b1 = map.containsKey("赵丽颖");
        System.out.println("b1:" + b1); //b1:true

        boolean b2 = map.containsKey("赵颖");
        System.out.println("b2:" + b2); //b2:false
    }

    /*
    * public V get(Object key) 根据指定的键,在Map集合中获取对应的值
    * 返回值:V
    *          key存在,v返回对应的value值
    *          key不存在,v放回null
    * */
    private static void show03() {
        //创建Map集合对象
        Map<String,Integer> map = new HashMap<>();
        map.put("赵丽颖",168);
        map.put("杨颖",165);
        map.put("林志玲",178);

        Integer v1 = map.get("杨颖");
        System.out.println("v1:" + v1); //v1:165

        Integer v2 = map.get("迪丽热巴");
        System.out.println("v2:" + v2); //v2:null
    }

    /*
    * public V remove(Object key)
    *      把指定的键所对应的键值对元素在Map集合中删除,返回被删除元素的值
    *      返回值:V
    *          key存在,v返回被删除的值
    *          key不存在,v放回null
    * */
    private static void show02() {
        //创建Map集合对象
        Map<String,Integer> map = new HashMap<>();
        map.put("赵丽颖",168);
        map.put("杨颖",165);
        map.put("林志玲",178);
        System.out.println(map); //{林志玲=178, 赵丽颖=168, 杨颖=165}

        Integer v1 = map.remove("林志玲");
        System.out.println("v1:" + v1); //v1:178

        System.out.println(map); //{赵丽颖=168, 杨颖=165}

        //int v2 = map.remove("林志颖"); //自动拆箱  会出现NullPointerException 空指针异常 因为返回null
        Integer v2 = map.remove("林志颖");
        System.out.println("v2:" + v2); //v2:null

        System.out.println(map); //{赵丽颖=168, 杨颖=165}
    }

    /*
    * public V put(K key, V value) 把指定的键和指定的值添加到Map集合中
    *      返回值:v
    *          存储键值对的时候,key不重复,返回值V是null
    *          存储键值对的时候,key重复,会使用新的value替换map中重复的value,返回被替换的value值
    * */
    private static void show01() {
        //创建Map集合对象,多态
        Map<String,String> map = new HashMap<>();

        String v1 = map.put("李晨", "范冰冰1");
        System.out.println("v1:" + v1); //v1:null

        String v2 = map.put("李晨","范冰冰2");
        System.out.println("v2:" + v2); //v2:范冰冰1

        System.out.println(map); //{李晨=范冰冰2}

        map.put("冷锋","龙小云");
        map.put("杨过","小龙女");
        map.put("尹志平","小龙女");
        System.out.println(map); //{杨过=小龙女, 尹志平=小龙女, 李晨=范冰冰2, 冷锋=龙小云}
    }
}
运行结果

在这里插入图片描述

示例二(通过键找值的方式遍历)
package com.itheima.demo06Map;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/*
* Map集合的第一种方式:通过键找值的方式
* Map集合中的方法:
*     Set<K> keySet() 返回此映射中包含的键的set视图
* 实现步骤:
*       1.使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中
*       2.遍历Set集合,获取Map集合中的每一个key
*       3.通过Map集合中的方法get(key),通过key找到value
* */
public class Demo02KeySet {
    public static void main(String[] args) {
        //创建Map集合对象
        Map<String,Integer> map = new HashMap<>();
        map.put("赵丽颖",168);
        map.put("杨颖",165);
        map.put("林志玲",178);

        //1.使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中
        Set<String> set = map.keySet();

        //2.遍历Set集合,获取Map集合中的每一个key
        Iterator<String> it = set.iterator();
        while(it.hasNext()) {
            String key = it.next();
            //3.通过Map集合中的方法get(key),通过key找到value
            Integer value = map.get(key);
            System.out.println(key + "=" + value);
        }
        System.out.println("==========================");

        //使用增强for遍历Set集合
        for (String key : set) {
            //3.通过Map集合中的方法get(key),通过key找到value
            Integer value = map.get(key);
            System.out.println(key + "=" + value);
        }
        System.out.println("==========================");

        //使用增强for遍历Set集合
        for (String key : map.keySet()) {
            //3.通过Map集合中的方法get(key),通过key找到value
            Integer value = map.get(key);
            System.out.println(key + "=" + value);
        }

    }
}
运行结果

在这里插入图片描述

示例三(使用Entry对象遍历)
package com.itheima.demo06Map;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/*
* Map集合遍历的第二种方式:使用Entry对象遍历
* Map集合中的方法:
*      Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的Set视图
* 实现步骤:
*      1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
*      2.遍历Set集合,获取每一个Entry对象
*      3.使用Entry对象中的方法getKey()和getValue()获取键与值
* */
public class Demo03EntrySet {
    public static void main(String[] args) {
        //创建Map集合对象
        Map<String,Integer> map = new HashMap<>();
        map.put("赵丽颖",168);
        map.put("杨颖",165);
        map.put("林志玲",178);

        //1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
        Set<Map.Entry<String,Integer>> set = map.entrySet();

        //2.遍历Set集合,获取每一个Entry对象
        Iterator<Map.Entry<String,Integer>> it = set.iterator();
        while(it.hasNext()) {
            Map.Entry<String, Integer> entry = it.next();
            //3.使用Entry对象中的方法getKey()和getValue()获取键与值
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key + "=" + value);
        }
        System.out.println("============================");

        //2.使用增强for遍历
        for (Map.Entry<String, Integer> entry : set) {
            //3.使用Entry对象中的方法getKey()和getValue()获取键与值
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key + "=" + value);
        }

    }
}
运行结果

在这里插入图片描述

示例四(存储自定义类型键值)

要求:姓名和年龄一样的为同一人

package com.itheima.demo07Map;

import java.util.Objects;

public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
package com.itheima.demo07Map;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/*
* HashMap存储自定义类型键值
* Map集合保证是唯一的:
*     作为key的元素,必须重写hashCode方法和equals方法,以保证key唯一
* */
public class Demo01HashMapSavePerson {
    public static void main(String[] args) {
        show01();
        show02();
    }
    /*
    * HashMap存储自定义类型键值
    * key:Person类型
    *      Person类就必须重写hashCode方法和equals方法,以保证key唯一
    * value:String类型
    *      可以重复
    * */
    private static void show02() {
        //创建HashMap集合
        HashMap<Person,String> map = new HashMap<>();
        //往集合中添加元素
        map.put(new Person("女王",18),"英国");
        map.put(new Person("秦始皇",18),"秦国");
        map.put(new Person("普京",30),"俄罗斯");
        map.put(new Person("女王",18),"毛里求斯");
        //使用entrySet和增强for遍历集合
        Set<Map.Entry<Person,String>> set = map.entrySet();
        for (Map.Entry<Person, String> entry : set) {
            Person key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "-->" + value);
        }
    }

    /*
    * HashMap存储自定义类型键值
    * key:String类型
    *      String类重写hashCode方法和equals方法,以保证key唯一
    * value:Person类型
    *      value可以重复(同名同年龄的人视为同一个人)
    * */
    private static void show01() {
        //创建HashMap集合
        HashMap<String,Person> map = new HashMap<>();
        //往集合中添加元素
        map.put("北京",new Person("张三",18));
        map.put("上海",new Person("李四",19));
        map.put("广州",new Person("王五",20));
        map.put("北京",new Person("赵六",18));
        //使用keySet()方法加增强for循环遍历集合
        Set<String> set = map.keySet();
        for (String key : set) {
            Person value = map.get(key);
            System.out.println(key + "-->" + value);
        }
    }
}
运行结果

在这里插入图片描述

java.util.LinkedHashMap<k,v>集合 extends HashMap<k,v>集合
LinkedHashMap集合的特点

1.LinkedHashMap集合底层是哈希表+链表(保证迭代顺序)
2.LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序一致

示例一
package com.itheima.demo08Map;

import java.util.HashMap;
import java.util.LinkedHashMap;

public class Demo01LinkedHashMap {
    public static void main(String[] args) {
        HashMap<String,String> map = new HashMap<>();
        map.put("a","a");
        map.put("c","c");
        map.put("b","b");
        map.put("a","d");
        System.out.println(map);// key不允许重复,无序 {a=d, b=b, c=c}

        LinkedHashMap<String,String> linked = new LinkedHashMap<>();
        linked.put("a","a");
        linked.put("c","c");
        linked.put("b","b");
        linked.put("a","d");
        System.out.println(linked);// key不允许重复,有序 {a=d, c=c, b=b}
    }
}
运行结果

在这里插入图片描述

java.util.Hashtable<K,V>集合 implements Map<K,V>接口
Hashtable和HashMap的比较

Hashtable:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢。
HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快。

HashMap集合(之前学的所有的集合):可以存储null值,null键。
Hashtable集合:不能存储null值,null键。

注意:Hashtable和Vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了。Hashtable的子类Properties依然活跃在历史舞台。Properties集合是一个唯一和IO流相结合的集合。

示例
package com.itheima.dem![微信图片_20210713134344](E:\tupian\微信图片_20210713134344.png)o08Map;

import java.util.HashMap;
import java.util.Hashtable;

public class Demo02Hashtable {
    public static void main(String[] args) {
        HashMap<String,String> map = new HashMap<>();
        map.put(null,"a");
        map.put("b",null);
        map.put(null,null);
        System.out.println(map);//{null=null, b=null}

        Hashtable<String,String> table = new Hashtable<>();
        table.put(null,"a");//NullPointerException
        table.put("b",null);//NullPointerException
        table.put(null,null);//NullPointerException
        System.out.println(table);
    }
}
运行结果

在这里插入图片描述

Map集合的练习

练习一

计算一个字符串中每个字符出现次数

分析

​ 1.使用Scanner获取用户输入的字符串
​ 2.创建Map集合,key是字符串中的字符,value是字符的个数
​ 3.遍历字符串,获取每一个字符
​ 4.使用获取到的字符,去Map集合判断key是否存在
​ key存在:
​ 通过字符(key),获取value(字符个数)
​ value++
​ put(key,value)把新的value存储到Map集合中
​ key不存在:
​ put(key,1)
​ 5.遍历Map集合,输出结果

package com.itheima.demo08Map;

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

public class Demo03MapTest {
    public static void main(String[] args) {
        //1.使用Scanner获取用户输入的字符串
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String str = sc.next();
        //2.创建Map集合,key是字符串中的字符,value是字符的个数
        HashMap<Character,Integer> map = new HashMap<>();
        //3.遍历字符串,获取每一个字符
        for(char c :str.toCharArray()){
            //4.使用获取到的字符,去Map集合判断key是否存在
            if(map.containsKey(c)){
                //key存在
                Integer value = map.get(c);
                value++;
                map.put(c,value);
            }else{
                //key不存在
                map.put(c,1);
            }
        }
        //5.遍历Map集合,输出结果
        for(Character key :map.keySet()){
            Integer value = map.get(key);
            System.out.println(key+"="+value);
        }
    }
}
运行结果

在这里插入图片描述

练习二

上面集合的综合案例斗地主综合案例的有序版本

package com.itheima.demo11Test;

import java.util.*;

/*
    斗地主综合案例:有序版本
    1.准备牌
    2.洗牌
    3.发牌
    4.排序
    5.看牌
 */
public class DouDiZhu {
    public static void main(String[] args) {
        //1.准备牌
        //创建一个Map集合,存储牌的索引和组装好的牌
        HashMap<Integer,String> poker = new HashMap<>();
        //创建一个List集合,存储牌的索引
        ArrayList<Integer> pokerIndex = new ArrayList<>();
        //定义两个集合,存储花色和牌的序号
        List<String> colors = Arrays.asList("♠", "♥", "♣", "♦");
        List<String> numbers = Arrays.asList("2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3");
        //把大王和小王存储到集合中
        //定义一个牌的索引
        int index = 0;
        poker.put(index,"大王");
        pokerIndex.add(index);
        index++;
        poker.put(index,"小王");
        pokerIndex.add(index);
        index++;
        //循环嵌套遍历两个集合,组装52张牌,存储到集合中
        for (String number : numbers) {
            for (String color : colors) {
                poker.put(index,color+number);
                pokerIndex.add(index);
                index++;
            }
        }
        System.out.println(poker);
        //System.out.println(pokerIndex);

        /*
            2.洗牌
            使用Collections中的方法shuffle(List)
         */
        Collections.shuffle(pokerIndex);
        //System.out.println(pokerIndex);

        /*
            3.发牌
         */
        //定义4个集合,存储玩家牌的索引,和底牌的索引
        ArrayList<Integer> player01 = new ArrayList<>();
        ArrayList<Integer> player02 = new ArrayList<>();
        ArrayList<Integer> player03 = new ArrayList<>();
        ArrayList<Integer> diPai = new ArrayList<>();
        //遍历存储牌索引的List集合,获取每一个牌的索引
        for (int i = 0; i <pokerIndex.size() ; i++) {
            Integer in = pokerIndex.get(i);
            //先判断底牌
            if(i>=51){
                //给底牌发牌
                diPai.add(in);
            }else if(i%3==0){
                //给玩家1发牌
                player01.add(in);
            }else if(i%3==1){
                //给玩家2发牌
                player02.add(in);
            }else if(i%3==2){
                //给玩家3发牌
                player03.add(in);
            }
        }

        /*
            4.排序
            使用Collections中的方法sort(List)
            默认是升序排序
         */
        Collections.sort(player01);
        Collections.sort(player02);
        Collections.sort(player03);
        Collections.sort(diPai);

        /*
            5.看牌
            调用看牌的方法
         */
        lookPoker("刘德华",poker,player01);
        lookPoker("周润发",poker,player02);
        lookPoker("周星驰",poker,player03);
        lookPoker("底牌",poker,diPai);
    }

    /*
        定义一个看牌的方法,提高代码的复用性
        参数:
            String name:玩家名称
            HashMap<Integer,String> poker:存储牌的poker集合
            ArrayList<Integer> list:存储玩家和底牌的List集合
        查表法:
             遍历玩家或者底牌集合,获取牌的索引
             使用牌的索引,去Map集合中,找到对应的牌
     */
    public static void lookPoker(String name, HashMap<Integer,String> poker, ArrayList<Integer> list){
        //输出玩家名称,不换行
        System.out.print(name+":");
        //遍历玩家或者底牌集合,获取牌的索引
        for (Integer key : list) {
            //使用牌的索引,去Map集合中,找到对应的牌
            String value = poker.get(key);
            System.out.print(value+" ");
        }
        System.out.println();//打印完每一个玩家的牌,换行
    }

}
运行结果

在这里插入图片描述

后续会持续更新,欢迎一起交流学习!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值