首页 > 其他分享 >集合

集合

时间:2022-11-29 23:13:51浏览次数:27  
标签:println add 集合 new public out

集合

集合概述

数组其实就是一个集合。集合实际上就是一个容器。可以来容纳其它类型的数据。

集合为什么说在开发中使用较多?
集合是一个容器,是一个载体,可以一次容纳多个对象。
在实际开发中,假设连接数据库,数据库当中有10条记录,
那么假设把这10条记录查询出来,在java程序中会将10条
数据封装成10个java对象,然后将10个java对象放到某一个
集合当中,将集合传到前端,然后遍历集合,将一个数据一个数据展现出来。

集合中存储什么?

集合不能直接存储基本数据类型,另外集合也不能直接存储java对象,
集合当中存储的都是java对象的内存地址。(或者说集合中存储的是引用。)
list.add(100);

​ //自动装箱Integer
注意:集合在java中本身是一个容器,是一个对象。集合中任何时候存储的都是“引用”。

image-20221120191524231

不同的集合对应不同的数据结构

在java中每一个不同的集合,底层会对应不同的数据结构

往不同的集合中存储元素,等于将数据放到了不同的数据结构当中。什么是数据结构?

数据存储的结构就是数据结构。不同的数据结构,数据存储方式不同。例如:
数组、二叉树、链表、哈希表...
以上这些都是常见的数据结构。

​ 你往集合c1中放数据,可能是放到数组上了。
​ 你往集合c2中放数据,可能是放到二叉树上了。
​ .....
​ 你使用不同的集合等同于使用了不同的数据结构。

​ 你在java集合这一章节,你需要掌握的不是精通数据结构。java中已经将数据结构实现了,已经写好了这些常用的集合类,你只需要掌握怎么用?在什么情况下选择哪一种合适的集合去使用即可。

​ new ArrayList(); 创建一个集合,底层是数组。
​ new LinkedList(); 创建一个集合对象,底层是链表。
​ new TreeSet(); 创建一个集合对象,底层是二叉树。

集合在java JDK中哪个包下?
java.util.*;
所有的集合类和集合接口都在java.util包下。

集合的分类

  1. ​ 一类是单个方式存储元素:
    ​ 单个方式存储元素,这一类集合中超级父接口:java.util.Collection;
  2. ​ 一类是以键值对儿的方式存储元素
    ​ 以键值对的方式存储元素,这一类集合中超级父接口:java.util.Map;

集合的继承结构图

image-20221121110716859

image-20221121110839175

Collection

Collection接口中的常用方法

关于java.util.Collection接口中常用的方法。

Collection存放的元素

​ 没有使用“泛型”之前,Collection中可以存储Object的所有子类型。
​ 使用了“泛型”之后,Collection中只能存储某个具体的类型。
​ 集合后期我们会学习“泛型”语法。目前先不用管。Collection中什么都能存,只要是Object的子类型就行。(集合中不能直接存储基本数据类型,也不能存java对象,只是存储java对象的内存地址。)

Collection中的常用方法

​ boolean add(Object e) 向集合中添加元素
​ int size() 获取集合中元素的个数
​ void clear() 清空集合
​ boolean contains(Object o) 判断当前集合中是否包含元素o,包含返回true,不包含返回false
​ boolean remove(Object o) 删除集合中的某个元素。
​ boolean isEmpty() 判断该集合中元素的个数是否为0
​ Object[] toArray() 调用这个方法可以把集合转换成数组。【作为了解,使用不多。】

public class CollectionTest01 {
    public static void main(String[] args) {
        // 创建一个集合对象
        //Collection c = new Collection(); // 接口是抽象的,无法实例化。
        // 多态
        Collection c = new ArrayList();
        // 测试Collection接口中的常用方法
        c.add(1200); // 自动装箱(java5的新特性。),实际上是放进去了一个对象的内存地址。Integer x = new Integer(1200);
        c.add(3.14); // 自动装箱
        c.add(new Object());
        c.add(new Student());
        c.add(true); // 自动装箱

        // 获取集合中元素的个数
        System.out.println("集合中元素个数是:" + c.size()); // 5

        // 清空集合
        c.clear();
        System.out.println("集合中元素个数是:" + c.size()); // 0

        // 再向集合中添加元素
        c.add("hello"); // "hello"对象的内存地址放到了集合当中。
        c.add("world");
        c.add("浩克");
        c.add("绿巨人");
        c.add(1);

        // 判断集合中是否包含"绿巨人"
        boolean flag = c.contains("绿巨人");
        System.out.println(flag); // true
        boolean flag2 = c.contains("绿巨人2");
        System.out.println(flag2); // false
        System.out.println(c.contains(1)); // true

        System.out.println("集合中元素个数是:" + c.size()); // 5

        // 删除集合中某个元素
        c.remove(1);
        System.out.println("集合中元素个数是:" + c.size()); // 4

        // 判断集合是否为空(集合中是否存在元素)
        System.out.println(c.isEmpty()); // false
        // 清空
        c.clear();
        System.out.println(c.isEmpty()); // true(true表示集合中没有元素了!)

        c.add("abc");
        c.add("def");
        c.add(100);
        c.add("helloworld!");
        c.add(new Student());

        // 转换成数组(了解,使用不多。)
        Object[] objs = c.toArray();
        for(int i = 0; i < objs.length; i++){
            // 遍历数组
            Object o = objs[i];
            System.out.println(o);
        }
    }
}

class Student{

}

Collection迭代


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

/**
 * 关于集合遍历/迭代专题。(重点:五颗星*****)
 */
public class CollectionTest02 {
    public static void main(String[] args) {
        // 注意:以下讲解的遍历方式/迭代方式,是所有Collection通用的一种方式。
        // 在Map集合中不能用。在所有的Collection以及子类中使用。
        // 创建集合对象
        Collection c = new ArrayList(); // 后面的集合无所谓,主要是看前面的Collection接口,怎么遍历/迭代。
        // 添加元素
        c.add("abc");
        c.add("def");
        c.add(100);
        c.add(new Object());
        // 对集合Collection进行遍历/迭代
        // 第一步:获取集合对象的迭代器对象Iterator
        Iterator it = c.iterator();
        // 第二步:通过以上获取的迭代器对象开始迭代/遍历集合。
        /*
            以下两个方法是迭代器对象Iterator中的方法:
                boolean hasNext()如果仍有元素可以迭代,则返回 true。
                Object next() 返回迭代的下一个元素。
         */
        while(it.hasNext()){
            Object obj = it.next();
            System.out.println(obj);
        }

        // 一直取,不判断,会出现异常:java.util.NoSuchElementException
        /*while(true){
            Object obj = it.next();
            System.out.println(obj);
        }*/

        /*boolean hasNext = it.hasNext();
        System.out.println(hasNext);
        if(hasNext) {
            // 不管你当初存进去什么,取出来统一都是Object。
            Object obj = it.next();
            System.out.println(obj);
        }

        hasNext = it.hasNext();
        System.out.println(hasNext);
        if(hasNext) {
            Object obj = it.next();
            System.out.println(obj);
        }

        hasNext = it.hasNext();
        System.out.println(hasNext);
        if(hasNext) {
            Object obj = it.next();
            System.out.println(obj);
        }

        hasNext = it.hasNext();
        System.out.println(hasNext);
        if(hasNext) {
            Object obj = it.next();
            System.out.println(obj);
        }

        hasNext = it.hasNext();
        System.out.println(hasNext);
        if(hasNext) {
            Object obj = it.next();
            System.out.println(obj);
        }*/
    }
}

