第十三章_Java中常用集合大整理(含底层数据结构简单介绍)

第十三章_Java中常用集合大整理

1.集合和数组的区别

  • 集合

    1. 既可以存储基本数据类型还可以存储引用数据类型
    2. 定长–>是数组最大的特点,也是最大的缺点
  • 数组

    1. 只能存储引用数据类型
    2. 长度可变
  • 相同点

    都是容器,可以存储多个数据

  • 不同点

    • 数组的长度是不可变的,集合的长度是可变的

    • 数组可以存基本数据类型和引用数据类型

      集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类

2.集合类体系结构

集合分为单列集合和双列集合,

  • 单列集合(Collection):所有单列集合的顶级父类接口

    • List(可重复) – 继承Collection
      • ArrayList – 实现List
      • LinkedList – 实现List
    • Set(不可重复) – 继承Collection
      • TreeSet – 实现Set
      • HashSet – 实现Set
        • LinkedHashSet – 继承HashSet
  • 存储的时候一个一个存

  • 双列集合(Map):所有双列集合的顶级父类接口

    • HashMap – 继承AbstractMap – 继承 Map
      • LinkedHashMap – 继承HashMap
    • TreeMap – 继承AbstractMap – 继承 Map
  • 存储的时候是一对一对存

  • 图解

在这里插入图片描述

3.单列集合

​ 单列集合框架:

在这里插入图片描述

3.1 Collection接口
  • 概述
    • 所有单列集合的顶级接口,它表示一组对象,这些对象也称为Collection的元素
    • JDK 不提供此接口的任何直接实现.它提供更具体的子接口(如Set和List)实现
  • 创建Collection集合的对象
    • 接口无法直接创建对象,使用多态的形式创建对象
    • 具体的实现类创建。如:ArrayList
  • Collection集合常用方法
  • Collection中的方法在子类中都可以直接使用
方法名说明
boolean add(E e)添加元素
boolean addAll(Collection<? extends E>)将另一个集合元素添加到当前集合中。
boolean remove(Object o)从集合中移除指定的元素
boolean removeIf(Object o)根据条件进行移除
void clear()清空集合中的元素
boolean contains(Object o)判断集合中是否存在指定的元素
boolean isEmpty()判断集合是否为空
int size()集合的长度,也就是集合中元素的个数
Object[] toArray()把集合中的元素,存储到数组中
3.1.1 向集合添加元素

代码演示:

public class Test {
    public static void main(String[] args) {
        Collection<String> list1 = new ArrayList<>();
        System.out.println("------------集合为空--------------");
        System.out.println(list1);
        System.out.println("------------方法:add--------------");
        //添加
        list1.add("aaa");
        list1.add("bbb");
        list1.add("ccc");
        System.out.println(list1);
        System.out.println("------------方法:addAll--------------");
        //向一个集合中添加另一个集合
        Collection<String> list2 = new ArrayList<>();
        list2.add("蔡徐坤");
        list2.add("吴亦凡");
        list2.addAll(list1);
        System.out.println(list2);
    }
}

执行效果:

在这里插入图片描述

3.1.2 删除集合中的元素

代码演示:

public class Test {
    public static void main(String[] args) {
        Collection<String> list = new ArrayList<>();
        System.out.println("------------方法:add--------------");
        //添加
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        list.add("dddd");
        System.out.println(list);
        System.out.println("------------方法:remove--------------");
        //删除指定的元素
        boolean result1 = list.remove("aaa");
        System.out.println(result1);
        boolean result2 = list.remove("eee");
        System.out.println(result2);
        System.out.println(list);
        System.out.println("------------方法:removeif--------------");
        /*
        //普通写法
        list.removeIf(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.length() == 4;
            }
        });*/
        //lambda表达式写法
        //根据条件删除元素
        list.removeIf(s -> s.length() == 4);
        System.out.println(list);
        System.out.println("------------方法:clear--------------");
        //清空集合
        list.clear();
        System.out.println(list);
    }
}

执行效果:

在这里插入图片描述

3.1.3 集合中的其他方法

contains,isEmpty,size,toArray

代码演示:

public class Test {
    public static void main(String[] args) {
        Collection<String> list = new ArrayList<>();
        System.out.println("------------方法:isEmpty + size--------------");
        int size1 = list.size();
        System.out.println(size1);
        boolean result1 = list.isEmpty();
        System.out.println(result1);
        System.out.println("------------方法:add--------------");
        //添加
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        list.add("dddd");
        System.out.println(list);
        System.out.println("------------方法:isEmpty + size--------------");
        //查看集合的大小
        int size2 = list.size();
        System.out.println(size2);
        //判断集合是否为空
        boolean result2 = list.isEmpty();
        System.out.println(result2);
        System.out.println("------------方法:contains--------------");
        //判断是否包含某个元素
        boolean result3 = list.contains("eee");
        System.out.println(result3);
        boolean result4 = list.contains("aaa");
        System.out.println(result4);
        System.out.println("------------方法:toArray--------------");
        //把集合转为一个数组
        Object[] objects = list.toArray();
        for (int i = 0; i < objects.length; i++) {
            System.out.println(objects[i]);
        }
    }
}

