Java之集合万字详解

集合



前言

早在 Java 2 中之前,Java 就提供了特设类。比如:Dictionary, Vector, Stack, 和 Properties 这些类用来存储和操作对象组。虽然这些类都非常有用,但是它们缺少一个核心的,统一的主题。由于这个原因,使用 Vector 类的方式和使用 Properties 类的方式有着很大不同。为此,整个集合框架就围绕一组标准接口而设计。你可以直接使用这些接口的标准实现,诸如: LinkedList, HashSet, 和 TreeSet 等,除此之外你也可以通过这些接口实现自己的集合


一、认识集合框架

1.1 区分集合与数组存储

集合的特点:
1、提供一种存储空间可变的存储模型, 存储的数据容量可以发生改变

数组存储的特点:
①数组初始化以后,长度就确定了
②数组声明类型,就决定了进行元素初始化的类型

数组存储的弊端:
①一旦初始化以后,长度就不可以改变
②数组存储都是有序,可重复的,对于我们的无序,不可以重复的需求不够友好
③获取数组中实际元素的个数需求,数组没有现成的属性或方法使用
④提供的方法使用有限

集合存储的优点:
解决数组存储数据方面的弊端

1.2 Java集合中的两种体系

在这里插入图片描述

从上面的集合框架图可以看到,Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。Collection 接口又有 3 种子类型,List、Set 和 Queue,再下面是一些抽象类,最后是具体实现类,常用的有 ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap 等等

二、ArrayList集合

1、ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制
2、ArrayList 继承了 AbstractList ,并实现了 List 接口
3、.ArrayList 类位于 java.util 包中,使用前需要引入它
4、ArrayList底层基于数组实现

ArrayList<E> objectName =new ArrayList<>();

常用方法

方法名称说明
public boolean add(E e)将元素插入到指定位置的 arraylist 中
public E remove(int index)删除 arraylist 里的单个元素
public E set(int index, E element)替换 arraylist 中指定索引的元素
public E get(int index)通过索引值获取 arraylist 中的元素
public int size()返回 arraylist 里元素数量
List<String> arrayList = new ArrayList<String>();
        //arrayList 对象名称
        arrayList.add("apple");
        arrayList.add("banana");
        arrayList.add("pear");
        arrayList.add("watermelon");
        System.out.println("集合中存入的元素有" + arrayList.size() + "个");
arrayList.remove(1);
arrayList.set(0,"peach");
//根据index 获取元素
        System.out.println(arrayList.get(0)); //apple
        System.out.println(arrayList.get(1)); //banana
        System.out.println(arrayList.get(2)); //pear
        System.out.println(arrayList.get(3)); //watermelon
  for (int i = 0; i < arrayList.size(); i++) {
            System.out.println(arrayList.get(i));
        }

练习:
创建一个存储学生对象的集合, 存储5个学生对象,遍历学生对象

package com.gance.xyz.day13;

/**
 * @author 杰仔正在努力
 * @create 2022-11-23 18:02
 */
public class Student {
    private String userName;
    private int age;
    /**
     * 1、构造方法
     * 2、set方法
     */

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

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getAge() {
        return age;
    }

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

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

package com.gance.xyz.day13;

import java.util.ArrayList;
import java.util.Scanner;

/**
 * @author 杰仔正在努力
 * @create 2022-11-23 18:13
 */
public class Test03 {
    public static void main(String[] args) {
        ArrayList<Student> students = new ArrayList<>();
        for (int i = 1; i <= 5; i++) {
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入第" + i + "位学生的名称:");
            String userName = scanner.nextLine();
            System.out.println("请输入第" + i + "位学位的年龄:");
            int age = scanner.nextInt();
            students.add(new Student(userName,age));
        }
        for (int i = 0; i <students.size(); i++) {
            Student student = students.get(i);
            System.out.println("姓名:" + student.getUserName() + student.getAge());
        }
    }
}

三、Collection 接口

Collection 接口是 List、Set 和 Queue 接口的父接口,该接口里定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合

add(Object e);//将元素e添加到集合中
size();//获取添加的元素的个数
addAll();//将集合中的元素添加到当前的集合中
isEmpty();//判断当前集合是否为空
clear();//清空集合元素
contains(Object obj);//判断当前集合中是否包含obj
containsAll(Collection coll): //判断形参coll中所有的元素是否都存在于当前集合中
remove(Object obj): //从当前集合中移除obj元素
removeAll(Collection coll): //差集:从当前集中移除coll中的所有元素
retainAll(Collection coll): //交集:获取当前集合和coll集合的交集,并返回当前集合
equals(Object obj): //要想返回true,需要判当前集合和形参相同
hashCode(): //返回当前对象的哈希值

Collection集合与数组间的转换

//集合 --->数组:toArray()
Object[] arr = coll.toArray();
for(int i = 0;i < arr.length;i++){
    System.out.println(arr[i]);
}
//数组 --->集合:调用Arrays类的静态方法asList(T ... t)
List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});
System.out.println(list);