image-20221121152452281

迭代器是通用的


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

/*
关于集合的迭代/遍历
 */
public class CollectionTest03 {
    public static void main(String[] args) {
        // 创建集合对象
        Collection c1  = new ArrayList(); // ArrayList集合:有序可重复
        // 添加元素
        c1.add(1);
        c1.add(2);
        c1.add(3);
        c1.add(4);
        c1.add(1);

        // 迭代集合
        Iterator it = c1.iterator();
        while(it.hasNext()){
            // 存进去是什么类型,取出来还是什么类型。
            Object obj = it.next();
            /*if(obj instanceof Integer){
                System.out.println("Integer类型");
            }*/
            // 只不过在输出的时候会转换成字符串。因为这里println会调用toString()方法。
            System.out.println(obj);
        }

        // HashSet集合:无序不可重复
        Collection c2 = new HashSet();
        // 无序:存进去和取出的顺序不一定相同。
        // 不可重复:存储100,不能再存储100.
        c2.add(100);
        c2.add(200);
        c2.add(300);
        c2.add(90);
        c2.add(400);
        c2.add(50);
        c2.add(60);
        c2.add(100);
        Iterator it2 = c2.iterator();
        while(it2.hasNext()){
            System.out.println(it2.next());
        }
    }
}

003-迭代原理

contains方法

深入Collection集合的contains方法:
boolean contains(Object o)
判断集合中是否包含某个对象o
如果包含返回true, 如果不包含返回false。

contains方法是用来判断集合中是否包含某个元素的方法,
那么它在底层是怎么判断集合中是否包含某个元素的呢?
调用了equals方法进行比对。
equals方法返回true,就表示包含这个元素。

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

public class CollectionTest04 {
    public static void main(String[] args) {
        // 创建集合对象
        Collection c = new ArrayList();

        // 向集合中存储元素
        String s1 = new String("abc"); // s1 = 0x1111
        c.add(s1); // 放进去了一个"abc"

        String s2 = new String("def"); // s2 = 0x2222
        c.add(s2);

        // 集合中元素的个数
        System.out.println("元素的个数是:" + c.size()); // 2

        // 新建的对象String
        String x = new String("abc"); // x = 0x5555
        // c集合中是否包含x?结果猜测一下是true还是false?
        System.out.println(c.contains(x)); //判断集合中是否存在"abc" true
    }
}

002-Collection的contains方法

contains源码解析(remove方法)


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

/*
测试contains方法
测试remove方法。
结论:存放在一个集合中的类型,一定要重写equals方法。
 */
public class CollectionTest05 {
    public static void main(String[] args) {
        // 创建集合对象
        Collection c = new ArrayList();
        // 创建用户对象
        User u1 = new User("jack");
        // 加入集合
        c.add(u1);

        // 判断集合中是否包含u2
        User u2 = new User("jack");

        // 没有重写equals之前:这个结果是false
        //System.out.println(c.contains(u2)); // false
        // 重写equals方法之后,比较的时候会比较name。
        System.out.println(c.contains(u2)); // true

        c.remove(u2);
        System.out.println(c.size()); // 0

        /*Integer x = new Integer(10000);
        c.add(x);

        Integer y = new Integer(10000);
        System.out.println(c.contains(y)); // true*/

        // 创建集合对象
        Collection cc = new ArrayList();
        // 创建字符串对象
        String s1 = new String("hello");
        // 加进去。
        cc.add(s1);

        // 创建了一个新的字符串对象
        String s2 = new String("hello");
        // 删除s2
        cc.remove(s2); // s1.equals(s2) java认为s1和s2是一样的。删除s2就是删除s1。
        // 集合中元素个数是?
        System.out.println(cc.size()); // 0
    }
}

class User{
    private String name;
    public User(){}
    public User(String name){
        this.name = name;
    }

    // 重写equals方法
    // 将来调用equals方法的时候,一定是调用这个重写的equals方法。
    // 这个equals方法的比较原理是:只要姓名一样就表示同一个用户。
    public boolean equals(Object o) {
        if(o == null || !(o instanceof User)) return false;
        if(o == this) return true;
        User u = (User)o;
        // 如果名字一样表示同一个人。(不再比较对象的内存地址了。比较内容。)
        return u.name.equals(this.name);
    }

}

第一个重点:把集合继承结构图背会。

第二个重点:把Collection接口中常用方法测试几遍。

第三个重点:把迭代器弄明白。

第四个重点:Collection接口中的remove方法和contains方法底层都会调用equals,
这个弄明白。

List接口

常用方法

测试List接口中常用方法
1、List集合存储元素特点:有序可重复
有序:List集合中的元素有下标。
从0开始,以1递增。
可重复:存储一个1,还可以再存储1.
2、List既然是Collection接口的子接口,那么肯定List接口有自己“特色”的方法:
以下只列出List接口特有的常用的方法:
void add(int index, Object element)
Object set(int index, Object element)
Object get(int index)
int indexOf(Object o)
int lastIndexOf(Object o)
Object remove(int index)

​ 以上几个方法不需要死记硬背,可以自己编写代码测试一下,理解一下,
​ 以后开发的时候,还是要翻阅帮助文档。


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

public class ListTest01 {
    public static void main(String[] args) {
        // 创建List类型的集合。
        //List myList = new LinkedList();
        //List myList = new Vector();
        List myList = new ArrayList();

        // 添加元素
        myList.add("A"); // 默认都是向集合末尾添加元素。
        myList.add("B");
        myList.add("C");
        myList.add("C");
        myList.add("D");

        //在列表的指定位置插入指定元素(第一个参数是下标)
        // 这个方法使用不多,因为对于ArrayList集合来说效率比较低。
        myList.add(1, "KING");

        // 迭代
        Iterator it = myList.iterator();
        while(it.hasNext()){
            Object elt = it.next();
            System.out.println(elt);
        }

        // 根据下标获取元素
        Object firstObj = myList.get(0);
        System.out.println(firstObj);

        // 因为有下标,所以List集合有自己比较特殊的遍历方式
        // 通过下标遍历。【List集合特有的方式,Set没有。】
        for(int i = 0; i < myList.size(); i++){
            Object obj = myList.get(i);
            System.out.println(obj);
        }

        // 获取指定对象第一次出现处的索引。
        System.out.println(myList.indexOf("C")); // 3

        // 获取指定对象最后一次出现处的索引。
        System.out.println(myList.lastIndexOf("C")); // 4

        // 删除指定下标位置的元素
        // 删除下标为0的元素
        myList.remove(0);
        System.out.println(myList.size()); // 5

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

        // 修改指定位置的元素
        myList.set(2, "Soft");

        // 遍历集合
        for(int i = 0; i < myList.size(); i++){
            Object obj = myList.get(i);
            System.out.println(obj);
        }
    }
}