执行效果:

在这里插入图片描述

3.1.4集合的遍历
  • 普通for循环遍历

  • 迭代器遍历

    • 迭代器介绍:

      本身是一个Iterator接口

    • 作用:遍历集合的特有方式

    • 方法:
      boolean hasNext()–>判断有没有下一个元素
      E next()–>获取下一个元素

    • 怎么获取Iterator接口
      Iterator<E.> iterator() -->也是Iterator的实现类对象

    • 迭代器中删除的方法

      void remove(): 删除迭代器对象当前指向的元素

  • 增强for循环遍历

    • 介绍
      • 它是JDK5之后出现的,其内部原理是一个Iterator迭代器
      • 实现Iterable接口的类才可以使用迭代器和增强for
      • 简化数组和Collection集合的遍历
    • 使用:遍历数组,还可以遍历集合
    • 格式:
      for(容器中元素的类型 变量名:要遍历的容器名称){
      System.out.println(变量名)
      }
    • 增强for在遍历集合的时候,底层原理是迭代器,所以,不能在遍历集合的同时,改变集合长度
    • 增强for在遍历数组的时候,底层原理不是迭代器

代码演示:

public class Test {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("蔡徐坤");
        list.add("吴亦凡");
        list.add("乔碧萝");
        list.add("giao哥");
        list.add("奥利给");

        System.out.println("----------迭代器-------------");
        //利用迭代器遍历
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

        System.out.println("----------普通for-------------");
        //利用普通for
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }

        System.out.println("-----------增强for------------");
        //利用增强for
        for (String s : list) {
            System.out.println(s);
        }
    }
}

执行效果:

在这里插入图片描述

3.1.5迭代器遍历注意事项
  • 使用迭代器遍历集合的时候不能修改集合的长度

  • 如遍历的时候向集合中添加元素,改变了集合的长度

    会出现并发修改异常ConcurrentModificationException

  • 解决方案,使用listIterator获取迭代器对象操作数据

代码演示(并发修改异常):

public class Test {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("蔡徐坤");
        list.add("吴亦凡");
        list.add("乔碧萝");
        list.add("giao哥");
        list.add("奥利给");

        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String s = iterator.next();
            //不能修改集合的长度否则会有并发修改异常ConcurrentModificationException
            if (s.equals("乔碧萝")) {
                list.add("坦克");
            }
        }
        System.out.println(list);
    }
}

执行效果:

在这里插入图片描述

解决方案:

public class Test {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("蔡徐坤");
        list.add("吴亦凡");
        list.add("乔碧萝");
        list.add("giao哥");
        list.add("奥利给");
        System.out.println(list);

        ListIterator<String> iterator = list.listIterator();
        while (iterator.hasNext()) {
            String s = iterator.next();
          
            if (s.equals("乔碧萝")) {
                iterator.add("坦克");
            }
        }
        System.out.println(list);
    }
}

执行效果:

在这里插入图片描述

3.1.6迭代器中的删除方法

​ void remove(): 删除迭代器对象当前指向的元素

代码演示:

public class Test {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("bbb");
        list.add("ccc");
        list.add("vvv");

        //直接删除
//        list.remove("bbb");
        System.out.println(list);

        //普通for循环遍历删除 -->删除时会有漏删的情况
        //原因:集合中的一个元素被删除时,后面的元素会自动向前补齐空缺的索引
        for (int i = 0; i < list.size(); i++) {
            if ("bbb".equals(list.get(i))){
                list.remove(list.get(i));
            }
        }
        System.out.println(list);

		//迭代器遍历删除
       /* Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            String next = iterator.next();
            if ("bbb".equals(next)){
                iterator.remove();
            }
        }
        System.out.println(list);*/

    }
}

执行效果:

普通for循环删除效果

在这里插入图片描述

迭代器删除效果:

在这里插入图片描述

