Java学习之集合

集合框架的概述

集合、数组都是对多个数据进行存储操作的结构,简称Java容器。

数组的特点:
一旦初始化后,其长度就确定了。
数组一旦定义好,其元素类型也就确定了。
数组存储的特点:有序、可重复。对于无序、不可重复的需求,不能满足。

Java集合可分为Collection 和 Map 两种体系

  1. Collection接口:单列数据,定义了存取一组对象的方法的集合
    - List: 元素有序、可重复的集合 (动态数组) -->实现类:ArrayList、LinkedList、Vector
    - Set: 元素无序、不可重复的集合 -->实现类:HashSet、LinkedHashSet、TreeSet
  2. Map 接口: 双列数据,保存具有映射关系"key-value" 的集合 -->实现类:HashMap、LinkedHashMap、TreeMap、Hashtable、Properies

Collection接口

方法:


import org.junit.Test;
import java.util.*;
import java.util.List;

/**
 * @Author: mei_ming
 * @DateTime: 2022/6/25 17:45
 */
public class CollectionTest {
    @Test
    public void test01(){
        //1.add() : 添加元素
        Collection coll  = new ArrayList();
        coll.add(13); //自动装箱
        coll.add("str");
        coll.add(13.5f);
        coll.add(new Date());

        //2.size() : 获取添加的元素个数
        System.out.println(coll.size());  //4

        Collection coll2  = new ArrayList();
        coll2.add(13);
        coll2.add("str");
        coll2.add(13.5f);
        coll.addAll(coll2);

        System.out.println(coll.size());  //7
        System.out.println(coll); //[13, str, 13.5, Sat Jun 25 17:52:17 CST 2022, 13, str, 13.5]

        //3.clear() : 清空集合元素
        coll.clear();

        System.out.println(coll);  //[]

        //4.isEmpty() : 判空
        System.out.println(coll.isEmpty());  //true
    }

    @Test
    public void test02(){
        Collection coll  = new ArrayList();
        coll.add(13); //自动装箱
        coll.add("str");
        coll.add(13.5f);
        coll.add(new String("abc"));
        coll.add(new Person("Tom",12));

        //1. contains() : 判断当前集合中是否包含obj
        //在判断时会调用obj对应对象类的equals()
        System.out.println(coll.contains(13));  //true
        System.out.println(coll.contains(new String("abc")));  //true   因为String中重写了equals方法
        System.out.println(coll.contains(new Person("Tom",12)));  //false

        // 2. containsAll(coll2) :  判断当前集合是否包含coll2的数据
        Collection coll2  = new ArrayList();
        coll2.add(13);
        coll2.add("str");
        System.out.println(coll.containsAll(coll2));  //true

        // 3. remove(obj):删除一个相等的对象
        System.out.println(coll.remove(13));  //true
        System.out.println(coll);  //[str, 13.5, abc, Person{age=12, name='Tom'}]

        // 4. removeAll(coll2):删除一个相等的集合,求差集
        Collection coll3  = new ArrayList();
        coll3.add(13.5f);
        coll3.add("str2");
        coll.removeAll(coll3);
        System.out.println(coll);  //[str, abc, Person{age=12, name='Tom'}]
    }

    @Test
    public void test03(){
        Collection coll  = new ArrayList();
        coll.add(123); //自动装箱
        coll.add(456);
        coll.add(13.5f);
        coll.add(new String("abc"));
        coll.add(new Person("Tom",12));

        //1. retainAll() : 取交集
        Collection coll2  = new ArrayList();
        coll2.add(123);
        coll2.add("str");
        coll.retainAll(coll2);
        System.out.println(coll);  // [123]

        //2. equals(obj)

        //3. hashCode()
        System.out.println(coll.hashCode());  //154

        //4. 集合 --> 数组  toArray()
        Object[] objects = coll.toArray();
        for (int i = 0; i < objects.length; i++) {
            System.out.println(objects[i]);  //154 123
        }

        //  数组--> 集合
        List<String> strings = Arrays.asList(new String[]{"AA", "BB", "CC"});
        System.out.println(strings);  //[AA, BB, CC]

        List<int[]> ints = Arrays.asList(new int[]{123, 456});
        System.out.println(ints);  //  [[I@32a1bec0]
        List<Integer> integers = Arrays.asList(new Integer[]{123, 456});
        System.out.println(integers);  //  [123, 456]
    }
}