/*
计算机英语:
    增删改查这几个单词要知道:
        增:add、save、new
        删:delete、drop、remove
        改:update、set、modify
        查:find、get、query、select
 */

ArrayList集合初始化容量以及扩容

ArrayList集合:
1、默认初始化容量10(底层先创建了一个长度为0的数组,当添加第一个元素的时候,初始化容量10。)
2、集合底层是一个Object[]数组。
3、构造方法:
new ArrayList();
new ArrayList(20);
4、ArrayList集合的扩容:
增长到原容量的1.5倍。
ArrayList集合底层是数组,怎么优化?
尽可能少的扩容。因为数组扩容效率比较低,建议在使用ArrayList集合
的时候预估计元素的个数,给定一个初始化容量。
5、数组优点:
检索效率比较高。(每个元素占用空间大小相同,内存地址是连续的,知道首元素内存地址,
然后知道下标,通过数学表达式计算出元素的内存地址,所以检索效率最高。)
6、数组缺点:
随机增删元素效率比较低。
另外数组无法存储大数据量。(很难找到一块非常巨大的连续的内存空间。)
7、向数组末尾添加元素,效率很高,不受影响。
8、面试官经常问的一个问题?
这么多的集合中,你用哪个集合最多?
答:ArrayList集合。
因为往数组末尾添加元素,效率不受影响。
另外,我们检索/查找某个元素的操作比较多。

​ 9、ArrayList集合是非线程安全的。(不是线程安全的集合。)


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

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

        // 默认初始化容量是10
        // 数组的长度是10
        List list1 = new ArrayList();
        // 集合的size()方法是获取当前集合中元素的个数。不是获取集合的容量。
        System.out.println(list1.size()); // 0

        // 指定初始化容量
        // 数组的长度是20
        List list2 = new ArrayList(20);
        // 集合的size()方法是获取当前集合中元素的个数。不是获取集合的容量。
        System.out.println(list2.size()); // 0

        list1.add(1);
        list1.add(2);
        list1.add(3);
        list1.add(4);
        list1.add(5);
        list1.add(6);
        list1.add(7);
        list1.add(8);
        list1.add(9);
        list1.add(10);

        System.out.println(list1.size());

        // 再加一个元素
        list1.add(11);
        System.out.println(list1.size()); // 11个元素。
        /*
        int newCapacity = ArraysSupport.newLength(oldCapacity,minCapacity - oldCapacity,oldCapacity >> 1);
         */
        // 100 二进制转换成10进制: 00000100右移一位 00000010 (2)  【4 / 2】
        // 原先是4、现在增长:2,增长之后是6,增长之后的容量是之前容量的:1.5倍。
        // 6是4的1.5倍
    }
}

计算机位运算分为:逻辑位运算 和 位移运算,不懂的应该去了解下就明白了

新的长度newlength,是在第一参数基础上,增加第二参数,第二参数为2,所以新容量就为6

ArrayList的构造方法


import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;

/*
集合ArrayList的构造方法
 */
public class ArrayListTest02 {
    public static void main(String[] args) {

        // 默认初始化容量10
        List myList1 = new ArrayList();

        // 指定初始化容量100
        List myList2 = new ArrayList(100);

        // 创建一个HashSet集合
        Collection c = new HashSet();
        // 添加元素到Set集合
        c.add(100);
        c.add(200);
        c.add(900);
        c.add(50);

        // 通过这个构造方法就可以将HashSet集合转换成List集合。
        List myList3 = new ArrayList(c);
        for(int i = 0; i < myList3.size(); i++){
            System.out.println(myList3.get(i));
        }
    }
}

单向链表数据结构

/*
链表类。(单向链表)
 */
public class Link<E> {
    public static void main(String[] args) {
        Link<String> link = new Link<>();
        link.add("abc");

        // 类型不匹配。
        //link.add(123);
    }

    // 头节点
    Node header;

    int size = 0;

    public int size(){
        return size;
    }

    // 向链表中添加元素的方法(向末尾添加)
    public void add(E data){
    //public void add(Object data){
        // 创建一个新的节点对象
        // 让之前单链表的末尾节点next指向新节点对象。
        // 有可能这个元素是第一个,也可能是第二个,也可能是第三个。
        if(header == null){
            // 说明还没有节点。
            // new一个新的节点对象,作为头节点对象。
            // 这个时候的头节点既是一个头节点,又是一个末尾节点。
            header = new Node(data, null);
        }else {
            // 说明头不是空!
            // 头节点已经存在了!
            // 找出当前末尾节点,让当前末尾节点的next是新节点。
            Node currentLastNode = findLast(header);
            currentLastNode.next = new Node(data, null);
        }
        size++;
    }

    /**
     * 专门查找末尾节点的方法。
     */
    private Node findLast(Node node) {
        if(node.next == null) {
            // 如果一个节点的next是null
            // 说明这个节点就是末尾节点。
            return node;
        }
        // 程序能够到这里说明:node不是末尾节点。
        return findLast(node.next); // 递归算法!
    }

    // 删除链表中某个数据的方法
    public void remove(Object obj){

    }

    // 修改链表中某个数据的方法
    public void modify(Object newObj){

    }

    // 查找链表中某个元素的方法。
    public int find(Object obj){
        return 1;
    }
}


/*
单链表中的节点。
节点是单向链表中基本的单元。
每一个节点Node都有两个属性:
    一个属性:是存储的数据。
    另一个属性:是下一个节点的内存地址。
 */
public class Node {

    // 存储的数据
    Object data;

    // 下一个节点的内存地址
    Node next;

    public Node(){

    }

    public Node(Object data, Node next){
        this.data = data;
        this.next = next;
    }
}

public class Test {
    public static void main(String[] args) {
        // 创建了一个集合对象
        Link link = new Link();

        // 往集合中添加元素
        link.add("abc");
        link.add("def");
        link.add("xyz");

        // 获取元素个数
        System.out.println(link.size());

    }
}

005-链表(单向链表)

链表的优点和缺点

链表的优点:

​ 由于链表上的元素在空间存储上内存地址不连续。
​ 所以随机增删元素的时候不会有大量元素位移,因此随机增删效率较高。
​ 在以后的开发中,如果遇到随机增删集合中元素的业务比较多时,建议
​ 使用LinkedList。

链表的缺点:

​ 不能通过数学表达式计算被查找元素的内存地址,每一次查找都是从头
​ 节点开始遍历,直到找到为止。所以LinkedList集合检索/查找的效率
​ 较低。