3.1.7 迭代器并发修改异常源码分析
Iterator<String> iterator = list.iterator();
    /* 循环迭代元素,在迭代的过程中,判断获取出来的元素是不是"乔碧萝"
     如果是,调用add,往集合中添加"坦克"*/
    while(iterator.hasNext()){
        String element = iterator.next();
        if (element.equals("乔碧萝")){
            list.add("坦克");
        }
    }
    =======================================================
     modCount:实际操作元素的次数
     expectedModCount:预期操作的次数

     public Iterator<E> iterator() {
         return new Itr();
     }

     private class Itr implements Iterator<E> {
         int expectedModCount = modCount;

     }

       public E next() {
         checkForComodification();
     }

       final void checkForComodification() {
           if (modCount != expectedModCount)
               throw new ConcurrentModificationException();
       }

       ==============调用add方法的源码=============

       public boolean add(E e) {
           ensureCapacityInternal(size + 1);  // Increments modCount!!
       }

       private void ensureCapacityInternal(int minCapacity) {
           ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
       }

       private void ensureExplicitCapacity(int minCapacity) {
           modCount++;
       }
3.2 List集合
  • List集合的概述

    • 有序集合,这里的有序指的是存取顺序
    • 用户可以精确控制列表中每个元素的插入位置,用户可以通过整数索引访问元素,并搜索列表中的元素
    • 与Set集合不同,列表通常允许重复的元素
  • List集合的特点

    • 元素存取有序
    • 可以重复
    • 有索引
  • List集合的特有方法

    方法名描述
    void add(int index,E element)在此集合中的指定位置插入指定的元素
    E remove(int index)删除指定索引处的元素,返回被删除的元素
    E set(int index,E element)修改指定索引处的元素,返回被修改的元素
    E get(int index)返回指定索引处的元素
  • 数据结构

    • 数据结构之栈和队列

      • 栈结构

        ​ 先进后出

      • 队列结构

        ​ 先进先出

    • 数据结构之数组和链表

      • 数组结构

        ​ 查询快、增删慢

      • 队列结构

        ​ 查询慢、增删快

  • 数据结构之栈的图解

在这里插入图片描述

  • 数据结构之对列图解

在这里插入图片描述

3.2.1 ArrayList集合
  • 数据结构:

    底层是数组结构实现,查询快、增删慢

  • 构造方法

方法名说明
public ArrayList()创建一个空的集合对象
  • 常用成员方法
方法名说明
public boolean remove(Object o)删除指定的元素,返回删除是否成功
public E remove(int index)删除指定索引处的元素,返回被删除的元素
public E set(int index,E element)修改指定索引处的元素,返回被修改的元素
public E get(int index)返回指定索引处的元素
public int size()返回集合中的元素的个数
public boolean add(E e)将指定的元素追加到此集合的末尾
public void add(int index,E element)在此集合中的指定位置插入指定的元素

ArrayList <E> :

​ 可调整大小的数组实现

​ <E> : 是一种特殊的数据类型,泛型。

怎么用呢 ?

​ 在出现E的地方我们使用引用数据类型替换即可

​ 举例:ArrayList<String>, ArrayList<Student>

集合中添加元素(add)

代码演示:

public class ArrayList_add {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        //默认添加是在集合的末尾
        list.add("111");
        list.add("222");
        list.add("333");
        list.add("444");
        list.add("555");

        System.out.println(list);//[111, 222, 333, 444, 555]
        //插入到指定索引的位置,后面的数据向后移一位
        list.add(2,"aaa");
        System.out.println(list);//[111, 222, aaa, 333, 444, 555]
    }
}

执行效果:

在这里插入图片描述

删除集合中的元素(remove)

代码演示:

public class ArrayList_Remove {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();

        //默认添加是在集合的末尾
        list.add("111");
        list.add("222");
        list.add("333");
        list.add("444");
        list.add("555");
        list.add("111");
        System.out.println(list);

        //删除之后后面的元素会自动向前补充空缺位置

        //根据元素进行删除   只删除集合中第一次出现的元素
        boolean b1 = list.remove("111");
        boolean b2 = list.remove("asfd");

        System.out.println(b1);//  存在返回true
        System.out.println(b2);// 不存在返回false
        System.out.println(list);

        //根据索引进行删除
        String s = list.remove(0); // 返回被删除的元素
        System.out.println(s);//  222
        System.out.println(list);
    }
}

执行效果:

在这里插入图片描述

修改集合中的元素(set)

代码演示:

public class ArrayList_Set {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();

        //默认添加是在集合的末尾
        list.add("111");
        list.add("222");
        list.add("333");
        list.add("444");
        list.add("555");

        //修改指定的元素
        String s = list.set(0, "abc"); // 返回值是被修改的元素
        System.out.println(s); // 111
        System.out.println(list);//[abc, 222, 333, 444, 555]
    }
}

执行效果:

在这里插入图片描述

获取集合中的元素(get)/大小(size)

代码演示:

public class ArrayList_Get {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();

        //默认添加是在集合的末尾
        list.add("111");
        list.add("222");
        list.add("333");
        list.add("444");
        list.add("555");

        //根据索引获取
        String s1 = list.get(0);
        String s2 = list.get(1);
        String s3 = list.get(2);