迭代器的使用


import org.junit.Test;

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

/**
 * @Author: mei_ming
 * @DateTime: 2022/6/25 19:18
 */
public class IteratorTest {
    @Test
    public void test01() {
        Collection coll = new ArrayList();
        coll.add(123); //自动装箱
        coll.add(456);
        coll.add(13.5f);
        coll.add(new String("abc"));
        coll.add(new Person("Tom", 12));

        // 使用迭代器遍历集合中的元素
        Iterator iterator = coll.iterator();

        while(iterator.hasNext()){   //判断是否还有下一个元素
            //next() 1. 指针下移  2. 将下移后的位置上的元素返回
            System.out.println(iterator.next());
        }
        System.out.println(coll);  //[123, 456, 13.5, abc, Person{age=12, name='Tom'}]

        iterator = coll.iterator();

        //remove() : 删除元素
        while(iterator.hasNext()){
        	//iterator.remove();  //在还未调用next()或调用next()后已经调用了remove方法,再次调用会报IllegalStateException
            Object obj = iterator.next();
            if("abc".equals(obj)){
                iterator.remove();
            }
        }
        System.out.println(coll);  //[123, 456, 13.5, Person{age=12, name='Tom'}]
    }

    //错误示范 1
    @Test
    public void test02() {
        Collection coll = new ArrayList();
        coll.add(123); //自动装箱
        coll.add(456);
        coll.add(13.5f);
        coll.add(new String("abc"));
        coll.add(new Person("Tom", 12));

        // 使用迭代器遍历集合中的元素
        Iterator iterator = coll.iterator();

        while((iterator.next())!=null){
            System.out.println(iterator.next());
        }

        //结果: 456 abc java.util.NoSuchElementException
    }

    //错误示范 2
    @Test
    public void test03() {
        Collection coll = new ArrayList();
        coll.add(123); //自动装箱
        coll.add(456);
        coll.add(13.5f);
        coll.add(new String("abc"));
        coll.add(new Person("Tom", 12));

        // 使用迭代器遍历集合中的元素
        Iterator iterator = coll.iterator();

        while(coll.iterator().hasNext()){
            System.out.println(coll.iterator().next());
        }

        // 因为集合对象每次调用iterator方法都会得到一个全新的迭代器对象,默认游标都在第一个元素之前
        //结果: 循环输出第一个元素
    }
}

foreach 增强for


import org.junit.Test;

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

/**
 * @Author: mei_ming
 * @DateTime: 2022/6/25 19:48
 */
public class ForTest {
    @Test
    public void test01() {
        Collection coll = new ArrayList();
        coll.add(123); //自动装箱
        coll.add(456);
        coll.add(13.5f);
        coll.add(new String("abc"));
        coll.add(new Person("Tom", 12));

        //for(集合元素的类型 局部变量 : 集合对象)
        //内部仍然是迭代器
        for(Object obj:coll){
            System.out.println(obj);
        }
    }

    @Test
    public void test02() {
        int[] arr = new int[]{1,2,3,4,5,6};

        //for(集合元素的类型 局部变量 : 集合对象)
        for(int i:arr){
            System.out.println(i);
        }
    }

    @Test
    public void test03() {
        String[] arr = new String[]{"MM","MM","MM"};

        //方式1: 普通for
        for (int i = 0; i <arr.length ; i++) {
            arr[i]="GG";
        }

        for(int i=0;i<arr.length;i++){
            System.out.println(arr[i]);  //GG GG GG
        }

        arr = new String[]{"MM","MM","MM"};

        //方式2:增强for
        for(String str:arr){
            str="GG";
        }

        for(int i=0;i<arr.length;i++){
            System.out.println(arr[i]);  //MM MM MM
        }

    }
}

List接口