ArrayList:把检索发挥到极致。(末尾添加元素效率还是很高的。)
LinkedList:把随机增删发挥到极致。
加元素都是往末尾添加,所以ArrayList用的比LinkedList多。


import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class LinkedListTest01 {
    public static void main(String[] args) {
        // LinkedList集合底层也是有下标的。
        // 注意:ArrayList之所以检索效率比较高,不是单纯因为下标的原因。是因为底层数组发挥的作用。
        // LinkedList集合照样有下标,但是检索/查找某个元素的时候效率比较低,因为只能从头节点开始一个一个遍历。
        List list = new LinkedList();
        list.add("a");
        list.add("b");
        list.add("c");

        for(int i = 0; i <list.size(); i++){
            Object obj = list.get(i);
            System.out.println(obj);
        }
----------------------------------------------
        // LinkedList集合有初始化容量吗?没有。
        // 最初这个链表中没有任何元素。first和last引用都是null。
        // 不管是LinkedList还是ArrayList,以后写代码时不需要关心具体是哪个集合。
        // 因为我们要面向接口编程,调用的方法都是接口中的方法。
        //List list2 = new ArrayList(); // 这样写表示底层你用了数组。
        List list2 = new LinkedList(); // 这样写表示底层你用了双向链表。

        // 以下这些方法你面向的都是接口编程。
        list2.add("123");
        list2.add("456");
        list2.add("789");

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

    }
}

双向链表006-双向链表

LinkedList源码分析

image-20221124201651789

image-20221124202026946

007-LinkedList内存图

Vector

1、底层也是一个数组。
2、初始化容量:10
3、怎么扩容的?
扩容之后是原容量的2倍。
10--> 20 --> 40 --> 80

4、ArrayList集合扩容特点:
ArrayList集合扩容是原容量1.5倍。

5、Vector中所有的方法都是线程同步的,都带有synchronized关键字,
是线程安全的。效率比较低,使用较少了。

6、怎么将一个线程不安全的ArrayList集合转换成线程安全的呢?
使用集合工具类:
java.util.Collections;

​ java.util.Collection 是集合接口。
​ java.util.Collections 是集合工具类。

public class VectorTest {
    public static void main(String[] args) {
        // 创建一个Vector集合
        List vector = new Vector();
        //Vector vector = new Vector();

        // 添加元素
        // 默认容量10个。
        vector.add(1);
        vector.add(2);
        vector.add(3);
        vector.add(4);
        vector.add(5);
        vector.add(6);
        vector.add(7);
        vector.add(8);
        vector.add(9);
        vector.add(10);

        // 满了之后扩容(扩容之后的容量是20.)
        vector.add(11);

        Iterator it = vector.iterator();
        while(it.hasNext()){
            Object obj = it.next();
            System.out.println(obj);
        }

        // 这个可能以后要使用!!!!
        List myList = new ArrayList(); // 非线程安全的。

        // 变成线程安全的
        Collections.synchronizedList(myList); // 这里没有办法看效果,因为多线程没学,你记住先!

        // myList集合就是线程安全的了。
        myList.add("111");
        myList.add("222");
        myList.add("333");
    }
}

泛型机制

1、JDK5.0之后推出的新特性:泛型
2、泛型这种语法机制,只在程序编译阶段起作用,只是给编译器参考的。(运行阶段泛型没用!)
3、使用了泛型好处是什么?
第一:集合中存储的元素类型统一了。
第二:从集合中取出的元素类型是泛型指定的类型,不需要进行大量的“向下转型”!

4、泛型的缺点是什么?
导致集合中存储的元素缺乏多样性!
大多数业务中,集合中元素的类型还是统一的。所以这种泛型特性被大家所认可。

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

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

        /*
        // 不使用泛型机制,分析程序存在缺点
        List myList = new ArrayList();

        // 准备对象
        Cat c = new Cat();
        Bird b = new Bird();

        // 将对象添加到集合当中
        myList.add(c);
        myList.add(b);

        // 遍历集合,取出每个Animal,让它move
        Iterator it = myList.iterator();
        while(it.hasNext()) {
            // 没有这个语法,通过迭代器取出的就是Object
            //Animal a = it.next();

            Object obj = it.next();
            //obj中没有move方法,无法调用,需要向下转型!
            if(obj instanceof Animal){
                Animal a = (Animal)obj;
                a.move();
            }
        }
         */

        // 使用JDK5之后的泛型机制
        // 使用泛型List<Animal>之后,表示List集合中只允许存储Animal类型的数据。
        // 用泛型来指定集合中存储的数据类型。
        List<Animal> myList = new ArrayList<Animal>();

        // 指定List集合中只能存储Animal,那么存储String就编译报错了。
        // 这样用了泛型之后,集合中元素的数据类型更加统一了。
        //myList.add("abc");

        Cat c = new Cat();
        Bird b = new Bird();

        myList.add(c);
        myList.add(b);

        // 获取迭代器
        // 这个表示迭代器迭代的是Animal类型。
        Iterator<Animal> it = myList.iterator();
        while(it.hasNext()){
            // 使用泛型之后,每一次迭代返回的数据都是Animal类型。
            //Animal a = it.next();
            // 这里不需要进行强制类型转换了。直接调用。
            //a.move();

            // 调用子类型特有的方法还是需要向下转换的!
            Animal a = it.next();
            if(a instanceof Cat) {
                Cat x = (Cat)a;
                x.catchMouse();
            }
            if(a instanceof Bird) {
                Bird y = (Bird)a;
                y.fly();
            }
        }
    }
}

class Animal {
    // 父类自带方法
    public void move(){
        System.out.println("动物在移动!");
    }
}

class Cat extends Animal {
    // 特有方法
    public void catchMouse(){
        System.out.println("猫抓老鼠!");
    }
}

class Bird extends Animal {
    // 特有方法
    public void fly(){
        System.out.println("鸟儿在飞翔!");
    }
}

类型自动推断

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

/*
JDK之后引入了:自动类型推断机制。(又称为钻石表达式)
 */
public class GenericTest02 {
    public static void main(String[] args) {

​        // ArrayList<这里的类型会自动推断>(),前提是JDK8之后才允许。
​        // 自动类型推断,钻石表达式!
​        List<Animal> myList = new ArrayList<>();

​        myList.add(new Animal());
​        myList.add(new Cat());
​        myList.add(new Bird());

​        // 遍历
​        Iterator<Animal> it = myList.iterator();
​        while(it.hasNext()){
​            Animal a = it.next();
​            a.move();
​        }

​        List<String> strList = new ArrayList<>();

​        // 类型不匹配。
​        //strList.add(new Cat());
​        strList.add("http://www.126.com");
​        strList.add("http://www.baidu.com");
​        strList.add("http://www.b.com");

​        // 类型不匹配。
​        //strList.add(123);

​        //System.out.println(strList.size());

​        // 遍历
​        Iterator<String> it2 = strList.iterator();
​        while(it2.hasNext()){
​            // 如果没有使用泛型
​            /*
​            Object obj = it2.next();
​            if(obj instanceof String){
​                String ss = (String)obj;
​                ss.substring(7);
​            }
​             */
​            // 直接通过迭代器获取了String类型的数据
​            String s = it2.next();
​            // 直接调用String类的substring方法截取字符串。
​            String newString = s.substring(7);
​            System.out.println(newString);
​        }
​    }
}