练习
在这里插入图片描述

package com.gance.xyz.day16;

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

/**
 * @author 杰仔正在努力
 * @create 2022-11-26 18:39
 */
public class Test02 {
    public static void main(String[] args) {
        //多态  由上图可知
        Collection<String> collection = new ArrayList<>();
        collection.add("apple1");
        collection.add("apple2");
        collection.add("apple3");
        //get方法 是在list接口中
        /**
         * 在Collection接口是没有个体方法
         */
        List list = (List) collection;
        list.add(1,"apple");
        Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }

    /**
     * Collection 接口提供 集合的基本方法
     * 具体实现 List ArrayList 需要重写实现 List接口
     * list集合下都是有序的接口 都是存放顺序 可以允许存放重复数据 set集合不允许
     */


}

四、迭代器(Iterator)

Java Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法,可用于迭代 ArrayList 和 HashSet 等集合

方法说明
it.next()返回迭代器的下一个元素,并且更新迭代器的状态
it.hasNext()用于检测集合中是否还有元素
it.remove()将迭代器返回的元素删除

迭代器本身用于遍历集合,并不存放对象

// 引入 ArrayList 和 Iterator 类
import java.util.ArrayList;
import java.util.Iterator;

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

        // 创建集合
        ArrayList<String> sites = new ArrayList<String>();
        sites.add("Google");
        sites.add("Runoob");
        sites.add("Taobao");
        sites.add("Zhihu");

        // 获取迭代器
        Iterator<String> it = sites.iterator();

        // 输出集合中的第一个元素
        System.out.println(it.next());
    }
}

手写迭代器的hasNext和next方法

在这里插入图片描述

快捷键小知识:
快速生成–> while (iterator.hasNext()) {
Object next = iterator.next();
}
只需要输入itit 然后再输入回车即可

package com.gance.xyz.day16;

import java.util.List;

/**
 * @author 杰仔正在努力
 * @create 2022-11-26 16:31
 */
public class IteratorTest {
    private List list;

    /**
     * 有参list集合
     */
    public IteratorTest(List list) {
        this.list = list;
    }

    /**
     * next底层会使用计数器 每次调用.next方法时 计数+1
     * 迭代器 计数器 初始值=0
     */
    private int count = 0;
    public Object next() {
        if (list == null) {
            throw new IteratorTestException("list is null");
        }
        if (count >= list.size()) {
            //说明集合中 没有继续可以获取到元素
            //访问下标越界 抛出异常 我们就可以自定义异常
        throw new IteratorTestException("无法继续获取元素");
        }
        return list.get(count++);
    }

    public boolean hashNext() {
        //hashNext 判断集合中 是否话可以继续获取元素 如果能够获取到元素则返回true
//        if (count == list.size()) {
//            //取次数==list.size() 集合个数 相等
//
//        }
        /**
         * 例如 集合中存放了三个元素 list.size()==3
         * 分析场景:
         * 1、第一次调用 next 方法=count=1 count=1!=list.size()(3)=true 可以继续调用.next方法
         * 2、第二次调用 next 方法=count=2 2!=list.size()(3)=true  可以继续调用.next方法
         * 3、第三次调用 next 方法=count=3 3!=list.size() false    不可以继续调用.next方法
         */
        return count != list.size();
    }
}