import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
 *   Collection 接口: 单列集合,用来存储一个一个得对象
 *      List接口:存储有序的可重复的数据, -->"动态"数组,替换原有的数组
 *          ArrayList:作为List接口的主要实现类,线程不安全,效率高,底层使用Object[] elementData存储
 *          LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高,底层使用双向链表存储
 *          Vector:作为List接口的古老实现类; 线程安全的,效率低,底层使用Object[] elementData存储
 *
 *
 *   1.ArrayList的源码分析:
 *     1.1 jdk1.7情况下:
 *      ArrayList list  = new ArrayList();  //底层创建了长度为10的Object[]数组
 *      list.add(123);  //elementData[0]=new Integer(123);
 *      ...
 *      list.add(456);  //如果此次的添加导致底层elementData数组容量不够,则扩容,
 *                      //默认情况下,扩容为原来的1.5倍,并同时将原有数据拷贝到新的数组中
 *     1.2  jdk1.8中ArrayList的变化:
 *       ArrayList list = new ArrayList();  //底层Object[] elementData初始化为{},并没有创建长度为10的数组
 *
 *       list.add(123);  //第一次调用add()时,底层才创建了长度为10的数组,并将数据123添加到elementData中
 *       ...
 *       后续的添加和扩容操作与jdk1.7 无异
 *     1.3 小结: jdk1.7中的ArrayList的对象的创建类似于单例的饿汉式
 *                  1.8中的ArrayList的对象的创建类似于单例的懒汉式
 *
 *   2. LinkedList的源码分析:
 *     LinkedList list = new LinkedList();  //内部声明了Node类型的first和last属性,默认值为空
 *     list.add(123); //将123 封装到Node中, 创建了Node对象
 *
 *     其中,Node定义为:  体现了LinkedList的双向链表
 *     private static class Node<E> {
 *         E item;
 *         Node<E> next;
 *         Node<E> prev;
 *
 *         Node(Node<E> prev, E element, Node<E> next) {
 *             this.item = element;
 *             this.next = next;
 *             this.prev = prev;
 *         }
 *     }
 *
 *   3. Vector 的源码分析
 *     jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组,
 *     在扩容方面,默认为原来的2倍
 *
 */

/**
 * @Author: mei_ming
 * @DateTime: 2022/8/8 21:59
 */
public class ListTest {

    //1. ArrayList 中常用方法:
    /**
     * 增: add(int index,Object ele)
     * 删: remove(int index) |  remove(Object obj)
     * 改: set(int index,Object ele)
     * 查: get(int index)
     * 长度: size()
     * 遍历:  1.Iterator:迭代器
     *       		2.foreach
     *       		3.for
     */
    @Test
    public void test1(){
        ArrayList list = new ArrayList();
        list.add(123);
        list.add(456);
        list.add("AA");
        list.add(456);
        list.add(new Person("Tom",12));
        System.out.println(list);  //[123, 456, AA, 456, Person{age=12, name='Tom'}]

        //1. void add(int index,Object ele): 在index处插入ele
        list.add(1,"BB");
        System.out.println(list); //[123, BB, 456, AA, 456, Person{age=12, name='Tom'}]

        //2. boolean addAll(int index,Collection eles) :从index位置插入eles中的所有元素
        List list1 = Arrays.asList(1, 2, 3, 4);
        list.addAll(1,list1);
        System.out.println(list);  //[123, 1, 2, 3, 4, BB, 456, AA, 456, Person{age=12, name='Tom'}]

        //3. Object get(int index) : 获取指定index位置的元素
        System.out.println(list.get(0)); //123

        //4. int indexOf(Object obj): 获取obj在集合中首次出现的位置
        int index = list.indexOf(456);
        System.out.println(index);  // 6


        //5. int lastIndexOf(Object obj) : 返回obj在当前集合中末次出现的位置
        int index1 = list.lastIndexOf(456);
        System.out.println(index1);  // 8

        //6. Object remove(int index) : 指定移除index位置的元素,并返回此元素
        Object remove = list.remove(0);
        System.out.println(remove);  //123

        //7. Object set(int index,Object ele) : 设置指定位置的元素为ele
        list.set(0,789);
        System.out.println(list); // [789, 2, 3, 4, BB, 456, AA, 456, Person{age=12, name='Tom'}]

        //8. List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合
        List subList = list.subList(5, 8);
        System.out.println(subList);  //[456, AA, 456]
    }

	// 遍历List
    @Test
    public void test2(){
        ArrayList list = new ArrayList();
        list.add(123);
        list.add(456);
        list.add("AA");

        //1. 迭代器
        Iterator iterator = list.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
        //2. foreach
        for (Object obj: list) {
            System.out.println(obj);
        }
        //3. for
        for (int i = 0; i <list.size() ; i++) {
            System.out.println(list.get(i));
        }
    }
    