        System.out.println(s1);//111
        System.out.println(s2);//222
        System.out.println(s3);//333

        //获取集合的大小
        int size = list.size();

        System.out.println(size);//5
    }
}

执行效果:

在这里插入图片描述

遍历集合

代码演示:

public class ArrayList_For {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();

        //默认添加是在集合的末尾
        list.add("111");
        list.add("222");
        list.add("333");
        list.add("444");
        list.add("555");

        //遍历集合普通for
        System.out.println("---------普通for遍历----------");
        for(int i = 0;i<list.size();i++){
            System.out.println(list.get(i));
        }

        //增强for遍历
        System.out.println("---------增强for遍历---------");
        for (String s : list) {
            System.out.println(s);
        }

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

执行效果:

在这里插入图片描述

ArrayList第一次添加元素源码分析

在这里插入图片描述

3.2.2 LinkedList集合
  • 数据结构

    底层是双链表结构实现,查询慢、增删快

    链表图解:

在这里插入图片描述

  • 特有方法
方法名说明
public void addFirst(E e)在该列表开头插入指定的元素
public void addLast(E e)将指定的元素追加到此列表的末尾
public E getFirst()返回此列表中的第一个元素
public E getLast()返回此列表中的最后一个元素
public E removeFirst()从此列表中删除并返回第一个元素
public E removeLast()从此列表中删除并返回最后一个元素

代码演示:

public class Test {
    public static void main(String[] args) {
        LinkedList<String> linkedList = new LinkedList<>();
        linkedList.add("aaa");
        linkedList.add("vvv");
        linkedList.add("bbb");
        linkedList.add("ddd");
        linkedList.add("fff");

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

        //获取集合中的第一个元素
        String first = linkedList.getFirst();
        System.out.println("获取集合第一个元素:"+first);
        System.out.println("---------------------");

        //获取集合中的最后一个元素
        String last = linkedList.getLast();
        System.out.println("获取集合最后一个元素:"+last);
        System.out.println("--------------------");

        //在集合0索引位置添加
        addFirst(linkedList);
        System.out.println("----------------------");

        //在集合末尾添加
        addLast(linkedList);
        System.out.println("----------------------");

        //删除集合中第一个和最后一个元素
        removeFisrtAndLast(linkedList);

    }

    private static void removeFisrtAndLast(LinkedList<String> linkedList) {
        linkedList.removeFirst();
        System.out.println("删除集合第一个元素:"+linkedList);
        linkedList.removeLast();
        System.out.println("删除集合最后一个元素:"+linkedList);
    }

    private static void addLast(LinkedList<String> linkedList) {
        linkedList.addLast("www");
        System.out.println("在集合末尾添加:"+linkedList);
    }

    private static void addFirst(LinkedList<String> linkedList) {
        linkedList.addFirst("qqq");
        System.out.println("在集合第一个位置添加:"+linkedList);
    }
}

执行效果:

在这里插入图片描述

LinkedList添加元素源码分析

在这里插入图片描述

3.2.3 ArrayList集合与LinkedList集合的比较
  • ArrayList集合

    ​ 底层是数组结构实现,查询快、增删慢

  • LinkedList集合

    ​ 底层是链表结构实现,查询慢、增删快

3.3 Set集合
1.Set概述:接口-->没有对Collection的功能进行扩充
2.使用:根据实现类对象去创建
3.特点:
   a.不允许出现重复元素
   b.无序(LinkedHashSet除外)
   c.没有索引-->增强for,迭代器遍历
4.实现类
    HashSet
      a.不允许出现重复元素
      b.无序
      c.没有索引-->增强for,和迭代器遍历集合
    TreeSet
      a.不允许出现重复元素
      b.根据其元素的自然排序进行排序
      c.根据指定的比较器进行排序
3.3.1 HashSet集合
  • 底层数据结构是哈希表
  • 存取无序
  • 不可以存储重复元素
  • 没有索引,不能使用普通for循环遍历
  • HashSet本身没有功能,依靠底层的HashMap
HashSet如何保证数据唯一?
  1. 先获取元素的哈希值,如果哈希值不一样,直接存
  2. 如果哈希值一样,再调用equals方法,比较内容,内容不一样,也可以存
  3. 如果哈希值一样,调用的equals方法内容也一样,HashSet直接去重复

代码演示:

public class Test {
    public static void main(String[] args) {
        HashSet<String> hashSet = new HashSet<>();

        hashSet.add("hello");
        hashSet.add("world");
        hashSet.add("java");
        hashSet.add("java");
        hashSet.add("java");
        hashSet.add("java");
        hashSet.add("java");
        hashSet.add("通话");
        hashSet.add("重地");
        System.out.println(hashSet);
		
        //哈希值一样,再调用equals方法,比较内容,内容不一样,也可以存
        System.out.println("通话".hashCode());//1179395
        System.out.println("重地".hashCode());//1179395

        System.out.println("----------迭代器遍历----------");
        Iterator<String> iterator = hashSet.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
        System.out.println("----------增强for遍历-----------");
        for (String s : hashSet) {
            System.out.println(s);
        }
    }
}

执行效果:

在这里插入图片描述

哈希表
  • 哈希值简介

