集合

一、 集合的引入

【1】数组,集合都是对多个数据进行存储操作的,简称为容器。PS:这里的存储指的是内存层面的存储,而不是持久化存储(.txt,.avi,.jpg,数据库)。
【2】数组:特点:
(1)数组一旦指定了长度,那么长度就被确定了,不可以更改。
int[] arr = new int[6];
(2)数组一旦声明了类型以后,数组中只能存放这个类型的数据。数组中只能存放同一种类型的数据。
int[] arr,String[] s,double[] d…
【3】数组:缺点:
(1)数组一旦指定了长度,那么长度就被确定了,不可以更改。
(2)删除,增加元素 效率低。
(3)数组中实际元素的数量是没有办法获取的,没有提供对应的方法或者属性来获取
(4)数组存储:有序,可重复 ,对于无序的,不可重复的数组不能满足要求。
【4】正因为上面的缺点,引入了一个新的存储数据的结构---->集合
【5】集合一章我们会学习很多集合,为什么要学习这么多集合呢?
因为不同集合底层数据结构不一样。集合不一样,特点也不一样

二、 集合的简要结构图

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

在这里插入图片描述

三、 集合的两大阵营

在这里插入图片描述

四、 集合的第一大阵营

1、Collection接口

1、常用方法

在这里插入图片描述

/**
 * 1.集合、数组都是对多个数据进行存储操作的结构,简称Java容器。
 *  说明:此时的存储,主要指的是内存层面的存储,不涉及持久化的存储(.txt,.jpg,.avi,数据库中)
 *
 * 2.1 数组再存储多个数据方面的特点:
 *      >一旦初始化以后,其长度就确定了。
 *      >数组一旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据了。
 *          比如:String[] arr;int[] arr1;Object[] arr2;
 * 2.2
 *      >一旦初始化以后,其长度就不可修改。
 *      >数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。
 *      >获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用。
 *      >数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。
 *
 *二、集合框架
 *    |----Collection接口:单列集合,用来存储一个一个的对象
 *      |----List接口:存储有序的、可重复的数据。-->"动态"数组
 *         |----ArrayList、LinkedList、Vector
 *
 *      |----Set接口:存储无序的、不可重复的数据-->高中讲的"集合"
 *         |----HashSet、LinkedHashSet、TreeSet
 *
 *    |----Map接口:双列集合,用来存储一对(key - value )一对的数据 -->高中函数:y=f(x)
 *         |====HashMap、LinkedHashMap、TreeMap、Hashtable、Properties
 *
 * 三、Collection接口中的方法的使用
 *      结论:向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals().
 *
 * 四、集合元素的遍历操作,使用迭代器Iterator接口
 *      1.内部的方法:hasNext()和next()
 *      2.集合对象每次调用iterator()方法都得到一个全新的迭代对象,
 *          默认游标都在集合的第一个元素之前。
 *      3.内部定义了remove(),可以在遍历的时候,删除集合中的元素,此方法不同于集合直接调用remove()
 * @author 刘瘦瘦
 * @create 2021-04-19-21:08
 */
public class CollectionTest {