	//    区分list中remove(int index) 和 remove(Object obj)
    @Test
    public void test3(){
         List list = new ArrayList();
         list.add(1);
         list.add(2);
         list.add(3);
         updatelist(list);
        System.out.println(list); //  默认按照索引删 结果为 [1, 2]
    }
    private void updatelist(List list) {
        list.remove(2);  //[1, 2]
//        list.remove(new Integer(2)); //[1, 3]  
    }
}

Set接口

/**
 * Set接口的框架:
 *      Collection接口:单列集合, 用来存储一个一个的对象
 *         Set接口:存储无序的,不可重复的数据,
 *             HashSet: 作为Set接口的主要实现类,线程不安全的,可以存储null值
 *                 LinkedHashSet: 作为HashSet的子类: 遍历器内部数据时,可以按照添加的顺序遍历
 *                                 对于频繁的遍历操作,LinkedHashSet效率高于HashSet
 *             TreeSet: 可以按照添加对象的指定属性,进行排序
 *
 * 1. Set接口中没有额外定义的新方法,使用的都是Collection中声明过的方法。
 * 2.  要求:向Set中添加的数据,其所在的类一定要重写hashCode()和equals()
 */

import org.junit.Test;
import java.util.*;

/**
 * @Author: mei_ming
 * @DateTime: 2022/8/14 14:41
 */
public class SetTest {
    /**
     * 一、Set的无序性和不可重复性:
     * 1. 无序性: 不等于随机性,存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的HashCode来分配空间
     * 2. 不可重复性: 保证添加的元素按照equals()判断时,不能返回true,即:相同的元素只能添加一个
     *
     * 二、 添加元素的过程:以HashSet为例:
     *     向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,
     *     此哈希值接着通过某种算法计算出HashSet底层数组中的存放位置(即为:索引位置),判断
     *     数组此位置上是否已经有元素:
     *          如果此位置上没有其他元素,则 元素a添加成功。 --> 情况1
     *          如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
     *              如果hash值不相同,则元素a添加成功。  --> 情况2
     *              如果hash值相同,进而需要调用元素a所在类的equals()方法
     *                  equals()返回true,元素a添加失败
     *                  equals()返回false,则元素a添加成功 -->情况3
     *
     *     对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上的数据以链表的方式存储:
     *     jdk1.7: 元素a 存放在数组中 指向原来的元素
     *     jdk1.8: 原来的元素存放在数组中,指向元素a
     *
     *
     *     HashSet底层: 数组+链表
     */
    /**
     * 以IDEA为例,在自定义类中可以调用工具自动重写equals和hashCode,
     * 问题:为什么复写hashCode方法,有31这个数字,
     * 1.选择系数的时候,要选择尽量大的系数,因为如果计算出来的hash地址越大,所谓的“冲突”就越少,查找起来效率也会提高
     * 2. 并且31 只占用5bits,相乘造成溢出的概率小。
     * 3. 31 可以通过位运算优化, i*31==(i<<5)-1
     * 4. 31 是一个素数
     */
    /**
     * 重写hashCode()方法的基本原则:
     * 1. 在程序运行是,同一个对象多次调用hashCode()方法应该返回相同的值。
     * 2. 当两个对象的equals()方法比较返回true时,这两个对象的hashCode()方法的返回值也应相等
     * 3. 对象中用作equals()方法比较的Field,都应该用来计算hashCode值
     */

    @Test
    public  void test1(){
        Set set = new HashSet();
        set.add(123);
        set.add(456);
        set.add("AA");
        set.add(new Person("Tom",24));
        set.add(new Person("Tom",24));

        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
        /**
         * 打印结果:
         * AA
         * Person{age=24, name='Tom'}
         * Person{age=24, name='Tom'}  //因为Person没有重写equals和hashCode方法
         * 456
         * 123
         */
    }

    /**
     *  LinkedHashSet的作用
     *  LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个shuju
     *  和后一个数据。
     *  优点: 对于频繁的遍历操作,LinkedHashSet效率高于HashSet
     */
    @Test
    public  void test2(){
        Set set = new LinkedHashSet();
        set.add(123);
        set.add(456);
        set.add("AA");
        set.add(new Person("Tom",24));
        set.add(new Person("Tom",24));

        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
        /**
         * 打印结果:
         * 123
         * 456
         * AA
         * Person{age=24, name='Tom'}
         * Person{age=24, name='Tom'}
         */
    }