自定义泛型

自定义泛型可以吗?可以
自定义泛型的时候

<> 尖括号中的是一个标识符,随便写。
java源代码中经常出现的是:

E是Element单词首字母。
T是Type单词首字母。


public class GenericTest03<标识符随便写> {

    public void doSome(标识符随便写 o){
        System.out.println(o);
    }

    public static void main(String[] args) {

        // new对象的时候指定了泛型是:String类型
        GenericTest03<String> gt = new GenericTest03<>();

        // 类型不匹配
        //gt.doSome(100);

        gt.doSome("abc");

        // =============================================================
        GenericTest03<Integer> gt2 = new GenericTest03<>();
        gt2.doSome(100);

        // 类型不匹配
        //gt2.doSome("abc");

        MyIterator<String> mi = new MyIterator<>();
        String s1 = mi.get();

        MyIterator<Animal> mi2 = new MyIterator<>();
        Animal a = mi2.get();

        // 不用泛型就是Object类型。
        /*GenericTest03 gt3 = new GenericTest03();
        gt3.doSome(new Object());*/
    }
}

class MyIterator<T> {
    public T get(){
        return null;
    }
}

增强for循环(foreach)

/*
JDK5.0之后推出了一个新特性:叫做增强for循环,或者叫做foreach
 */
public class ForEachTest01 {
    public static void main(String[] args) {

        // int类型数组
        int[] arr = {432,4,65,46,54,76,54};

        // 遍历数组(普通for循环)
        for(int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }

        // 增强for(foreach)
        // 以下是语法
        /*for(元素类型 变量名 : 数组或集合){
            System.out.println(变量名);
        }*/

        System.out.println("======================================");
        // foreach有一个缺点:没有下标。在需要使用下标的循环中,不建议使用增强for循环。
        for(int data : arr) {
            // data就是数组中的元素(数组中的每一个元素。)
            System.out.println(data);
        }
    }
}
/*
集合使用foreach
 */
public class ForEachTest02 {
    public static void main(String[] args) {
        // 创建List集合
        List<String> strList = new ArrayList<>();

        // 添加元素
        strList.add("hello");
        strList.add("world!");
        strList.add("kitty!");

        // 遍历,使用迭代器方式
        Iterator<String> it = strList.iterator();
        while(it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }

        // 使用下标方式(只针对于有下标的集合)
        for(int i = 0; i < strList.size(); i++){
            System.out.println(strList.get(i));
        }

        // 使用foreach
        for(String s : strList){ // 因为泛型使用的是String类型,所以是:String s
            System.out.println(s);
        }

        List<Integer> list = new ArrayList<>();
        list.add(100);
        list.add(200);
        list.add(300);
        for(Integer i : list){ // i代表集合中的元素
            System.out.println(i);
        }
    }
}

HashSet集合特点

/*
HashSet集合:
    无序不可重复。
 */
public class HashSetTest01 {
    public static void main(String[] args) {
        // 演示一下HashSet集合特点
        Set<String> strs = new HashSet<>();

        // 添加元素
        strs.add("hello3");
        strs.add("hello4");
        strs.add("hello1");
        strs.add("hello2");
        strs.add("hello3");
        strs.add("hello3");
        strs.add("hello3");
        strs.add("hello3");

        // 遍历
        /*
        hello1
        hello4
        hello2
        hello3
        1、存储时顺序和取出的顺序不同。
        2、不可重复。
        3、放到HashSet集合中的元素实际上是放到HashMap集合的key部分了。
         */
        for(String s : strs){
            System.out.println(s);
        }
    }
}

TreeSet集合

import java.util.Set;
import java.util.TreeSet;

/*
TreeSet集合存储元素特点:
    1、无序不可重复的,但是存储的元素可以自动按照大小顺序排序!
    称为:可排序集合。

    2、无序:这里的无序指的是存进去的顺序和取出来的顺序不同。并且没有下标。
 */
public class TreeSetTest01 {
    public static void main(String[] args) {
        // 创建集合对象
        Set<String> strs = new TreeSet<>();
        // 添加元素
        strs.add("A");
        strs.add("B");
        strs.add("Z");
        strs.add("Y");
        strs.add("Z");
        strs.add("K");
        strs.add("M");
        // 遍历
        /*
            A
            B
            K
            M
            Y
            Z
        从小到大自动排序!
         */
        for(String s : strs){
            System.out.println(s);
        }
    }
}

Map接口常用方法



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

public class MyClass {

    // 声明一个静态内部类
    private static class InnerClass {

        // 静态方法
        public static void m1(){
            System.out.println("静态内部类的m1方法执行");
        }

        // 实例方法
        public void m2(){
            System.out.println("静态内部类中的实例方法执行!");
        }

    }

    public static void main(String[] args) {

        // 类名叫做:MyClass.InnerClass
        MyClass.InnerClass.m1();

        // 创建静态内部类对象
        MyClass.InnerClass mi =  new MyClass.InnerClass();
        mi.m2();

        // 给一个Set集合
        // 该Set集合中存储的对象是:MyClass.InnerClass类型
        Set<MyClass.InnerClass> set = new HashSet<>();

        // 这个Set集合中存储的是字符串对象。
        Set<String> set2 = new HashSet<>();

        Set<MyMap.MyEntry<Integer, String>> set3 = new HashSet<>();
    }
}


class MyMap {
    public static class MyEntry<K,V> {

    }
}

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

/*
java.util.Map接口中常用的方法:
    1、Map和Collection没有继承关系。
    2、Map集合以key和value的方式存储数据:键值对
        key和value都是引用数据类型。
        key和value都是存储对象的内存地址。
        key起到主导的地位,value是key的一个附属品。
    3、Map接口中常用方法:
        V put(K key, V value) 向Map集合中添加键值对
        V get(Object key) 通过key获取value
        void clear()    清空Map集合
        boolean containsKey(Object key) 判断Map中是否包含某个key
        boolean containsValue(Object value) 判断Map中是否包含某个value
        boolean isEmpty()   判断Map集合中元素个数是否为0
        V remove(Object key) 通过key删除键值对
        int size() 获取Map集合中键值对的个数。
        Collection<V> values() 获取Map集合中所有的value,返回一个Collection

        Set<K> keySet() 获取Map集合所有的key(所有的键是一个set集合)

        Set<Map.Entry<K,V>> entrySet()
            将Map集合转换成Set集合
            假设现在有一个Map集合,如下所示:
                map1集合对象
                key             value
                ----------------------------
                1               zhangsan
                2               lisi
                3               wangwu
                4               zhaoliu

                Set set = map1.entrySet();
                set集合对象
                1=zhangsan 【注意:Map集合通过entrySet()方法转换成的这个Set集合,Set集合中元素的类型是 Map.Entry<K,V>】
                2=lisi     【Map.Entry和String一样,都是一种类型的名字,只不过:Map.Entry是静态内部类,是Map中的静态内部类】
                3=wangwu
                4=zhaoliu ---> 这个东西是个什么?Map.Entry
 */