    ​ 是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值

  • 如何获取哈希值

    ​ Object类中的public int hashCode():返回对象的哈希码值

  • 哈希值的特点

    • 同一个对象多次调用hashCode()方法返回的哈希值是相同的
    • 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
  • JDK1.8以前

    ​ 数组 + 链表

在这里插入图片描述

  • JDK1.8以后

    • 节点个数少于等于8个

      ​ 数组 + 链表

    • 节点个数多于8个

      ​ 数组 + 红黑树

在这里插入图片描述

LinkedHashSet
  1. LinkedHashSet extends HashSet
  2. 数据结构:链表+哈希表==>双链表
  3. 特点:
    a.有序
    b.不允许元素重复
    c.没有索引

代码演示:

public class Test {
    public static void main(String[] args) {
        LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
        linkedHashSet.add("吴亦凡");
        linkedHashSet.add("蔡徐坤");
        linkedHashSet.add("乔碧萝");
        linkedHashSet.add("giao哥");
        linkedHashSet.add("坦克");
        linkedHashSet.add("坦克");
        linkedHashSet.add("坦克");

        System.out.println(linkedHashSet);
        //遍历,没有索引,不能使用普通遍历
        for (String s : linkedHashSet) {
            System.out.println(s);
        }
    }
}

执行效果:

在这里插入图片描述

3.3.2 TreeSet集合
  • 底层是一个红黑树结构,基于Set接口的红黑树的实现.

  • 不可以存储重复元素

  • 没有索引

  • 可以将元素按照规则进行排序

    • TreeSet():根据其元素的自然排序进行排序。

      使用元素的自然顺序对元素进行排序(内部会使用Comparator比较器对元素进行默认的升序排序),

    • TreeSet(Comparator comparator) :根据指定的比较器进行排序

自然排序

代码演示:

public class Test {
    public static void main(String[] args) {
        //会自动去重复和自动排序
        TreeSet<Integer> treeSet1 = new TreeSet<>();
        treeSet1.add(1);
        treeSet1.add(5);
        treeSet1.add(4);
        treeSet1.add(4);
        treeSet1.add(6);
        treeSet1.add(3);

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

        TreeSet<String> treeSet2 = new TreeSet<>();
        treeSet2.add("a");
        treeSet2.add("c");
        treeSet2.add("c");
        treeSet2.add("b");
        treeSet2.add("d");

        System.out.println(treeSet2);
    }
}

执行效果:

在这里插入图片描述

字符串的比较:

  • 比较的是字符的ASCII码值
  • 从字符串的第一个字符开始比较,如果相同就往后移一位接着比较

代码演示:

public class Test {
    public static void main(String[] args) {
        String s1 = "abc";
        String s2 = "aaa";

        System.out.println(s1.compareTo(s2));//1

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

        TreeSet<String> treeSet = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
        treeSet.add("aba");
        treeSet.add("aaa");
        treeSet.add("vsag");
        treeSet.add("biqwojh");

        System.out.println(treeSet);      //[aaa, aba, biqwojh, vsag]
    }
}

执行效果:

在这里插入图片描述

比较器排序
  1. 自定义类实现Comparable接口
  2. 重写接口中的compareTo方法

代码演示:

public class TreeSetTest4 {
    public static void main(String[] args) {
       /* 
       内部类设置比较器规则
       TreeSet<Teacher> treeSet = new TreeSet<>(new Comparator<Teacher>() {
            @Override
            public int compare(Teacher o1, Teacher o2) {
                int result = o1.getAge() - o2.getAge();
                result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;
                return result;
            }
        });
		*/
		
		//λ表达式设置比较器规则按照年龄的升序排序
        //如果年龄相同就再按名字自然排序的升序排序
        TreeSet<Teacher> treeSet = new TreeSet<>(
                (o1,o2) -> {
                    int result = o1.getAge() - o2.getAge();
                    result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;
                    return result;
                }
        );

        treeSet.add(new Teacher("aaa",16));
        treeSet.add(new Teacher("bbb",18));
        treeSet.add(new Teacher("ccc",14));

//        System.out.println(treeSet);
        for (Teacher teacher : treeSet) {
            System.out.println(teacher);
        }
    }
}
class Teacher {
    private String name;
    private int age;