    /**
     * TreeSet:
     * 1.向TreeSet中添加的数据,要求是相同类的对象
     * 2.两种排序方式 自然排序(实现Comparable接口) 和 定制排序(Comparator)
     *
     * 3.自然排序中,比较两个对象是否相同的标准为CompareTo()返回0,不再是equals()
     * 4.定制排序中,比较两个对象是否相同的标准为Compare()返回0,不再是equals()
     */
    @Test
    public void test3(){
        Set set = new TreeSet();
        set.add(123);
        set.add(456);
        set.add(-11);
        set.add(22);

        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
        /**
         * 打印结果:
         * -11
         * 22
         * 123
         * 456
         */
        Set set1 = new TreeSet();
        set1.add(new Person("Tom",12));
        set1.add(new Person("Jack",14));
        set1.add(new Person("Mike",8));
        set1.add(new Person("Mike",16));

        Iterator iterator1 = set1.iterator();
        while(iterator1.hasNext()){
            System.out.println(iterator1.next());
        }
        /**
         * 打印结果:  没加 二级排序之前 , 把 {Mike,8} 和{Mike,16}看成是一个对象
         * Person{age=12, name='Tom'}
         * Person{age=8, name='Mike'}
         * Person{age=14, name='Jack'}
         *
         * 加 二级排序之后 , 把 {Mike,8} 和 {Mike,16}看成是两个对象
         * Person{age=12, name='Tom'}
         * Person{age=8, name='Mike'}
         * Person{age=16, name='Mike'}
         * Person{age=14, name='Jack'}
         */
    }
    @Test
    public void test4() {
        Comparator com = new Comparator() {
            //按照年龄从小到大排序
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof Person && o2 instanceof Person){
                    Person person1 = (Person) o1;
                    Person person2 = (Person) o2;
                    return Integer.compare(person1.getAge(),person2.getAge());
                }else{
                    throw new RuntimeException("数据类型不匹配");
                }
            }
        };

        Set set1 = new TreeSet(com);
        set1.add(new Person("Tom",12));
        set1.add(new Person("Jack",14));
        set1.add(new Person("Mike",8));
        set1.add(new Person("Mike",16));

        Iterator iterator1 = set1.iterator();
        while(iterator1.hasNext()){
            System.out.println(iterator1.next());
        }
        /**
         * 打印结果:
         * Person{age=8, name='Mike'}
         * Person{age=12, name='Tom'}
         * Person{age=14, name='Jack'}
         * Person{age=16, name='Mike'}
         */
    }

// 面试题 
    @Test
    public void test5(){
        HashSet set = new HashSet();
        //User 已重写 hashCode、equals
        User u1 = new User(1001,"AA");
        User u2 = new User(1002,"BB");

        set.add(u1);
        set.add(u2);
        System.out.println(set);  //2个   [User{id=1002, name='BB'}, User{id=1001, name='AA'}]

        u1.name= "CC";
        set.remove(u1);      // remove 先拿name id 进行hashcode判断, 结果 没有命中 remove的位置是空的
        System.out.println(set);  //2个   [User{id=1002, name='BB'}, User{id=1001, name='CC'}]

        set.add(new User(1001,"CC")); // add 拿 cc 与 1001 进行hashcode  之前的 cc地址是根据aa生成的 所以 add 成功
        System.out.println(set);  //3个   [User{id=1002, name='BB'}, User{id=1001, name='CC'}, User{id=1001, name='CC'}]

        set.add(new User(1001,"AA"));  // 这个hashcode 与之前 1001 aa 的相同, 进行equals判断 cc与aa 不同, add 成功
        System.out.println(set);  //4个   [User{id=1002, name='BB'}, User{id=1001, name='CC'}, User{id=1001, name='CC'}, User{id=1001, name='AA'}]

    }
}

Person

/**
 * @Author: mei_ming
 * @DateTime: 2022/6/25 17:55
 * @Description: TODO
 */
public class Person implements Comparable{
 int age;
 String name;

    public Person() {
    }

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

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

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    //按照姓名从大到小排序,年龄从小到大
    @Override
    public int compareTo(Object o) {
        if (o instanceof Person){
            Person person = (Person) o;
            return -this.name.compareTo(person.name);
//            int compare = -this.name.compareTo(person.name);
//            if(compare!=0){
//                return compare;
//            }else{
//                return Integer.compare(this.age,person.age);
//            }
        }else{
            throw new RuntimeException("输出的类型不匹配");
        }
    }
}