    @Test
    public void test1() {
        Collection coll = new ArrayList();

        //1.add(Object e):将元素e添加到集合coll中
        coll.add("AA");
        coll.add("BB");
        coll.add(123);//自动装箱
        coll.add(new Date());

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

        //3.addAll(Collection coll1):将coll1集合中的元素添加到当前的集合中
        Collection coll1 = new ArrayList();
        coll1.add(456);
        coll1.add("CC");
        coll.addAll(coll1);
        coll.add(coll);

        System.out.println(coll.size());//6
        System.out.println(coll.toString());//[AA, BB, 123, Mon Apr 19 21:40:57 CST 2021, 456, CC, (this Collection)]

        //4.clear():清空集合元素
        coll.clear();//只是把数值的值都赋为了null

        //5.isEmpty():判断当前集合是否为空
        System.out.println(coll.isEmpty());

        //6.contains(Object obj):判断当前集合中是否包含obj
        //contain底层进行遍历比较时调用的是equals(),Person类中没有重写,所以调用的是Object类中的
        //equals(),比较的是两个对象的地址。而String中重写了equals()比较的是字符串的内容,所以下面
        //程序输出true,我们比较时自定义类都要实现equals()方法
        //没重写equals()
//        coll.add(new Person("刘瘦瘦", 23));
//        System.out.println(coll.contains(new Person("刘瘦瘦", 23)));//false
        //重写equals()之后
        coll.add(new Person("刘瘦", 23));
        coll.add(new Person("瘦瘦", 23));
        coll.add(new Person("刘瘦瘦", 23));
        System.out.println(coll.contains(new Person("刘瘦瘦", 23)));//true
        coll.add("BB");
        System.out.println(coll.contains("BB"));//true

        //7.containsAll(Collection coll) 判断形参coll1中的所有元素是否都存在于当前集合中。-->调用equals()
        coll.add("aa");
        coll.add(123);
        coll.add("全世界拼成首诗");
        Collection col = Arrays.asList("aa", 123, "全世界拼成首诗");
        boolean contains = coll.containsAll(col);
        System.out.println(contains);


        //8.remove(Object obj):从当前集合中移除obj 调用equals()方法
        coll.add(false);
        System.out.println(coll);
        coll.remove("aa");
        coll.remove(false);
        System.out.println(coll.remove(new Person("刘瘦瘦", 23)));
        System.out.println(coll);

        //9.removeAll(Collection coll):移除coll中所有的元素
        Collection coll2 = Arrays.asList("BB", 123, "全世界拼成首诗");
        System.out.println(coll.removeAll(coll2));//true
        System.out.println(coll.size());//0

        //10.retainAll(Collection coll1):交集:获取当前集合和coll1集合的交集,并返回给当前集合
        coll.add("ufog");
        coll.add(456);
        System.out.println(coll);
        Collection list1 = Arrays.asList(123, 456, "ufo");
        coll.retainAll(list1);
        System.out.println(coll);

        //11.equals(Object obj):要想返回true,需要当前集合和形参集合的元素相同
        coll.clear();
        List<Object> asList = Arrays.asList("全世界拼成首诗", 520, new String("略略略"), new Person("刘瘦瘦", 12));
        coll.add("全世界拼成首诗");
        coll.add(520);
        coll.add(new String("略略略"));
        coll.add(new Person("刘瘦瘦", 12));
        System.out.println(asList.equals(coll));//true

        //12.hashCode():返回当前对象的哈希值
        System.out.println(coll.hashCode());

        //13.集合--->数组:toArray()
        Object[] objects = coll.toArray();//-1418690718
        System.out.println(Arrays.toString(objects));//-1418690718

        //拓展:数组-->集合:调用Arrays类的静态方法asList()
        List objects1 = Arrays.asList(new Object[]{"liu", "shou", "shou"});
        System.out.println(objects1.toString());
        System.out.println(objects1.size());
        //注意
        List ints = Arrays.asList(new int[]{123, 456});
        List integers = Arrays.asList(new Integer[]{123, 456});
        System.out.println(ints);//[[I@573fd745]
        System.out.println(ints.size());//1
        System.out.println(integers);//[123, 456]
        System.out.println(integers.size());//2

        System.out.println("---------------------------");
        //14.iterator():返回Iterator接口的实例,用于遍历集合元素。--->见IteratorTest.java
        //15.jdk5.0 新增了foreach循环,用于遍历数组、集合。--->见foreachTest.java

    }
}
2、集合的遍历

1、迭代器
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
迭代器内容可以查看底层源码。

public class IteratorTest {

    @Test
    public void test1(){
        Collection coll=new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(new Person("刘瘦瘦",23));
        coll.add("我行四方,以日以年");
        coll.add(false);

        Iterator iterator=coll.iterator();
        //方式一
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
        //java.util.NoSuchElementException
//        System.out.println(iterator.next());

        //方式二:不推荐
//        for (int i = 0; i < coll.size(); i++) {
//            System.out.println(iterator.next());
//        }

        //方式三:推荐
        //hasNext():判断是否还有下一个元素
        while (iterator.hasNext()){
            //next():①指针下移 ②将下移以后集合位置上的元素返回
            System.out.println(iterator.next());
        }
    }