抛出异常

package com.gance.xyz.day16;

import javax.crypto.interfaces.PBEKey;
import java.security.PublicKey;

/**
 * @author 杰仔正在努力
 * @create 2022-11-26 16:46
 */
public class IteratorTestException extends RuntimeException{
    public IteratorTestException(String errMsg) {
        super(errMsg);
    }
}

4.1 foreach循环的使用

增强for循环(也称for each循环)是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作

增强for循环
基本语法
for(元素类型 元素名(自己取的变量名称):集合名或数组名){
访问元素
}

package com.gance.xyz.day16;

import java.util.ArrayList;

/**
 * @author 杰仔正在努力
 * @create 2022-11-26 19:15
 */
public class Test04 {
    public static void main(String[] args) {
        //增强for循环 for each 遍历 数组和集合
        int[] arrInt = {23, 43, 56, 76};
        for (int i = 0;i<arrInt.length;i++) {
            System.out.println(arrInt[i]);
        }

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

        //增强for循环
        /**
         * for(类型 变量名称:集合或数组名){
         *      使用变量名称 访问集合
         * }
         */

        for (int j:arrInt) {
            System.out.println(j);
        }

        System.out.println("----增强for循环 遍历  String[]-----");

        String[] strs={"apple1","apple2","apple3"};
        for (String str : strs) {
            System.out.println(str);
        }

        System.out.println("----增强for循环 遍历  ArrayList<String>----");
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("apple1");
        arrayList.add("apple2");
        arrayList.add("apple3");
        for (String str : arrayList) {
            System.out.println(str);
        }
    }
}

三种循环遍历

package com.gance.xyz.day16;

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

/**
 * @author 杰仔正在努力
 * @create 2022-11-27 15:12
 * //使用三种不同方式实现循环
 */
public class Test05 {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("张三",22));
        students.add(new Student("李四",22));
        students.add(new Student("赵五",22));
        //方式一 for循环遍历 集合
        for (int i = 0; i < students.size(); i++) {
            Student student = students.get(i);
            System.out.println(student.getName() + student.getAge());
        }

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

        //方式二 迭代器遍历
        Iterator<Student> iterator = students.iterator();
        while (iterator.hasNext()) {
            Student next = iterator.next();
            System.out.println(next.getName() + next.getAge());
        }

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

        //方式三 增强for循环
        for (Student stu : students) {
            System.out.println(stu.getName() + stu.getAge());
        }

    }
}

五、泛型

①Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
②泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数

泛型标记符说明
EElement (在集合中使用,因为集合中存放的是元素)
TType(Java 类)
Vvalue (值)
Kkey (键)
Nnumber (数值类型)
?代表不确定的Java类型

1、泛型底层原理就是使用擦除机制
2、泛型是在编译阶段限制传递的类型

package com.gance.xyz.day17;

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

/**
 * @author 杰仔正在努力
 * @create 2022-11-28 11:44
 */
public class Test07 {
    public static void main(String[] args) {
        ArrayList<Object> strs = new ArrayList<>();
        //说明:将一个list集合 泛型赋值给一个没有使用到泛型list集合 直接去除泛型
        List list = strs;
        strs.add("aaple");
        strs.add(1);

    }
}

5.1 泛型类

1、静态方法中不能使用类的泛型
2、泛型的类型必须是类,不能是基本数据类型,需要用到基本数据类型的位置,拿包装类替换
3、如果实例化时,没有指明泛型的类型,默认类型为java.lang.Object类型

//定义的格式
修饰符 class 类名<类型> {}
ege: public class Student<E> {}
public class Gener<T> {
    /**
     * 泛型类格式: public class Gener<T>
     *     private T t;
     *     在类中定义的成员属性 泛型T 类型  与我们在类上定义泛型类类型相同
     */
    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}
package com.gance.xyz.day17;

/**
 * @author 杰仔正在努力
 * @create 2022-11-27 15:44
 */