User

/**
 * @Author: mei_ming
 * @DateTime: 2022/6/25 17:55
 * @Description: TODO
 */
public class User{
    int id;
    String name;

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public User() {
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User user = (User) o;

        if (id != user.id) return false;
        return name != null ? name.equals(user.name) : user.name == null;
    }

    @Override
    public int hashCode() {
        int result = id;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        return result;
    }

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

Map接口

import org.junit.Test;
import java.util.*;
/**
 * /---Map:双列数据,存储key-value对的数据
 *       /---HashMap:作为Map的主要实现类,线程不安全的,效率高,存储null的key和value
 *          /---LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。
 *              原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。
 *                 对于频繁的遍历操作,此类执行效率高于HashMap
 *       /---TreeMap:保证按照添加的key-value对进行排序,实现排序遍历,此时考虑key的自然排序或定制排序
 *              底层使用红黑树。
 *       /---Hashtable:作为古老的实现类:线程安全的,效率低;不能存储null的key和value
 *          /---Properties:常用来处理配置文件。key和value都是String类型
 *    HashMap的底层:数组+链表 (jdk7之前)
 *                 数组+链表+红黑树 (jdk8)
 *
 *  面试题:
 *  1. HashMap的底层实现原理?
 *  2. HashMap和HashTable的异同?
 *  3. CurrentHashMap 与 Hashtable 的异同?
 *
 * 二、Map的结构的理解:
 *   Map中的key:无序的、不可重复的,使用Set存储所有的Key  --> key所在的类要重写equals和hashCode
 *   Map中的value: 无序的、可重复的,使用Collection存储所有的value -->value所在的类要重写equals()
 *   一个键值对:key-value构成一个Entry对象
 *   Map中entry:无序的、不可重复的,使用Set存储所有的entry
 *
 * 三、HashMap的底层原理: (以jdk7为例)
 *    HashMap map = new HashMap();
 *      在实例化以后,底层创建了长度为16的以为数组Entry[] table.
 *      map.put(key1,value1):
 *      首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法(indexFor方法)计算以后,得到Entry数组中的存放位置。
 *      如果此位置上的数据为空,此时的key1-value1添加成功。----情况1
 *      如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据,
 *      的哈希值:
 *          如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功  ---情况2
 *          如果key1的哈希值和原来的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)
 *             如果equals()返回false:此时key1-value1添加成功。  ---情况3
 *             如果equals()返回true:使用value1替换value2
 *
 *   补充: 关于情况2、3,此时key1-value1和原来的数据以链表的方式存储。
 *
 * 在不断的添加过程中,会涉及到扩容问题,当超出临界值时且要存放的位置非空,则扩容,
 * 默认的扩容方式: 原来的2倍,并将原有数据复制过来
 *
 * jdk8 相较于jdk1.7底层实现方面的不同:
 * 1. new HashMap() : 底层没有创建一个长度为16的数组
 * 2. jdk8底层的数组时Node[],而非Entry[]
 * 3. 首次调用put()方法,底层创建长度为16的数组
 * 4. jdk7底层结构只有:数组+链表。jdk8底层结构:数组+链表+红黑树
 *    当数组的某个索引位置上的元素以链表形式存在的数据》8 且当前数组长度>64时,
 *    此时此索引位置上的所有数据改为红黑树存储
 *
 * 面试题:
 * 谈谈你对HashMap中put/get方法的认识?如果了解再谈谈HashMap的扩容机制?默认大小是多少?
 * 什么是负载因子(填充比)?什么是吞吐临界值(阈值/threshold)? 16*0.75 = 12
 *
 * DEFAULT_INITIAL_CAPACITY: HashMap的默认容量,16
 * MAXIMUM_CAPACITY: HashMap的最大支持容量,2^30
 * DEFAULT_LOAD_FACTOR: HashMap的默认加载因子,0.75f
 * TREEIFY_THRESHOLD: Bucket中链表长度大于该默认值,转化为红黑树,8
 * UNTREEIFY_THRESHOLD: Bucket中红黑树存储的Node小于该默认值,转化为链表,6
 * MIN_TREEIFY_CAPACITY: 桶中的Node被树化时的hash表容量。(当桶中Node的数量大到需要变红黑树
 * 时,若hash表容量小于MIN_TREEIFY_CAPACITY时,此时应执行resize扩容操作,这个MIN_TREEIFY_CAPACITY的值
 * 至少是TREEIFY_THRESHOLD的4倍),64
 *
 * table: 存储元素的数组,总是2的n次幂
 * entrySet: 存储具体元素的集
 * size: HashMap中存储的键值对的数量,
 * modCount: HashMap扩容和结构改变的次数
 * threshold: 扩容的临界值,= 容量*填充因子
 * loadFactor: 填充因子
 *
 *
 * 四、LinkedHashMap的底层实现原理(了解)
 *
 *  static class Entry<K,V> extends HashMap.Node<K,V> {
 *         Entry<K,V> before, after;
 *         Entry(int hash, K key, V value, Node<K,V> next) {
 *             super(hash, key, value, next);
 *         }
 *     }
 * 五、Map中定义的方法:
 *  添加、删除、修改操作:
 *      Object put(Object key,Object value): 将指定key-value 添加到(修改)当前map中
 *      void putAll(Map m): 将m中的所有key-value对存放到当前的map中
 *      Object remove(Object key): 移除指定的key的key-value对,并返回value
 *      void clear(): 清空当前map中的所有数据
 *  元素查询的操作:
 *      Object get(Object key): 获取指定key对应的value
 *      boolean containsKey(Object key): 是否包含指定的key
 *      boolean containsValue(Object value): 是否包含指定的value
 *      int size(): 返回map中key-value 对的个数
 *      boolean isEmpty(): 判断当前map是否为空
 *      boolean equals(Object obj): 判断当前map和参数对象obj是否相等
 *  元视图操作的方法:
 *      Set keySet(): 返回所有key构成的Set集合
 *      Collection values(): 返回所有value构成的Collection集合
 *      Set entrySet(): 返回所有key-value对构成的Set集合
 *
 *
 * 总结:常用方法
 *  添加:put
 *  删除:remove
 *  修改:put
 *  查询:get
 *  长度:size
 *  遍历:keySet()/values()/entrySet()
 *
 * 六、TreeMap
 *  向TreeMap中添加key-value,要求key必须是由一个类创建的对象
 *  因为要按照key进行排序: 自然排序、定制排序
 */

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/18 21:22
 */
public class MapTest {
 