public class MapTest01 {
    public static void main(String[] args) {
        // 创建Map集合对象
        Map<Integer, String> map = new HashMap<>();
        // 向Map集合中添加键值对
        map.put(1, "zhangsan"); // 1在这里进行了自动装箱。
        map.put(2, "lisi");
        map.put(3, "wangwu");
        map.put(4, "zhaoliu");
        // 通过key获取value
        String value = map.get(2);
        System.out.println(value);
        // 获取键值对的数量
        System.out.println("键值对的数量:" + map.size());
        // 通过key删除key-value
        map.remove(2);
        System.out.println("键值对的数量:" + map.size());
        // 判断是否包含某个key
        // contains方法底层调用的都是equals进行比对的,所以自定义的类型需要重写equals方法。
        System.out.println(map.containsKey(new Integer(4))); // true
        // 判断是否包含某个value
        System.out.println(map.containsValue(new String("wangwu"))); // true

        // 获取所有的value
        Collection<String> values = map.values();
        // foreach
        for(String s : values){
            System.out.println(s);
        }

        // 清空map集合
        map.clear();
        System.out.println("键值对的数量:" + map.size());
        // 判断是否为空
        System.out.println(map.isEmpty()); // true
    }
}

遍历Map集合


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

/*
Map集合的遍历。【非常重要】
 */
public class MapTest02 {
    public static void main(String[] args) {

        // 第一种方式:获取所有的key,通过遍历key,来遍历value
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "zhangsan");
        map.put(2, "lisi");
        map.put(3, "wangwu");
        map.put(4, "zhaoliu");
        // 遍历Map集合
        // 获取所有的key,所有的key是一个Set集合
        Set<Integer> keys = map.keySet();
        // 遍历key,通过key获取value
        // 迭代器可以
        /*Iterator<Integer> it = keys.iterator();
        while(it.hasNext()){
            // 取出其中一个key
            Integer key = it.next();
            // 通过key获取value
            String value = map.get(key);
            System.out.println(key + "=" + value);
        }*/
        // foreach也可以
        for(Integer key : keys){
            System.out.println(key + "=" + map.get(key));
        }

        // 第二种方式:Set<Map.Entry<K,V>> entrySet()
        // 以上这个方法是把Map集合直接全部转换成Set集合。
        // Set集合中元素的类型是:Map.Entry
        Set<Map.Entry<Integer,String>> set = map.entrySet();
        // 遍历Set集合,每一次取出一个Node
        // 迭代器
        /*Iterator<Map.Entry<Integer,String>> it2 = set.iterator();
        while(it2.hasNext()){
            Map.Entry<Integer,String> node = it2.next();
            Integer key = node.getKey();
            String value = node.getValue();
            System.out.println(key + "=" + value);
        }*/

        // foreach
        // 这种方式效率比较高,因为获取key和value都是直接从node对象中获取的属性值。
        // 这种方式比较适合于大数据量。
        for(Map.Entry<Integer,String> node : set){
            System.out.println(node.getKey() + "--->" + node.getValue());
        }
    }
}

image-20221127132615148

新方法


哈希表数据结构

     HashMap集合

​ 1、HashMap集合底层是哈希表/散列表的数据结构。
​ 2、哈希表是一个怎样的数据结构呢?
​ 哈希表是一个数组和单向链表的结合体。
​ 数组:在查询方面效率很高,随机增删方面效率很低。
​ 单向链表:在随机增删方面效率较高,在查询方面效率很低。
​ 哈希表将以上的两种数据结构融合在一起,充分发挥它们各自的优点。
​ 3、HashMap集合底层的源代码:
​ public class HashMap{
​ // HashMap底层实际上就是一个数组。(一维数组)
​ Node<K,V>[] table;
​ // 静态的内部类HashMap.Node
​ static class Node<K,V> {
​ final int hash; // 哈希值(哈希值是key的hashCode()方法的执行结果。hash值通过哈希函数/算法,可以转换存储成数组的下标。)
​ final K key; // 存储到Map集合中的那个key
​ V value; // 存储到Map集合中的那个value
​ Node<K,V> next; // 下一个节点的内存地址。
​ }
​ }
​ 哈希表/散列表:一维数组,这个数组中每一个元素是一个单向链表。(数组和链表的结合体。)
​ 4、最主要掌握的是:
​ map.put(k,v)
​ v = map.get(k)
​ 以上这两个方法的实现原理,是必须掌握的。
​ 5、HashMap集合的key部分特点:
​ 无序,不可重复。
​ 为什么无序? 因为不一定挂到哪个单向链表上。
​ 不可重复是怎么保证的? equals方法来保证HashMap集合的key不可重复。
​ 如果key重复了,value会覆盖。

​ 放在HashMap集合key部分的元素其实就是放到HashSet集合中了。
​ 所以HashSet集合中的元素也需要同时重写hashCode()+equals()方法。

​ 6、哈希表HashMap使用不当时无法发挥性能!
​ 假设将所有的hashCode()方法返回值固定为某个值,那么会导致底层哈希表变成了
​ 纯单向链表。这种情况我们成为:散列分布不均匀。
​ 什么是散列分布均匀?
​ 假设有100个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的,
​ 是散列分布均匀的。
​ 假设将所有的hashCode()方法返回值都设定为不一样的值,可以吗,有什么问题?
​ 不行,因为这样的话导致底层哈希表就成为一维数组了,没有链表的概念了。
​ 也是散列分布不均匀。
​ 散列分布均匀需要你重写hashCode()方法时有一定的技巧。
​ 7、重点:放在HashMap集合key部分的元素,以及放在HashSet集合中的元素,需要同时重写hashCode和equals方法。
​ 8、HashMap集合的默认初始化容量是16,默认加载因子是0.75
​ 这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组开始扩容。

​ 重点,记住:

HashMap集合初始化容量必须是2的倍数,这也是官方推荐的,
​ 这是因为达到散列均匀,为了提高HashMap集合的存取效率,所必须的。

*/


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

public class HashMapTest01 {
    public static void main(String[] args) {
        // 测试HashMap集合key部分的元素特点
        // Integer是key,它的hashCode和equals都重写了。
        Map<Integer,String> map = new HashMap<>();
        map.put(1111, "zhangsan");
        map.put(6666, "lisi");
        map.put(7777, "wangwu");
        map.put(2222, "zhaoliu");
        map.put(2222, "king"); //key重复的时候value会自动覆盖。

        System.out.println(map.size()); // 4

        // 遍历Map集合
        Set<Map.Entry<Integer,String>> set = map.entrySet();
        for(Map.Entry<Integer,String> entry : set){
            // 验证结果:HashMap集合key部分元素:无序不可重复。
            System.out.println(entry.getKey() + "=" + entry.getValue());
        }
    }
}