public class Test01 {
    public static void main(String[] args) {
        /**
         *泛型类
         */
        Gener<String> stringGener = new Gener<>();
        stringGener.setT("gener");
        System.out.println(stringGener.getT());
        Gener<Integer> integerGener = new Gener<>();
        integerGener.setT(10);
        System.out.println(integerGener.getT());
        Gener<Double> doubleGener = new Gener<>();
        doubleGener.setT(10.00);
        System.out.println(doubleGener.getT());
    }
}

5.2 泛型方法

泛型方法使用的规则:
①所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(例如: < E >)。
②每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
③类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
④泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像 int、double、char 等)

//格式
修饰符<类型>返回值类型 方法名(类型 变量名){}
ege:public<T> void show(T t){}
public class GenerMain {
    /**
     * public 修饰符
     * <T>  该方法使用泛型T
     * void 返回
     * 方法的名称
     * 参数列表(T t)
     * @param t
     * @param <T>
     */
    public <T> T show(T t) {
        return t;
    }
}
package com.gance.xyz.day17;

/**
 * @author 杰仔正在努力
 * @create 2022-11-27 16:17
 */
public class Test02 {
    public static void main(String[] args) {

        GenerMain generMain = new GenerMain();

        String string = generMain.show("apple");
        System.out.println(string);
        Integer integer = generMain.show(12);
        System.out.println(integer);
        Double d = generMain.show(12.00);
        System.out.println(d);
    }

}

5.3 泛型接口

//格式:
修饰符 interface 接口名<类型>{}
ege:public interface MayiktInterface  <T>{}
package com.gance.xyz.day17;

/**
 * @author 杰仔正在努力
 * @create 2022-11-27 16:36
 */
public interface GenerInterface<T> {
    T show(T t);
}

package com.gance.xyz.day17;

/**
 * @author 杰仔正在努力
 * @create 2022-11-27 16:37
 */
public class GenerImpl<T> implements GenerInterface<T>{

    @Override
    public T show(T t) {
        return t;
    }
}

package com.gance.xyz.day17;

/**
 * @author 杰仔正在努力
 * @create 2022-11-27 16:42
 */
public class Test03 {
    public static void main(String[] args) {
        GenerImpl<String> stringGener = new GenerImpl<>();
        String string = stringGener.show("23");
        System.out.println(string);
    }
}

5.4 类型通配符

1、类型通配符<?> 一般用于接受使用

2、List<?>:表示元素类型未知的list,它的元素可以匹配任何类型

3、 List<?>只能够用于接收,可以接收所有的泛型类型 但是不能添加
4、可以做get操作,获取object类型

import java.util.*;
 
public class GenericTest {
     
    public static void main(String[] args) {
        List<String> name = new ArrayList<String>();
        List<Integer> age = new ArrayList<Integer>();
        List<Number> number = new ArrayList<Number>();
        
        name.add("icon");
        age.add(18);
        number.add(314);
 
        getData(name);
        getData(age);
        getData(number);
       
   }
 
   public static void getData(List<?> data) {
      System.out.println("data :" + data.get(0));
   }
}

//输出结果
data :icon
data :18
data :314

注意点:
注意点1:编译错误:不能用在泛型方法声明上,返回值类型前面<>不能使用?
public static <?> void test(ArrayList<?> list){ }

注意点2:编译错误:不能用在泛型类的声明上
class GenericTypeClass<?>{ }

注意点3:编译错误:不能用在创建对象上,右边属于创建集合对象 ArrayList<?> list2 = new ArrayList<?>()

上限通配符:<? extends 类型>
上限extends:使用时指定的类型必须是继承某个类,或者实现某个接口,即<=

? extends A:
     List<? extends A> List表示A或者其子类

下限通配符: <? super 类型>
下限super:使用时指定的类型不能小于操作的类,即>=

? super A:
     List<? super A> List表示A或其父类型

5.5 可变参数

可变参数 又称 参数个数可变,用作方法的形参出现,那么方法参数个数就是 可变 的了

//格式:
修饰符 返回值类型 方法名(数据类型... 量名){ }
ege:public static int sum(int... a) { }

注意事项:
1、这里的 可变参数变量 其实是一个数组
2、如果一个方法 有多个参数,包含可变参数,可变参数要放在最后