    @Test
    public void test(){
        Map map = new HashMap();
        map.put(null,null);  //正常

        Map map1 = new Hashtable();
        map1.put(null,null);   //编译出错  java.lang.NullPointerException
    }
	
	@Test
    public void test2(){
        Map map = new HashMap();
        map.put("A",1);
        map.put("BDX",2);
        map.put("C",3);
        System.out.println(map);  //{A=1, C=3, BDX=2}

        Map map1 = new LinkedHashMap();
        map1.put("A",1);
        map1.put("BDX",2);
        map1.put("C",3);
        System.out.println(map1);  //{A=1, BDX=2, C=3}
    }

	@Test
    public void test3(){
        Map map = new HashMap();
        //添加
        map.put("AA",22);
        map.put(33,"BB");
        map.put("44",55);
        //修改
        map.put("AA",78);
        System.out.println(map); // {AA=78, 44=55, 33=BB}

        Map map1 = new HashMap();
        map1.put(44,"BB");
        map1.put("99",55);
        map.putAll(map1);
        System.out.println(map); // {AA=78, 44=55, 99=55, 33=BB, 44=BB}

        //remove(Object key)
        Object value = map.remove("99");
        System.out.println(value);  // 55
        System.out.println(map);  // {AA=78, 44=55, 33=BB, 44=BB}

        //clear()
        map.clear();
        System.out.println(map.size());// 0

    }
    
	@Test
    public void test4(){
        Map map = new HashMap();
        //添加
        map.put("AA",22);
        map.put(33,"BB");
        map.put("44",55);
        
        //Object get(Object key)
        System.out.println(map.get(33));  // BB
        
        //boolean containsKey(Object key)
        boolean b = map.containsKey("55");
        System.out.println(b);  //false
        boolean b1 = map.containsValue(22);
        System.out.println(b1);  //true
    }
    