    public Teacher() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

执行效果:

在这里插入图片描述

4. 双列集合

4.1 Map集合
  1. 概述:是一个双列集合的接口
    • interface Map<K,V> K:键的类型;V:值的类型
  2. 存储元素的特点:键值对的形式存储
  3. 特点:
    • 键唯一,不能重复,一个键对应一个值
    • 无序
    • 没有索引

代码演示:

public class Test {
    public static void main(String[] args) {
        //map集合中存储都是键值对。每次添加两个元素
        /*
                键不可重复,值可以重复
                键值对是一一对应的,每个键都能找到相应的值
         */

        Map<String,String> map = new HashMap<>();

        map.put("A001","小红");
        map.put("A002","小兰");
        map.put("A003","小黑");
        //如果键重复,后面的值会覆盖前面数据的值
        map.put("A003","小黄");

        System.out.println(map);
    }
}

执行效果:

在这里插入图片描述

4.1.1 Map集合的常用方法

方法介绍

方法名说明
V put(K key,V value)添加元素
V remove(Object key)根据键删除键值对元素
void clear()移除所有的键值对元素
boolean containsKey(Object key)判断集合是否包含指定的键
boolean containsValue(Object value)判断集合是否包含指定的值
boolean isEmpty()判断集合是否为空
int size()集合的长度,也就是集合中键值对的个数

代码演示:

public class Test {
    public static void main(String[] args) {
        Map<String , String> map = new HashMap<>();

        //如果添加数据时键已经存在,就会覆盖原来的值
        map.put("A001","小红");
        map.put("A002","小兰");
        map.put("A003","小黑");
        map.put("A004","小黄");
        map.put("A005","小白");
        System.out.println(map);

        System.out.println("------------向集合中添加元素(键已存在)------------");
        //向集合中添加元素
        putMethod(map);

        System.out.println("-----------删除集合中的元素(根据键删除)-------------");
        //删除集合中的元素
        removeMethod(map);

        System.out.println("---------判断键是否存在---------------");
        //判断键在集合中是否存在
        conKeyMethod(map);

        System.out.println("----------判断值是否存在--------------");
        //判断值在集合中是否存在
        conValMethod(map);

        System.out.println("-----------获取集合的大小-------------");
        //获取集合的大小
        mapSize(map);

        System.out.println("-----------判断集合是否为空-------------");
        //判断集合是否为空
        isEmpty(map);

        System.out.println("------------清空集合------------");
        clearMethod(map);



    }

    private static void mapSize(Map<String, String> map) {
        int size = map.size();
        System.out.println(map);
        System.out.println(size);
    }

    private static void isEmpty(Map<String, String> map) {
        boolean empty1 = map.isEmpty();
        System.out.println(empty1);
        map.clear();
        boolean empty2 = map.isEmpty();
        System.out.println(empty2);
    }

    private static void conValMethod(Map<String, String> map) {
        boolean value1 = map.containsValue("aaa");
        boolean value2 = map.containsValue("小黑");
        System.out.println(value1);
        System.out.println(value2);
    }

    private static void conKeyMethod(Map<String, String> map) {
        boolean key1 = map.containsKey("A001");
        System.out.println(key1);
        boolean key2 = map.containsKey("A002");
        System.out.println(key2);
    }

    private static void clearMethod(Map<String, String> map) {
        map.clear();
        System.out.println(map);
    }

    private static void removeMethod(Map<String, String> map) {
        map.remove("A001");
        System.out.println(map);
    }

    private static void putMethod(Map<String, String> map) {
        map.put("A001","小紫");
        System.out.println(map);
    }
}

执行效果:

在这里插入图片描述

4.1.2 Map集合的获取功能

方法介绍

方法名说明
V get(Object key)根据键获取值
Set<K> keySet()获取所有键的集合
Collection<V> values()获取所有值的集合
Set<Map.Entry<K,V>> entrySet()获取所有键值对对象的集合

代码演示:

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

        Map<String, String> map = new HashMap<>();

        //如果添加数据时键已经存在,就会覆盖原来的值
        map.put("A001", "小红");
        map.put("A002", "小兰");
        map.put("A003", "小黑");
        map.put("A004", "小黄");
        map.put("A005", "小白");
        System.out.println(map);

        //根据键获取值
        System.out.println("===========根据键获取值==========");
        String s = map.get("A001");
        System.out.println(s);

        //获取所有的键的集合
        System.out.println("======获取键的集合=========");
        Set<String> strings = map.keySet();
        for (String string : strings) {
            System.out.println(string);
        }

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

        //获取所有键值对集合
        System.out.println("=========获取所有键值对集合========");
        Set<Map.Entry<String, String>> entries = map.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            //从键值对中获取键
            String key = entry.getKey();
            //从键值对中获取值
            String value = entry.getValue();
            System.out.println(entry);
            System.out.println("键:" + key + ",值:" + value);
        }