package com.gance.xyz.day17;

/**
 * @author 杰仔正在努力
 * @create 2022-11-28 11:21
 * 可变参数
 */
public class Test05 {
    public static void main(String[] args) {
        int sum1 = sum(1,1); //arr[0]  = 1
        System.out.println("sum:" + sum1);
        int sum2 = sum(2,2,2);
        System.out.println("sum:" + sum2);
        int sum3 = sum(3,3,3,3);
        System.out.println("sum:" + sum3);
    }

    /**
     * int ... a 可变参数 底层基于 数组实现
     * @param a
     * @return
     */
    public static int sum(int ... a) {
        int sum = 0;
        for (int i = 0; i < a.length; i++) {
            sum += a[i];
        }
        return sum;
    }
}

sum:2
sum:6
sum:12

使用Arrays.asList() 定义好的 元素个数 是不能够发生变化

package com.gance.xyz.day17;

import java.util.Arrays;
import java.util.List;

/**
 * @author 杰仔正在努力
 * @create 2022-11-28 11:40
 * 可变参数的使用
 */
public class Test06 {
    public static void main(String[] args) {
        List<String> strings = Arrays.asList("apple", "banana", "water");
        //如果使用 Arrays.asList() 方法 创建的集合是不可以使用添加和删除 可以修改
        strings.set(0,"6666");
        System.out.println(strings);
    }
}

六、LinkedList接口

1、 linkedList底层基于链表实现 增加 删除的效率非常高 查询效率非常低
2、linkedList底层基于双向链表实现
3、定义了Node类型的first和last,用于记录首末元素,默认值都是为null

Node类型源码
源码图

prev变量:记录前一个元素的位置
next变量:记录下一个元素的位置

package com.gance.xyz.day18;

import java.util.Iterator;
import java.util.LinkedList;

/**
 * @author 杰仔正在努力
 * @create 2022-11-30 11:27
 * LinkedList实现
 */
public class Test01 {
    public static void main(String[] args) {
        LinkedList<String> linkedList = new LinkedList<>();
        linkedList.add("apple1");
        linkedList.add("apple2");
        linkedList.add("apple3");
        System.out.println(linkedList.size());
        Iterator<String> iterator1 = linkedList.iterator();
        while (iterator1.hasNext()) {
            System.out.println(iterator1.next());
        }
        linkedList.remove(0);

        System.out.println("删除之后遍历的结果");

        Iterator<String> iterator2 = linkedList.iterator();
        while (iterator2.hasNext())  {
            System.out.println(iterator2.next());
        }
        
        linkedList.get(0);
    }
}

七、set接口

1、存储无序的、不可重复的数据
2、Set接口是Collection的子接口,set接口没有提供额外的方法
3、 Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals() 方法

7.1 HashSet

特点:
1、HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合
2、HashSet 允许有 null 值
3、.HashSet 是无序的,即不会记录插入的顺序
4、HashSet 没有Get方法,所以不能使用普通for循环遍历

底层数据结构为:Node[]数组 + 单链表 + 红黑树

元素添加实现过程
在这里插入图片描述

添加元素

package com.gance.xyz.day19;

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

/**
 * @author 杰仔正在努力
 * @create 2022-11-30 17:02
 */