    //遍历操作
    @Test
    public void test5(){
        Map map = new HashMap();
        //添加
        map.put("AA",22);
        map.put(33,"BB");
        map.put("44",55);

        //遍历所有key :keySet()
        Set set = map.keySet();
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
        System.out.println("--------");

        //遍历所有value:  values()
        Collection values = map.values();
        for (Object obj:values) {
            System.out.println(obj);
        }
        System.out.println("--------");

        //遍历所有key-value对
        Set entrySet = map.entrySet();
        Iterator iterator1 = entrySet.iterator();
        while (iterator1.hasNext()){
            Object obj = iterator1.next();
            Map.Entry entry = (Map.Entry)obj;
            System.out.println(entry.getKey()+" "+entry.getValue());
        }
    }

	//自然排序
    @Test
    public void test6(){
        TreeMap map = new TreeMap();
        Person p1 = new Person("Tom",23);
        Person p2 = new Person("Jack",21);
        Person p3 = new Person("Jerry",20);
        Person p4 = new Person("Rose",26);

        map.put(p1,11);
        map.put(p2,22);
        map.put(p3,33);
        map.put(p4,44);

        Set set = map.entrySet();
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            Object o = iterator.next();
            Map.Entry entry = (Map.Entry)o;
            System.out.println(entry.getKey()+" "+entry.getValue());
        }
    }
    
    //定制排序
    @Test
    public void test7(){
        TreeMap map = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof Person && o2 instanceof  Person){
                    Person p1 = (Person)o1;
                    Person p2 = (Person)o2;
                    return Integer.compare(p1.getAge(),p2.getAge());
                }
                throw new RuntimeException("输入的类型不匹配!");
            }
        });
        Person p1 = new Person("Tom",23);
        Person p2 = new Person("Jack",21);
        Person p3 = new Person("Jerry",20);
        Person p4 = new Person("Rose",26);

        map.put(p1,11);
        map.put(p2,22);
        map.put(p3,33);
        map.put(p4,44);

        Set set = map.entrySet();
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            Object o = iterator.next();
            Map.Entry entry = (Map.Entry)o;
            System.out.println(entry.getKey()+" "+entry.getValue());
        }
    }
}

Properties类

Properties是Hashtable的子类,常用来处理配置文件。key和value都是String类型。

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/20 21:40
 * @Description: TODO
 */
public class PropertiesTest {

    public static void main(String[] args) throws IOException {
        Properties pro = new Properties();

        FileInputStream fis = new FileInputStream("jdbc.properties");
        pro.load(fis);

        String name = pro.getProperty("name");
        String password = pro.getProperty("password");

        System.out.println(name + " "+ password);  // ming 123456
    }
}

Collections工具类

import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * Collections: 操作Collection、Map的工具类
 */
public class CollectionsTest {
    /**
     * 排序操作:(均为static)
     *  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)
     *  int frequency(Collection,Object): 返回指定集合中指定元素的出现次数
     *  void copy(List dest,List src): 将src中的内容复制到dest中
     *  boolean replaceAll(List list,Object oldVal,Object newVal): 使用新值替换List对象的所有旧值
     */
    @Test
    public void test(){
        List list = new ArrayList();
        list.add(123);
        list.add(456);
        list.add(321);
        list.add(-99);
        list.add(0);

        System.out.println(list); //[123, 456, 321, -99, 0]
        //Collections.reverse(list); // sout [0, -99, 321, 456, 123]
        //Collections.sort(list);  //sout [-99, 0, 123, 321, 456]
        //Collections.shuffle(list); //sout [-99, 321, 123, 456, 0] sout [123, -99, 0, 456, 321]
        //Collections.swap(list,2,3);  //sout [123, 456, -99, 321, 0]

        list.add(321);
        int frequency = Collections.frequency(list, 321); //2
        System.out.println(frequency);
        System.out.println(list);
    }

    @Test
    public void test2(){
        List list = new ArrayList();
        list.add(123);
        list.add(456);
        list.add(321);
        list.add(-99);
        list.add(0);

        //List dest = new ArrayList();
        //Collections.copy(dest,list);  //报错:java.lang.IndexOutOfBoundsException: Source does not fit in dest
        //因为dest的size=0,list的size=5 不一致
        //System.out.println(dest);

        //正常写法:
        List dest = Arrays.asList(new Object[list.size()]);
        Collections.copy(dest,list);
        System.out.println(dest); // [123, 456, 321, -99, 0]
    }

    @Test
    public void test3(){
        List list = new ArrayList();
        list.add(123);
        list.add(456);
        list.add(321);
        list.add(-99);
        list.add(0);
        List list1 = Collections.synchronizedList(list);
        //list1是线程安全的
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值