    @Test
    public void test2(){

        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(new Person("Jerry",20));
        coll.add(new String("Tom"));
        coll.add(false);

        //错误方式一:
//        Iterator iterator = coll.iterator();
//        while((iterator.next()) != null){
//            System.out.println(iterator.next());
//        }

        //错误方式二:
        //集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。
        while (coll.iterator().hasNext()){
            System.out.println(coll.iterator().next());
        }


    }

    //测试Iterator中的remove()
    //如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,
    // 再调用remove都会报IllegalStateException。·
    @Test
    public void test3(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(new Person("Jerry",20));
        coll.add(new String("Tom"));
        coll.add(false);

        //删除集合中"Tom"
        Iterator iterator = coll.iterator();
        while (iterator.hasNext()){
//            iterator.remove();
            Object obj = iterator.next();
            if("Tom".equals(obj)){
                iterator.remove();
//                iterator.remove();
            }

        }
        //遍历集合
        iterator = coll.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

2、增强for循环

/**
 * @author 刘瘦瘦
 * @create 2021-04-20-10:11
 */
public class ForEachTest {
    @Test
    public void test1(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(new Person("Jerry",20));
        coll.add(new String("Tom"));
        coll.add(false);

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

    @Test
    public void test2(){
        int arr[]=new int[]{1,2,3,4,5,6};
        //for(数组元素的类型 局部变量 :数组对象)
        for (int i :
                arr) {
            System.out.println(i);
        }
    }

    //面试题
    @Test
    public void test3(){

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

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

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

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

2、Collection接口的子接口List接口

在这里插入图片描述

/**
 * 1.List接口框架
 *
 *    |----Collection接口:单列集合,用来存储一个一个的对象
 *          |----List接口:存储有序的、可重复的数据。  -->“动态”数组,替换原有的数组
 *              |----ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
 *              |----LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
 *              |----Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储
 *
 *  2.ArrayList的源码分析
 *      2.1 jdk7情况下
 *          ArrayList list=new ArrayList();//底层创建了长度是10的Object数组elementData
 *          list.add(123);//elementData[0] = new Integer(123);
 *          ...
 *      list.add(11);//如果此次的添加导致底层elementData数组容量不够,则扩容。
 *      默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
 *
 *      结论:如果你知道大概的容量,建议开发中使用带参的构造器,可以减少数组拷贝,提高效率:ArrayList list = new ArrayList(int capacity)
 *
 *      2.2 jdk8中ArrayList的变化:
 *          ArrayList list=new ArrayList();底层Object[] elementData初始化为{}.并没有创建长度为10的数组
 *          list.add(123);//第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到elementData[0]
 *          ...
 *          后续的添加和扩容操作与jdk 7 无异。
 *       2.3 小结:jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象
 *            的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。
 *
 *  3.LinkedList的源码分析:
 *          LinkedList list=new LinkedList();//内部声明了Node类型的first和last属性,默认值为null
 *          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;
             }
        }
 *
 * 4. Vector的源码分析:jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。
 *      在扩容方面,默认扩容为原来的数组长度的2倍。
 *  面试题:ArrayList、LinkedList、Vector三者的异同?
 *  同:三个类都是实现了List接口,存储数据的特点相同:存储有序的、可重复的数据
 *  不同:见上
 *
 * 5.List接口中的常用方法
 *
 * @author 刘瘦瘦
 * @create 2021-04-20-10:29
 */
public class ListTest {
        /*
        void add(int index, Object ele):在index位置插入ele元素
        boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
        Object get(int index):获取指定index位置的元素
        int indexOf(Object obj):返回obj在集合中首次出现的位置
        int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
        Object remove(int index):移除指定index位置的元素,并返回此元素
        Object set(int index, Object ele):设置指定index位置的元素为ele
        List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合

        总结:常用方法
        增:add(Object obj)
        删:remove(int index) / remove(Object obj)
        改:set(int index, Object ele)
        查:get(int index)
        插:add(int index, Object ele)
        长度:size()
        遍历:① Iterator迭代器方式
             ② 增强for循环
             ③ 普通的循环
     */
        @Test
         public void test1(){

            ArrayList list=new ArrayList();
            list.add("有很多话想对你说");
            list.add("但一直没有机会");
            list.add("铺在山与海之间,花盛开就是一句,夜漫过就是一篇");
            System.out.println(list);

            //1.void add(int index, Object ele):在index位置插入ele元素
            list.add(2,"我携带着它们穿越四季,掠过高架");
            System.out.println(list);

            //2. boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
            List list1 = Arrays.asList("黄昏开始书写,黎明是无数的扉页", "全世界拼成首诗,我爱你当做最后一行");
            list.add(4,list1);
            System.out.println(list);

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

           //4 int indexOf(Object obj):返回obj在集合中首次出现的位置
            int index=list.indexOf("有很多话想对你说");
            System.out.println(index);

          //5.int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
            index=list.indexOf("有很多话想对你说");
            System.out.println(index);

            //6.Object remove(int index):移除指定index位置的元素,并返回此元素
            boolean b = list.remove("但一直没有机会");
            System.out.println(b);
            System.out.println(list);

            //7.Object set(int index, Object ele):设置指定index位置的元素为ele
            Object o1= list.set(1, "但一直没有机会");
            System.out.println(o1);

           //8 List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
            List list2 = list.subList(0,  2);
            System.out.println(list2);
        }
    @Test
    public void test3(){
        ArrayList list = new ArrayList();
        list.add(123);
        list.add(456);
        list.add("AA");

        //方式一:Iterator迭代器方式
        Iterator iterator = list.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }

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

        //方式二:增强for循环
        for(Object obj : list){
            System.out.println(obj);
        }

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

        //方式三:普通for循环
        for(int i = 0;i < list.size();i++){
            System.out.println(list.get(i));
        }

        LinkedList<String> list2=new LinkedList<>();
        list2.add(0,"aini");
        System.out.println(list2);

    }
}

List接口的实现类LinkedList中特有的一些方法演示如下。List接口的两位两个实现类:ArrayList、Vector中没有单独的方法都是Collection和List接口中的方法。

**
 *
 * LinkedList中的方法测试,这些方法是LinkedList类中所拥有的
 * @author 刘瘦瘦
 * @create 2021-04-25-12:18
 */
public class LinkedListTest {
    public static void main(String[] args) {
        LinkedList list=new LinkedList();
        list.add("有很多话想跟你说,但一直没有机会");
        list.add("我携带着它们穿越季节,掠过高架,铺在山与海之间");
        list.add("花盛开就是一句,夜漫过就是一篇");
        list.add("黄昏开始书写,黎明是无数的扉页");
        list.add("全世界拼成首诗,我爱你当作最后一行");

        //addFirst(E e) 将指定元素插入此列表的开头。
        list.addFirst("《从你的全世界路过》");
        //addLast(E e)将指定元素添加到此列表的结尾。
        list.addLast("张嘉佳");
        //offer(E e)将指定元素添加到此列表的末尾(最后一个元素)。
        list.offer(new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
        //offer(E e)在此列表的开头插入指定的元素。offer提供可以理解为添加
        list.offerFirst("微博最会讲故事的人");
        //peek()  获取但不移除此列表的头(第一个元素)。
        System.out.println(list.peek());
        //peekFirst() 获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。
        System.out.println(list.peekFirst());
        //peekLast()获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。
        //pollFirst() 获取并移除此列表的第一个元素;如果此列表为空,则返回 null。
        //poll	英[pəʊl] 动词是投票、查(票)、剪,此处可以理解为移除
        //pollLast() 获取并移除此列表的最后一个元素;如果此列表为空,则返回 null。


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


    }
}

LinkedList存储结构:双向链表
在这里插入图片描述
LinkedList源码分析:

public class LinkedList<E>{//E是一个泛型,具体的类型要在实例化的时候才会最终确定
        transient int size = 0;//集合中元素的数量
        //Node的内部类
        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;
        }
    }
        transient Node<E> first;//链表的首节点
        transient Node<E> last;//链表的尾节点
        //空构造器:
        public LinkedList() {
    }
        //添加元素操作:
        public boolean add(E e) {
        linkLast(e);
        return true;
    }
        void linkLast(E e) {//添加的元素e
        final Node<E> l = last;//将链表中的last节点给l 如果是第一个元素的话 l为null
                //将元素封装为一个Node具体的对象:
        final Node<E> newNode = new Node<>(l, e, null);
                //将链表的last节点指向新的创建的对象:
        last = newNode;
                
        if (l == null)//如果添加的是第一个节点
            first = newNode;//将链表的first节点指向为新节点
        else//如果添加的不是第一个节点 
            l.next = newNode;//将l的下一个指向为新的节点
        size++;//集合中元素数量加1操作
        modCount++;
    }
        //获取集合中元素数量
        public int size() {
        return size;
    }
        //通过索引得到元素:
        public E get(int index) {
        checkElementIndex(index);//健壮性考虑
        return node(index).item;
    }
        
    Node<E> node(int index) {
        //如果index在链表的前半段,那么从前往后找
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {//如果index在链表的后半段,那么从后往前找
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }
}

在这里插入图片描述

3、Collection接口的子接口Set接口

在这里插入图片描述

public class SetTest {
    /*
        一、Set:存储无序的、不可重复的数据
        以HashSet为例说明:
        1.无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。

        2.不可重复性:保证添加的元素按照equals()判断时,不能返回true。即:相同的元素只能添加一个。

        二、添加元素的过程:以HashSet为例:
         我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,
         此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组在此位置上是否
         已经有元素:
                如果此位置上没有其他元素,则元素a添加成功。--->情况一
                如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a和元素b的hash值
                    如果hash值不同,则元素a添加成功。--->情况2
                    如果hash值相同,进而需要调用元素a所在类的equals()方法:
                           equals()返回true,元素a添加失败
                           equals()返回false,则元素a添加成功。--->情况3

          对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上数据以链表的方式存储。
          Jdk7:元素a放到数组中,指向原来的元素(头插法)
          Jdk8:原来的元素在数值中,指向元素a
          总结:七上八下
          HashSet底层:数据+链表的结构。
     */
    @Test
    public void test1(){
        Set set=new HashSet();
        set.add(456);//Integer类型的哈希code就是它本身
        set.add(123);
        set.add(123);
        set.add("AA");
        set.add("CC");
        set.add(new Person("刘瘦瘦",23));
        set.add(new Person("刘瘦瘦",23));
        System.out.println(set);

        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
    /*
        LinkedHashSet的使用
        LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据的前一个数据和后一个数据。
        优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet
     */
    @Test
    public void test2(){
        Set set = new LinkedHashSet();
        set.add(456);
        set.add(123);
        set.add(123);
        set.add("AA");
        set.add("CC");
        set.add(new Person("刘瘦瘦",12));
        set.add(new Person("刘瘦瘦",12));
        set.add(123);
        System.out.println(set);

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

HashSet的底层实现是HashMap不信可以看源码,用HashSet一定重写equals()方法和hashCode()方法。

LinkedHashMap实现类
LinkedHashMap输出是有序的,因为Linked.Entry<K,V>除了继承HashMap.Node<K,V>之外,还额外增加了before、after用于指向前一个Entry和后一个Entry。也就是说,元素之间维持着一条总的链表数据结构。
在这里插入图片描述
在这里插入图片描述

public class TestInteger {
    //这是main方法,程序的入口
    public static void main(String[] args) {
        //创建一个HashSet集合:
        LinkedHashSet<Integer> hs = new LinkedHashSet<>();
        System.out.println(hs.add(19));//true
        hs.add(5);
        hs.add(20);
        System.out.println(hs.add(19));//false 这个19没有放入到集合中
        hs.add(41);
        hs.add(0);
        System.out.println(hs.size());//唯一,无序
        System.out.println(hs);
    }
}

TreeSet实现类
使用TreeSet是自定义类要实现自然排序或者外部(定制)排序。

class Person implements Comparable {
    private String name;
    private int age;

    public Person() {

    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

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

    @Override
    public int hashCode() {

        return Objects.hash(name, age);
    }

    //按照姓名从大到下排列,年龄从小到大排列
    @Override
    public int compareTo(Object o) {
        if (o instanceof Person) {
            Person o1 = (Person) o;
            int num = -(this.name.compareTo(o1.name));
            if (num != 0) {
                return num;
            } else {
                int num2 = this.age - o1.age;
                return num2;
            }
        } else {//else这里直接return 0;也行,返回0直接添加失败了
            throw new RuntimeException("输入的类型不匹配");
        }

    }

    public void method(){
        String str="刘瘦瘦";
        int num=10;     
    }
}


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

        3.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals() 返回0则说明两个对象相同,就不在放入
        4.定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()  返回0则说明两个对象相同,就不在放入

        5.特点:唯一,无序(没有按照输入顺序进行输出), 有序(按照升序进行遍历)
     */
    @Test
    public void test1(){
        Set set = new TreeSet();

        //失败:不能添加不同类的对象
//        set.add(123);
//        set.add(24);
//        set.add("AA");
//        set.add(new Person("刘瘦瘦",23));
//        System.out.println(set);

        //举例一
//        set.add(43);
//        set.add(-43);
//        set.add(11);
//        set.add(11);
//        set.add(43);
//        System.out.println(set.toString());

        //举例三
        set.add(new Person("Tom",12));
        set.add(new Person("Jerry",32));
        set.add(new Person("Jim",2));
        set.add(new Person("Mike",65));
        set.add(new Person("Jack",33));
        set.add(new Person("Jack",56));
        System.out.println(set);
        /*
            [Person{name='Tom', age=12}, Person{name='Mike', age=65},
             Person{name='Jim', age=2}, Person{name='Jerry', age=32},
             Person{name='Jack', age=33}, Person{name='Jack', age=56}]

         */
    }

    @Test
    public void test2(){
        Comparator com=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("输入的数据类型不匹配");
            }
        };

        System.out.println(com.getClass());//class com.liuyongbin.set.TreeSetTest$1此时是匿名内部类对象

        Set set=new TreeSet(com);
        set.add(new Person("刘瘦瘦",23));
        set.add(new Person("刘瘦瘦",45));
        set.add(new Person("刘大宝",23));
        System.out.println(set);
        /*
        [Person{name='刘瘦瘦', age=23}, Person{name='刘瘦瘦', age=45}]
         */
    }
}


特点:唯一,无序(没有按照输入顺序进行输出), 有序(按照升序进行遍历)
【2】原理:底层:二叉树(数据结构中的一个逻辑结构)
存储时比根节点小的放左边,比根节点大的放右边。
在这里插入图片描述

五、 集合的第二大阵营

1.Map部分整体结构图

在这里插入图片描述
1.HashMap

/**
 *
 * 一、Map的实现类的结构
 *  |----Map:双列数据,存储key-value对的数据。----类似于高中的函数:y=f(x)
 *     |----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(以HashMap为例)
 *      Map中的value:无序的、可重复的,使用Collection存储所有的value--->value所在的类要重写equals()方法
 *      一个键值对:key-value构成了一个Entry对象。
 *      Map中的entry:无序的、不可重复的,使用Set存储所有的entry
 *
 *  三、HashMap的底层实现原理?以jdk7为例说明:
 *         HashMap map=new HashMap();
 *         在实例化以后,底层创建了长度是16的一维数组Entry[] table.
 *         ...可能已经执行过多次put...
 *         map.put(key1,value1);
 *         首先,调用key1所在类的hashCode()计算key1哈希值。
 *         如果此位置上的数据为空,此时的key1-value1添加成功。----情况1
 *         如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据
 *         的哈希值:
 *              如果key1的哈希值与已经存在的数据的哈希值都不相等,此时key1-value1添加成功。----情况2
 *              如果key1的哈希值和已经存在的某一个数据(key2-value2)d的哈希值相同,继续比较:调用key1所在类的equals(key2)方法,比较:
 *                  如果equals()返回false:此时key1-value1添加成功。--情况3
 *                  如果equals()返回true:使用value1替换value2。
 *           补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。
 *
 *          在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)时,扩容。默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。
 *
 *          jdk8相较于jdk7在底层实现方面的不同:
 *          1.new HashMap():底层没有创建一个长度为16的数组
 *          2.jdk8底层的数组是:Node[],而非Entry[]
 *          3.首次调用put()方法时,底层创建长度为16的数组
 *          4.jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树。
 *              4.1 形成链表时,七上八下(jdk7::新的元素指向旧的元素。jdk8:旧的元素指向新的元素)
 *              4.2当数组的某一个索引位置上的元素以链表形式存在的数据个数>8,且当前数组的长度 > 64时,此时此索引位置上的所数据改为使用红黑树存储。
 *
 * *    DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16
 *      DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75
 *      threshold:扩容的临界值,=容量*填充因子:16 * 0.75 => 12
 *      TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8
 *      MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64
 *
 *  四、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(Object key,Object value)
 * 删除:remove(Object key)
 * 修改:put(Object key,Object value)
 * 查询:get(Object key)
 * 长度:size()
 * 遍历:keySet() / values() / entrySet()
 *
 * @author 刘瘦瘦
 * @create 2021-04-22-16:32
 */
public class MapTest {

    @Test
    public void test1(){
        Map map=new HashMap();
        map.put(null,null);
        System.out.println(map);//{null=null}

        map=new Hashtable();
//        map.put(null,null);//Hash-table中的key-value不能为null
        System.out.println(map);//java.lang.NullPointerException
    }

    @Test
    public void test2(){
        Map map=new HashMap();
        //添加
        map.put(123,"AA");
        map.put(456,"BB");
        map.put(789,"DD");
        System.out.println(map);//{789=DD, 456=BB, 123=AA}

        map=new LinkedHashMap();
        //修改
        map.put(123,"AA");
        map.put(456,"BB");
        map.put(789,"DD");
        System.out.println(map);//{123=AA, 456=BB, 789=DD}
    }
        /*
         添加、删除、修改操作:
     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中的所有数据
     */
        @Test
        public void test3(){
            Map map=new HashMap();
            map.put(123,"AA");
            map.put(456,"BB");
            map.put(12,new Person("刘瘦瘦",23));

            Map map1=new HashMap();
            map1.put(123,"cc");
            map1.put(456,"bb");
            map1.put(77,"DD");
            map1.put(66,"全世界拼成首诗,我爱你当做最后一行");

            //void putAll(Map m):将m中的所有key-value对存放到当前map中
            map.putAll(map1);
            System.out.println(map);

            //Object remove(Object key):移除指定key的key-value对,并返回value
            Object remove = map.remove(123);
            System.out.println(remove);

            //void clear():清空当前map中的所有数据
            map.clear();
            System.out.println(map);//{}
            System.out.println(map.isEmpty());

        }

        /*
        元素查询的操作:
         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是否相等
         */
        @Test
    public void test4(){
            Map map=new HashMap();
            map.put(123,"AA");
            map.put(456,"BB");
            map.put(12,new Person("刘瘦瘦",23));

            //Object get(Object key):获取指定key对应的value
            Object o = map.get(123);
            System.out.println(o);

            //boolean containsKey(Object key):是否包含指定的key
            System.out.println(map.containsKey(12));//true

            //boolean containsValue(Object value):是否包含指定的value  底层一个一个遍历,只调用了equals()
            System.out.println(map.containsValue(new Person("刘瘦瘦", 23)));//true

            //boolean equals(Object obj):判断当前map和参数对象obj是否相等
            Map map1=new HashMap();
            map1.put(123,"AA");
            map1.put(456,"BB");
            map1.put(12,new Person("刘瘦瘦",23));

            System.out.println(map.equals(map1));//true 底层还是equals()
            map1.put(12,new Person("刘瘦瘦瘦",23));
            System.out.println(map.equals(map1));//false

        }

        /*
         元视图操作的方法:
         Set keySet():返回所有key构成的Set集合
         Collection values():返回所有value构成的Collection集合
         Set entrySet():返回所有key-value对构成的Set集合
         */
        @Test
    public void test5(){
            Map map=new HashMap();
            //Set keySet():返回所有key构成的Set集合
            map.put(123,"AA");
            map.put(456,"BB");
            map.put(12,new Person("刘瘦瘦",23));

            Set set = map.keySet();
            System.out.println(set);//[456, 123, 12]

           // Collection values():返回所有value构成的Collection集合
            Collection values = map.values();
            System.out.println(values);//[BB, AA, Person{name='刘瘦瘦', age=23}]

            Set set1 = map.entrySet();
            System.out.println(set1);//[456=BB, 123=AA, 12=Person{name='刘瘦瘦', age=23}]

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

            System.out.println("-----------");
            //遍历所有的value集合:values()
            Iterator iterator2 = map.values().iterator();
            while (iterator2.hasNext()){
                System.out.println(iterator2.next());
            }

            System.out.println("-----------");
            //方式一:遍历遍历所有的key-value
            Iterator iterator3 = map.entrySet().iterator();
            while (iterator3.hasNext()){
                Object obj = iterator3.next();
                //entrySet集合中的元素都是entry
                Map.Entry entry=(Map.Entry)obj;
                System.out.println(entry.getKey()+":"+entry.getValue());
            }
            /*
            456:BB
            123:AA
            12:Person{name='刘瘦瘦', age=23}
             */

            //方式二:
            Set keySet = map.keySet();
            Iterator iterator4 = keySet.iterator();
            while(iterator4.hasNext()){
                Object key = iterator4.next();
                Object value = map.get(key);
                System.out.println(key + "=====" + value);

            }

        }
}

值得注意的一点:HashSet的底层实现就是HashMap。

2.TreeMap

public class TreeMapTest {

    /*
        TreeMap中添加key-value,要求key必须是由同一个类创建的对象
        因为要按照key进行排序:自然排序、定制排序
     */
    //自然排序
    @Test
    public void test1(){
        TreeMap map=new TreeMap();
        Person p1=new Person("Tom",23);
        Person p2=new Person("Jerry",32);
        Person p3=new Person("Jack",20);
        Person p4=new Person("Jack",18);

        map.put(p1,98);
        map.put(p2,58);
        map.put(p3,78);
        map.put(p4,68);

        Iterator iterator = map.keySet().iterator();
        while (iterator.hasNext()){
            Object o=iterator.next();
            System.out.println(o+":"+map.get(o));
        }
    }

    //定制排序
    @Test
    public void test2(){
        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;
                    int num=p1.getName().compareTo(p2.getName());
                    if(num!=0){
                        return num;
                    }else{
                        return ((Integer)(p1.getAge())).compareTo(p2.getAge());
                    }
                }
                throw new RuntimeException("输入的类型不匹配!");
            }
        });

        Person p1=new Person("Tom",23);
        Person p2=new Person("Jerry",32);
        Person p3=new Person("Jack",20);
        Person p4=new Person("Jack",18);

        map.put(p1,98);
        map.put(p2,58);
        map.put(p3,78);
        map.put(p4,68);

        Iterator iterator = map.keySet().iterator();
        while (iterator.hasNext()){
            Object o=iterator.next();
            System.out.println(o+":"+map.get(o));
        }

    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值