public class Test03 {
    public static void main(String[] args) {
        
        HashSet<String> hashSet = new HashSet<>();
        hashSet.add("apple");
        hashSet.add("banana");
        hashSet.add("pear");
        Iterator<String> iterator = hashSet.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

判断元素是否存在

import java.util.HashSet;

public class RunoobTest {
    public static void main(String[] args) {
    HashSet<String> sites = new HashSet<String>();
        sites.add("Google");
        sites.add("Runoob");
        sites.add("Taobao");
        sites.add("Zhihu");
        sites.add("Runoob");  // 重复的元素不会被添加
        System.out.println(sites.contains("Taobao"));
    }
}

删除元素

import java.util.HashSet;

public class RunoobTest {
    public static void main(String[] args) {
    HashSet<String> sites = new HashSet<String>();
        sites.add("Google");
        sites.add("Runoob");
        sites.add("Taobao");
        sites.add("Zhihu");
        sites.add("Runoob");     // 重复的元素不会被添加
        sites.remove("Taobao");  // 删除元素,删除成功返回 true,否则为 false
        System.out.println(sites);
    }
}

删除所有元素

import java.util.HashSet;

public class RunoobTest {
    public static void main(String[] args) {
    HashSet<String> sites = new HashSet<String>();
        sites.add("Google");
        sites.add("Runoob");
        sites.add("Taobao");
        sites.add("Zhihu");
        sites.add("Runoob");     // 重复的元素不会被添加
        sites.clear();  
        System.out.println(sites);
    }
}

计算大小

import java.util.HashSet;

public class RunoobTest {
    public static void main(String[] args) {
    HashSet<String> sites = new HashSet<String>();
        sites.add("Google");
        sites.add("Runoob");
        sites.add("Taobao");
        sites.add("Zhihu");
        sites.add("Runoob");     // 重复的元素不会被添加
        System.out.println(sites.size());  
    }
}

7.2 LinkedHashSet

1、LinkedHashSet 是 HashSet 的子类
2、LinkedHashSet 不允许集合元素重复
3、LinkedHashMap 有序的

package com.gance.xyz.day20;

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

/**
 * @author 杰仔正在努力
 * @create 2022-12-02 15:19
 */
public class Test04 {
    public static void main(String[] args) {
        /**
         * LinkedHashMap与HashMap集合的区别
         * 唯一区别:
         * LinkedHashMap 有序的 HashMap无序的
         */

        LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
        for (int i = 0;i <= 100; i++) {
            linkedHashMap.put(i+"",i+"");
        }
        //hashMap 无序存放 底层基于散列
        for (Map.Entry<String,String> entry : linkedHashMap.entrySet()) {
            System.out.println(entry.getKey()+ "," + entry.getValue());
        }
    }
}

7.3 TreeSet

1、TreeSet 是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态
2、TreeSet底层使用红黑树结构存储数据

两种排序:
自然排序、定制排序

自然排序:
比较两个对象上是否相同的标准为:compareTo()返回0,不是equals()

定制排序:
比较两个对象上是否相同的标准为:compare()返回0,不是equals()

说明:
1.向TreeSet中添加的数据,要求是相同类的对象。
2.两种排序方式:自然排序(实现Comparable接口 和 定制排序(Comparator)

自然排序

public void test1(){
        TreeSet set = new TreeSet();
        
        set.add(new User("Tom",12));
        set.add(new User("Jerry",32));
        set.add(new User("Jim",2));
        set.add(new User("Mike",65));
        set.add(new User("Jack",33));
        set.add(new User("Jack",56));


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

    }

定制排序

 public void test2(){
        Comparator com = new Comparator() {
            //照年龄从小到大排列
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof User && o2 instanceof User){
                    User u1 = (User)o1;
                    User u2 = (User)o2;
                    return Integer.compare(u1.getAge(),u2.getAge());
                }else{
                    throw new RuntimeException("输入的数据类型不匹配");
                }
            }
        };

        TreeSet set = new TreeSet(com);
        set.add(new User("Tom",12));
        set.add(new User("Jerry",32));
        set.add(new User("Jim",2));
        set.add(new User("Mike",65));
        set.add(new User("Mary",33));
        set.add(new User("Jack",33));
        set.add(new User("Jack",56));


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

八、Map接口

1、Map与Collection并列存在。用于保存具有映射关系的数据:key-value
2、Map 中的 key 和 value 都可以是任何引用类型的数据
3、Map 中的 key 用Set来存放,不允许重复,即同一个 Map 对象所对应的类,须重写hashCode()和equals()方法
4、 key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到唯一的、确定的 value

8.1 HashMap

1、HashMap 是一个散列表,它存储的内容是键值对(key-value)映射
2、HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步
3、HashMap 是无序的,即不会记录插入的顺序
4、HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口

在这里插入图片描述

JDK 1.8之前:
HashMap的内部存储结构其实是数组和链表的结合
JDK 1.8之后:
HashMap的内部存储结构其实是数组+链表+树的结合

何为hash冲突:
根据key(键)即经过一个函数f(key)得到的结果的作为地址去存放当前的key value键值对(这个是hashmap的存值方式),但是却发现算出来的地址上已经被占用了。这就是所谓的hash冲突

HashMap中是如何解决hash冲突?
使用链地址法进行解决


优点
1、 处理冲突简单,无堆积现象。即非同义词决不会发生冲突,因此平均查找长度较短;
2、适合总数经常变化的情况。(因为拉链法中各链表上的结点空间是动态申请的)
3、占空间小。装填因子可取α≥1,且结点较大时,拉链法中增加的指针域可忽略不计
4、删除结点的操作易于实现。只要简单地删去链表上相应的结点即可。


缺点
1、 查询时效率较低。(存储是动态的,查询时跳转需要更多的时间)
2、在key-value可以预知,以及没有后续增改操作时候,开放定址法性能优于链地址法。
3、 不容易序列化

HashMap底层源码常量
在这里插入图片描述

HashMap集合中键值对实现封装:
通过Map接口封装的Entry对象交给子类实现

在这里插入图片描述

数组和链表的特点:
数组:具有随机访问的特点,能达到o(1)的时间复杂度,查询速度很快,但是插入和删除慢,需要移动数组元素的位置
链表:链表插入和删除不需要移动位置,只需改变next指针指向的对象引用,但是链表的时间复杂度为o(n),只能顺着节点往下查找,查询速度慢

方法名称作用
V get(Object key)根据键获取值
Set< K > keySet()获取所有键的集合(返回Set集合)
Collection< V > values()获取所有值的集合(返回Collection集合)
Set<Map.Entry<K,V>> entrySet()获取所有键值对象的集合
default V getOrDefault(Object key,V defaultValue)如果存在相应的key则返回其对应的value,否则返回给定的默认值defaultValue

添加元素

在这里插入图片描述

import java.util.HashMap;

public class RunoobTest {
    public static void main(String[] args) {
        // 创建 HashMap 对象 Sites
        HashMap<Integer, String> Sites = new HashMap<Integer, String>();
        // 添加键值对
        Sites.put(1, "Google");
        Sites.put(2, "Runoob");
        Sites.put(3, "Taobao");
        Sites.put(4, "Zhihu");
        System.out.println(Sites);
    }
}

访问元素
在这里插入图片描述

import java.util.HashMap;

public class RunoobTest {
    public static void main(String[] args) {
        // 创建 HashMap 对象 Sites
        HashMap<Integer, String> Sites = new HashMap<Integer, String>();
        // 添加键值对
        Sites.put(1, "Google");
        Sites.put(2, "Runoob");
        Sites.put(3, "Taobao");
        Sites.put(4, "Zhihu");
        System.out.println(Sites.get(3));
    }
}

删除元素

import java.util.HashMap;

public class RunoobTest {
    public static void main(String[] args) {
        // 创建 HashMap 对象 Sites
        HashMap<Integer, String> Sites = new HashMap<Integer, String>();
        // 添加键值对
        Sites.put(1, "Google");
        Sites.put(2, "Runoob");
        Sites.put(3, "Taobao");
        Sites.put(4, "Zhihu");
        Sites.remove(4);
        System.out.println(Sites);
    }
}

获取所有键的集合

public class Test05 {
    public static void main(String[] args) {
        Map<String, String> hashMap = new HashMap<>();
        hashMap.put("daxue","zhangsan");
        hashMap.put("gaozhong","lisi");
        hashMap.put("chuzhong","wangwu");

		Set<String> strings = hashMap.keySet();
        for (String str : strings) {
            System.out.println(str);
        }
    }
}

获取所有值的集合(返回Collection集合)

public class Test05 {
    public static void main(String[] args) {
        Map<String, String> hashMap = new HashMap<>();
        hashMap.put("daxue","zhangsan");
        hashMap.put("gaozhong","lisi");
        hashMap.put("chuzhong","wangwu");

		Collection<String> values = hashMap.values();
        for (String str : values) {
            System.out.println(str);
        }
    }
}

获取所有键值对象的集合

public class Test05 {
    public static void main(String[] args) {
        Map<String, String> hashMap = new HashMap<>();
        hashMap.put("daxue","zhangsan");
        hashMap.put("gaozhong","lisi");
        hashMap.put("chuzhong","wangwu");

		 Set<Map.Entry<String, String>> entries = hashMap.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            System.out.println(entry);
        }
    }
}

练习:
1.定义学生Student 实体类 成员属性 姓名、年龄;
2.定义HashMap集合存入Student对象,key存放Student对象 value存放手机号码;
3.要求保证key的唯一性,也就是学生对象的成员变量值相同,我们就应该认为是同一个对象

package com.gance.xyz.day20;

import java.util.Objects;

/**
 * @author 杰仔正在努力
 * @create 2022-12-01 20:47
 */
public class Student {
    private String name;
    private Integer age;