equals返回true 代表就是同一个对象 同一个对象的内存空间都是一个,哈希值相同,哈希算法算出下标都相同,然后单向链表上只有一个

比如我们还拿 Set 集合举例,Set 首先会调用对象的 hashCode 方法寻找对象的存储位置,如果两个相同的对象调用 hashCode 方法得到的结果不同,那么造成的后果就是 Set 中存储了相同的元素,而这样的结果肯定是不对的。所以就要求 调用 equals 返回 true 的两个对象必须具有相等的哈希码

  1. 调用 equals 返回 true 的两个对象必须具有相等的哈希码。
  2. 如果两个对象的 hashCode 返回值相同,调用它们 equals 方法不一返回 true 。

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

/*
1、向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,然后再调用equals方法!
equals方法有可能调用,也有可能不调用。
    拿put(k,v)举例,什么时候equals不会调用?
        k.hashCode()方法返回哈希值,
        哈希值经过哈希算法转换成数组下标。
        数组下标位置上如果是null,equals不需要执行。
    拿get(k)举例,什么时候equals不会调用?
        k.hashCode()方法返回哈希值,
        哈希值经过哈希算法转换成数组下标。
        数组下标位置上如果是null,equals不需要执行。

2、注意:如果一个类的equals方法重写了,那么hashCode()方法必须重写。
并且equals方法返回如果是true,hashCode()方法返回的值必须一样。
    equals方法返回true表示两个对象相同,在同一个单向链表上比较。
    那么对于同一个单向链表上的节点来说,他们的哈希值都是相同的。
    所以hashCode()方法的返回值也应该相同。

3、hashCode()方法和equals()方法不用研究了,直接使用IDEA工具生成,但是这两个方法需要同时生成。

4、终极结论:
    放在HashMap集合key部分的,以及放在HashSet集合中的元素,需要同时重写hashCode方法和equals方法。

5、对于哈希表数据结构来说:
    如果o1和o2的hash值相同,一定是放到同一个单向链表上。
    当然如果o1和o2的hash值不同,但由于哈希算法执行结束之后转换的数组下标可能相同,此时会发生“哈希碰撞”。

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

        Student s1 = new Student("zhangsan");
        Student s2 = new Student("zhangsan");

        // 重写equals方法之前是false
        //System.out.println(s1.equals(s2)); // false

        // 重写equals方法之后是true
        System.out.println(s1.equals(s2)); //true (s1和s2表示相等)

        System.out.println("s1的hashCode=" + s1.hashCode()); //284720968 (重写hashCode之后-1432604525)
        System.out.println("s2的hashCode=" + s2.hashCode()); //122883338 (重写hashCode之后-1432604525)

        // s1.equals(s2)结果已经是true了,表示s1和s2是一样的,相同的,那么往HashSet集合中放的话,
        // 按说只能放进去1个。(HashSet集合特点:无序不可重复)
        Set<Student> students = new HashSet<>();
        students.add(s1);
        students.add(s2);
        System.out.println(students.size()); // 这个结果按说应该是1. 但是结果是2.显然不符合HashSet集合存储特点。怎么办?
    }
}

import java.util.Objects;

public class Student {
    private String name;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

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

    // hashCode

    // equals(如果学生名字一样,表示同一个学生。)
    /*public boolean equals(Object obj){
        if(obj == null || !(obj instanceof Student)) return false;
        if(obj == this) return true;
        Student s = (Student)obj;
        return this.name.equals(s.name);
    }*/

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

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

HashMap和Hashtable的区别


import java.util.Hashtable;
import java.util.Map;

/*
Hashtable的key可以为null吗?
    Hashtable的key和value都是不能为null的。
    HashMap集合的key和value都是可以为null的。

Hashtable方法都带有synchronized:线程安全的。
线程安全有其它的方案,这个Hashtable对线程的处理
导致效率较低,使用较少了。

Hashtable和HashMap一样,底层都是哈希表数据结构。
Hashtable的初始化容量是11,默认加载因子是:0.75f
Hashtable的扩容是:原容量 * 2 + 1
 */
public class HashtableTest01 {
    public static void main(String[] args) {
        Map map = new Hashtable();

        //map.put(null, "123");
        map.put(100, null);

    }
}

属性类Properties类

import java.util.Properties;

/*
目前只需要掌握Properties属性类对象的相关方法即可。
Properties是一个Map集合,继承Hashtable,Properties的key和value都是String类型。
Properties被称为属性类对象。
Properties是线程安全的。
 */
public class PropertiesTest01 {
    public static void main(String[] args) {

        // 创建一个Properties对象
        Properties pro = new Properties();

        // 需要掌握Properties的两个方法,一个存,一个取。
        pro.setProperty("url", "jdbc:mysql://localhost:3306/bjpowernode");
        pro.setProperty("driver","com.mysql.jdbc.Driver");
        pro.setProperty("username", "root");
        pro.setProperty("password", "123");

        // 通过key获取value
        String url = pro.getProperty("url");
        String driver = pro.getProperty("driver");
        String username = pro.getProperty("username");
        String password = pro.getProperty("password");

        System.out.println(url);
        System.out.println(driver);
        System.out.println(username);
        System.out.println(password);

    }
}
import java.util.TreeSet;

/*
1、TreeSet集合底层实际上是一个TreeMap
2、TreeMap集合底层是一个二叉树。
3、放到TreeSet集合中的元素,等同于放到TreeMap集合key部分了。
4、TreeSet集合中的元素:无序不可重复,但是可以按照元素的大小顺序自动排序。
称为:可排序集合。
 */
public class TreeSetTest02 {
    public static void main(String[] args) {
        // 创建一个TreeSet集合
        TreeSet<String> ts = new TreeSet<>();
        // 添加String
        ts.add("zhangsan");
        ts.add("lisi");
        ts.add("wangwu");
        ts.add("zhangsi");
        ts.add("wangliu");
        // 遍历
        for(String s : ts){
            // 按照字典顺序,升序!
            System.out.println(s);
        }

        TreeSet<Integer> ts2 = new TreeSet<>();
        ts2.add(100);
        ts2.add(200);
        ts2.add(900);
        ts2.add(800);
        ts2.add(600);
        ts2.add(10);
        for(Integer elt : ts2){
            // 升序!
            System.out.println(elt);
        }
    }
}

/*
数据库中有很多数据:
    userid  name     birth
    -------------------------------------
    1       zs          1980-11-11
    2       ls          1980-10-11
    3       ww          1981-11-11
    4       zl          1979-11-11

    编写程序从数据库当中取出数据,在页面展示用户信息的时候按照生日升序或者降序。
    这个时候可以使用TreeSet集合,因为TreeSet集合放进去,拿出来就是有顺序的。
 */