        System.out.println("========foreach遍历===========");
        map.forEach(
                (String key, String value) ->
                        System.out.println("键:" + key + ",值:" + value)
        );
        //λ表达式简写
        /*map.forEach(
                (key,value)
                        ->
                System.out.println("键:" + key + ",值:" + value));*/
    }
}

执行效果:

在这里插入图片描述

4.1.3 Map集合的遍历方式
  • 方式1(通过键获取值遍历集合数据)
    • 遍历思路
      • 我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合
        • 把所有的丈夫给集中起来
        • 遍历丈夫的集合,获取到每一个丈夫
        • 根据丈夫去找对应的妻子
    • 步骤分析
      • 获取所有键的集合。用keySet()方法实现
      • 遍历键的集合,获取到每一个键。用增强for实现
      • 根据键去找值。用get(Object key)方法实现
  • 代码演示:
public class Test {
    public static void main(String[] args) {
        //创建集合对象
        Map<String, String> map = new HashMap<String, String>();

        //添加元素
        map.put("张无忌", "赵敏");
        map.put("郭靖", "黄蓉");
        map.put("杨过", "小龙女");

        //获取所有键的集合。用keySet()方法实现
        Set<String> keySet = map.keySet();
        //遍历键的集合,获取到每一个键。用增强for实现
        for (String key : keySet) {
            //根据键去找值。用get(Object key)方法实现
            String value = map.get(key);
            System.out.println(key + "," + value);
        }
    }
}
  • 执行效果:

在这里插入图片描述

  • 方式2(通过键值对获取集合的数据)
    • 遍历思路
      • 我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合
        • 获取所有结婚证的集合
        • 遍历结婚证的集合,得到每一个结婚证
        • 根据结婚证获取丈夫和妻子
    • 步骤分析
      • 获取所有键值对对象的集合
        • Set<Map.Entry<K,V>> entrySet():获取所有键值对对象的集合
      • 遍历键值对对象的集合,得到每一个键值对对象
        • 用增强for实现,得到每一个Map.Entry
      • 根据键值对对象获取键和值
        • 用getKey()得到键
        • 用getValue()得到值
  • 代码演示:
public class Test {
    public static void main(String[] args) {
        //创建集合对象
        Map<String, String> map = new HashMap<String, String>();

        //添加元素
        map.put("张无忌", "赵敏");
        map.put("郭靖", "黄蓉");
        map.put("杨过", "小龙女");

        //获取所有键值对对象的集合
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        //遍历键值对对象的集合,得到每一个键值对对象
        for (Map.Entry<String, String> me : entrySet) {
            //根据键值对对象获取键和值
            String key = me.getKey();
            String value = me.getValue();
            System.out.println(key + "," + value);
        }
    }
}
  • 执行效果:

在这里插入图片描述

4.2 HashMap集合
  • HashMap底层是哈希表结构的
  • 依赖hashCode方法和equals方法保证键的唯一
  • 如果键要存储的是自定义对象,需要重写hashCode和equals方法
4.2.1存储基本数据类型

代码演示:

public class Test {
    public static void main(String[] args) {
        //创建集合对象
        HashMap<String, String> map = new HashMap<String, String>();

        //添加元素
        map.put("蔡徐坤", "乔碧萝");
        map.put("吴亦凡", "凤姐");
        map.put("Myself", "苍老师");

        //获取所有键值对对象的集合
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        //遍历键值对对象的集合,得到每一个键值对对象
        for (Map.Entry<String, String> me : entrySet) {
            //根据键值对对象获取键和值
            String key = me.getKey();
            String value = me.getValue();
            System.out.println(key + "," + value);
        }
    }
}

执行效果:

在这里插入图片描述

4.2.2 存储自定义数据类型
  • 要重写equals和hashCode方法保证数据的唯一性
  • 如果不重写比较的是对象的地址值,两个对象的地址值不同就可以存,不能保证map集合的键唯一
未重写equals和hashCode方法,存储数据

代码演示:

public class Test {
    public static void main(String[] args) {
        HashMap<Student, String> hashMap = new HashMap<>();

        hashMap.put(new Student("小黑",18),"河南");
        hashMap.put(new Student("小红",16),"东北");
        hashMap.put(new Student("小蓝",19),"北京");
        //未重写equals和hashCode方法,默认比较地址值
        hashMap.put(new Student("小黑",18),"广东");

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

        hashMap.forEach((student, str) -> System.out.println(student + "..." + str));
    }
}
class Student {
    private String name;
    private int age;

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", 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;
    }

    public Student() {
    }
}

执行效果:

在这里插入图片描述

重写equals和hashCode方法,存储数据