    public Student(String name, Integer 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;
        Student student = (Student) o;
        return Objects.equals(name, student.name) && Objects.equals(age, student.age);
    }

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

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

package com.gance.xyz.day20;

import java.util.HashMap;

/**
 * @author 杰仔正在努力
 * @create 2022-12-01 20:47
 */
public class Test01 {
    public static void main(String[] args) {
        HashMap<Student, String> hashMap = new HashMap<>();
        /**
         * key:存放学生对象
         * value:学生对应的手机号码
         */
        hashMap.put(new Student("zhangsan",23),"1234567");
        hashMap.put(new Student("zhangsan",23),"1234568");
        hashMap.put(new Student("zhangsan1",23),"1234568");
        for (Student student : hashMap.keySet()) {
            String phone = hashMap.get(student);
            System.out.println("key:" + student.toString()+"value:"+phone);
        }
        System.out.println(hashMap.size());
    }
}

8.2 LinkedHashMap

1、LinkedHashMap 是 HashMap 的子类
2、在HashMap存储结构的基础上,使用了一对双向链表来记录添加元素的顺序
3、与LinkedHashSet类似,LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key-Value 对的插入顺序一致

package com.gance.xyz.day20;

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

/**
 * @author 杰仔正在努力
 * @create 2022-12-02 15:19
 */
public class Test04 {
    public static void main(String[] args) {
        /**
         * LinkedHashMap与HashMap集合的区别
         * 唯一区别:
         * LinkedHashMap 有序的 HashMap无序的
         */

        LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
        for (int i = 0;i <= 100; i++) {
            linkedHashMap.put(i+"",i+"");
        }
        //hashMap 无序存放 底层基于散列
        for (Map.Entry<String,String> entry : linkedHashMap.entrySet()) {
            System.out.println(entry.getKey()+ "," + entry.getValue());
        }
    }
}

8.3 TreeMap

1、TreeMap存储 Key-Value 对时,需要根据 key-value 对进行排序,可以保证所有的 Key-Value 对处于有序状态
2、TreeSet底层使用红黑树结构存储数据
3、TreeMap判断两个key相等的标准:两个key通过compareTo()方法或者compare()方法返回0

九、Collections工具类

作用:操作Collection和Map的工具类

Collection与Collections的区别:
Collections是单列集合操作的工具类 ,Collection 单列集合

方法作用
reverse(List)反转 List 中元素的顺序
shuffle(List)对 List 集合元素进行随机排序
sort(List)根据元素的自然顺序对指定 List 集合元素按升序排序
sort(List,Comparator)根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
swap(List,int, int)将指定 list 集合中的 i 处元素和 j 处元素进行交换
Object max(Collection)根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection,Comparator)根据 Comparator 指定的顺序,返回给定集合中的最大元素
Object min(Collection)根据元素的自然顺序,返回给定集合中的最小元素
Object min(Collection,Comparator)根据 Comparator 指定的顺序,返回给定集合中的最小元素
int frequency(Collection,Object)返回指定集合中指定元素的出现次数
void copy(List dest,List src)将src中的内容复制到dest中
boolean replaceAll(List list,Object oldVal,Object newVal)使用新值替换List 对象的所有旧值
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值