TreeSet无法对自定义类型排序

import java.util.TreeSet;

/*
对自定义的类型来说,TreeSet可以排序吗?
    以下程序中对于Person类型来说,无法排序。因为没有指定Person对象之间的比较规则。
    谁大谁小并没有说明啊。

    以下程序运行的时候出现了这个异常:
        java.lang.ClassCastException:
            class com.bjpowernode.javase.collection.Person
            cannot be cast to class java.lang.Comparable
    出现这个异常的原因是:
        Person类没有实现java.lang.Comparable接口。
 */
public class TreeSetTest03 {
    public static void main(String[] args) {
        Person p1 = new Person(32);
        //System.out.println(p1);
        Person p2 = new Person(20);
        Person p3 = new Person(30);
        Person p4 = new Person(25);

        // 创建TreeSet集合
        TreeSet<Person> persons = new TreeSet<>();
        // 添加元素
        persons.add(p1);
        persons.add(p2);
        persons.add(p3);
        persons.add(p4);

        // 遍历
        for (Person p : persons){
            System.out.println(p);
        }
    }
}

class Person {
    int age;
    public Person(int age){
        this.age = age;
    }

    // 重写toString()方法
    public String toString(){
        return "Person[age="+age+"]";
    }
}

自定义类型实现Comparable接口


import java.util.TreeSet;

public class TreeSetTest04 {
    public static void main(String[] args) {
        Customer c1 = new Customer(32);
        Customer c2 = new Customer(20);
        Customer c3 = new Customer(30);
        Customer c4 = new Customer(25);

        // 创建TreeSet集合
        TreeSet<Customer> customers = new TreeSet<>();
        // 添加元素
        customers.add(c1);
        customers.add(c2);
        customers.add(c3);
        customers.add(c4);

        // 遍历
        for (Customer c : customers){
            System.out.println(c);
        }
    }
}

// 放在TreeSet集合中的元素需要实现java.lang.Comparable接口。
// 并且实现compareTo方法。equals可以不写。
class Customer implements Comparable<Customer>{

    int age;
    public Customer(int age){
        this.age = age;
    }

    // 需要在这个方法中编写比较的逻辑,或者说比较的规则,按照什么进行比较!
    // k.compareTo(t.key)
    // 拿着参数k和集合中的每一个k进行比较,返回值可能是>0 <0 =0
    // 比较规则最终还是由程序员指定的:例如按照年龄升序。或者按照年龄降序。
    @Override
    public int compareTo(Customer c) { // c1.compareTo(c2);
        // this是c1
        // c是c2
        // c1和c2比较的时候,就是this和c比较。
        /*int age1 = this.age;
        int age2 = c.age;
        if(age1 == age2){
            return 0;
        } else if(age1 > age2) {
            return 1;
        } else {
            return -1;
        }*/
        //return this.age - c.age; // =0 >0 <0
        return c.age - this.age;
    }

    public String toString(){
        return "Customer[age="+age+"]";
    }
}

把这个听完后先去听JDBC,再去听数据库sql和数据结构

比较规则

/*
先按照年龄升序,如果年龄一样的再按照姓名升序。
 */
public class TreeSetTest05 {
    public static void main(String[] args) {
        TreeSet<Vip> vips = new TreeSet<>();
        vips.add(new Vip("zhangsi", 20));
        vips.add(new Vip("zhangsan", 20));
        vips.add(new Vip("king", 18));
        vips.add(new Vip("soft", 17));
        for(Vip vip : vips){
            System.out.println(vip);
        }
    }
}

class Vip implements Comparable<Vip>{
    String name;
    int age;

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

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

    /*
    compareTo方法的返回值很重要:
        返回0表示相同,value会覆盖。
        返回>0,会继续在右子树上找。【10 - 9 = 1 ,1 > 0的说明左边这个数字比较大。所以在右子树上找。】
        返回<0,会继续在左子树上找。
     */
    @Override
    public int compareTo(Vip v) {
        // 写排序规则,按照什么进行比较。
        if(this.age == v.age){
            // 年龄相同时按照名字排序。
            // 姓名是String类型,可以直接比。调用compareTo来完成比较。
            return this.name.compareTo(v.name);
        } else {
            // 年龄不一样
            return this.age - v.age;
        }
    }
}

自平衡二叉树数据结构

010-自平衡二叉树

标签:println,add,集合,new,public,out
From: https://www.cnblogs.com/LDCnc-lili/p/16937037.html

相关文章

  • 集合习题
    基于集合,传入字符串时间,返回该时间是周几privatestaticvoiddemo1(){//基于集合,传入字符串时间,返回该时间是周几List<String>list=Arrays.asList("周日......
  • 集合习题分数排序
    创建一个学生类,属性包括id[1-40],分数0-100,所有属性随机生成,创建set集合,保存20个对象,找到分数最高和分数最低的学生privatestaticvoiddemo2(){//用treeset......
  • Kotlin系列六:从集合谈Kotlin中的Lambda编程
    目录 ​​一集合的函数式API​​​​1.1.1List​​​​1.1.2 Set​​​​1.1.3 Map​​​​二集合的函数式API​​​​2.1 集合的函数式API的推导​​​​2.1集合常......
  • JAVA面试题--Java集合
    Java集合1.常见的集合有哪些?2.常见的集合底层实现3.HashMap与HashTable的区别?4.ConcurrentHashMap和Hashtable的区别?5.ConcurrentHashMap实现原理6.ArrayList和Ve......
  • OCC集合类 NCollection_Array1源码解析
    //NCollection_Array1是一个模板类,容器类是模板类我们对这应该很熟悉了,毕竟为所有类型单独写一个容器类是不现实也没必要的。//元素内存是顺序存储的,类似vector//此类......
  • 集合
    集合集合概述数组其实就是一个集合。集合实际上就是一个容器。可以来容纳其它类型的数据。集合为什么说在开发中使用较多?集合是一个容器,是一个载体,可以一次容纳多个对......
  • 集合栈计算机(The SetStack Computer)
    TheSetStackComputerTimelimit:3.000seconds题目是这样的:      有一个专门为了集合运算而设计的“集合栈”计算机。该机器有一个初始为空的栈,并且支持以下操......
  • 学习过程中遇到报错 通过网络和自己的理解独立解决问题的集合 这个将会是我的宝藏哦
    第一次自己独立解决问题2022-11-2817:37:34星期一1.报错信息:TypeError:objectsupportingthebufferAPIrequired2.翻译报错:TypeError:需要支持缓冲区API的对象3......
  • Java中数组、集合初始化及遍历方式
    一、数组1.一维数组一维数组两种初始化方式静态初始化int[]array={1,2,3};int[]array=newint[]{1,2,3};动态初始化int[]array=newint[3......
  • Immutable(不可变)集合
    不可变集合,顾名思义就是说集合是不可被修改的。集合的数据项是在创建的时候提供,并且在整个生命周期中都不可改变。为什么要用immutable对象?immutable对象有以下的优点:对不可......