代码演示:

public class Test {
    public static void main(String[] args) {
        HashMap<Student, String> hashMap = new HashMap<>();

        hashMap.put(new Student("小黑",18),"河南");
        hashMap.put(new Student("小红",16),"东北");
        hashMap.put(new Student("小蓝",19),"北京");
        //添加的自定义数据类型一致时,会覆盖
        hashMap.put(new Student("小黑",18),"广东");

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

       hashMap.forEach((student,str)-> System.out.println(student+"..."+str));
    }
}
class Student {
    private String name;
    private int age;

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", 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 age == student.age &&
                Objects.equals(name, student.name);
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public Student() {
    }
}

执行效果:

在这里插入图片描述

LinkedHashMap
  1. 特点:

    • 存取有序
    • key不能重复
    • 没有索引
  2. 使用:
    和HashMap一样,遍历也一样

  3. 代码演示:

    public class Test {
        public static void main(String[] args) {
            LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
            linkedHashMap.put("1","老王");
            linkedHashMap.put("2","老李");
            linkedHashMap.put("3","老马");
            linkedHashMap.put("4","老黄");
    
            System.out.println(linkedHashMap);
    
            for (String key : linkedHashMap.keySet()) {
                String value = linkedHashMap.get(key);
                System.out.println("键:"+key+",值:"+value);
            }
        }
    }
    

执行效果:

在这里插入图片描述

4.3 TreeMap集合
  • TreeMap底层是红黑树结构

    基于红黑树(Red-Black tree)的 NavigableMap 实现。

  • 内部包含了一个比较器,会对key进行排序

    依赖自然排序或者比较器排序,对键进行排序

  • 如果键存储的是自定义对象,需要实现Comparable接口或者在创建TreeMap对象时候给出比较器排序规则

代码演示:

public class Test {
    public static void main(String[] args) {
        //以键的降序排序
        TreeMap<Integer, String> treeMap = new TreeMap<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;
            }
        });

        /*
        λ表达式
        TreeMap<Integer, String> treeMap = new TreeMap<>((o1, o2) -> o2-o1);
        */

        treeMap.put(1, "坤坤");
        treeMap.put(5, "吴亦凡");
        treeMap.put(3, "Myself");
        System.out.println(treeMap);

        for (Map.Entry<Integer, String> entry : treeMap.entrySet()) {
            System.out.println("键:"+entry.getKey()+",值:"+entry.getValue());
        }
    }
}

执行效果:

在这里插入图片描述

5.线程安全常用集合

  • 单列集合,为保证线程安全使用vector集合
  • 双列集合,为保证线程安全使用Hashtable和ConcurrentHashMap工具类
    • Hashtable出现的原因 : 在集合类中HashMap是比较常用的集合对象,但是HashMap是线程不安全的(多线程环境下可能会存在问题)。为了保证数据的安全性我们可以使用Hashtable,但是Hashtable的效率低下。
    • ConcurrentHashMap出现的原因 : Hashtable的效率低下,为了提高效率我们通常使用ConcurrentHashMap。

基于以上两个原因我们可以使用JDK1.5以后所提供的ConcurrentHashMap。

  • HashMap是线程不安全的。多线程环境下会有数据安全问题
  • Hashtable是线程安全的,但是会将整张表锁起来,效率低下
  • ConcurrentHashMap也是线程安全的,效率较高。 在JDK7和JDK8中,底层原理不一样。
5.1 Vector
  • 用法 : Vector<数据类型> 集合名 = new Vector<>();
  • 特点:
    • 有序
    • 底层是数组
    • 是同步的,线程安全,但是效率低
  • 方法
    • 与普通单列集合方法基本一致
5.2 HashTable
  • 底层数据结构是哈希表
  1. HashTable采用的是悲观锁synchronized的形式保证数据的安全性
  2. 只要有线程访问就会把整张表全部锁按起来,所以HashTable的效率底下。
5.3 ConcurrentHashMap
  • 既可以保证效率,又可以保证安全
5.4 并发工具类-ConcurrentHashMap1.7原理

在这里插入图片描述

5.5 并发工具类-ConcurrentHashMap1.8原理

在这里插入图片描述

  1. 如果使用空参构造创建ConcurrentHashMap对象,则什么事情都不做。
    在第一次添加元素的时候创建哈希表
  2. 计算当前元素应存入的索引。
  3. 如果该索引位置为null,则利用cas算法,将本结点添加到数组中。
  4. 如果该索引位置不为null,则利用volatile关键字获得当前位置最新的结点地址,挂在他下面,变成链表。
  5. 当链表的长度大于等于8时,自动转换成红黑树
  6. 以链表或者红黑树头结点为锁对象,配合悲观锁保证多线程操作集合时数据的安全性。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值