美文网首页
Java 集合

Java 集合

作者: Java慈祥 | 来源:发表于2021-11-21 18:10 被阅读0次

    Java 集合是一个常用的技术 ,无论是在: 开发使用,还是面试总是高频的提及到~

    • 正因如此: 本篇是对个人使用集合的, 总结 方法... 不适合初学者,适合面试|复习...

    Java 集合

    集合和数组:

    • 数组声明了它容纳的元素的类型,而集合可以不声明存储Object类型 可以通过泛型进行规范!
    • 数组是静态的,一个数组实例具有固定的大小,一旦创建了就无法改变容量了。
      集合是可以动态扩展容量,可以根据需要动态改变大小,集合提供更多的成员方法,能满足更多的需求。
    • 集合: 和数组一样Java中用来存储数据的作用,弥补了数组长度固定的缺点更灵活

    Java 集合框架概述

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

    Collection接口: 单列数据,定义了存取一组对象的方法的集合

    • List:元素有序、可重复的集合
    • Set:元素无序、不可重复的集合

    Map接口:双列数据,保存具有映射关系 key-value 的集合

    Collection 接口:

    2021042520100832

    简介

    Collection集合主要有List和Set两大接口 不常用的 Queue接口

    List 接口

    • 一个有序,(元素存入集合的顺序和取出的顺序一致) 的集合容器 元素可以重复,可以插入多个null元素,元素都有索引

    • 有三个实现类: ArrayList LinkedList Vector

    Set 接口

    • 一个无序, (存入和取出顺序有可能不一致) 的集合容器

      • 不可以存储重复元素,只允许存入一个null元素 必须保证元素唯一性
    • 常用实现类: HashSet LinkedHashSet TreeSet

    Collection接口的常用方法:

    JDK不提供此接口的任何直接实现,而是提供更具体的子接口:set list 都默认继承了Collection 的方法();

    boolean     .add( object );             //在列表 末尾添元素 和数组一样 起始索引位置从 0 开使,可储存null; 数值类型会自动装箱;
    void        .add( int,object );         //指定索引位置添加元素 但 不可指定 在超过目前 集合长度;
    void        .forEach(System.out::print);//jdk8 新特性;一种循环遍历;  
    void        .clear();                   //清空集合;
    int         .size();                    //返回集合元素个数; 
    boolean     .contains( object );        //判断集合在是否存在指定元素; ture/flase; 底层相当于是调 equls();比,String类重写了所以比较的是值;
                                            //没重写当然就是比较地址咯;
    boolean     .containAll( Collection );  //判断参数是个集合,判断参数集合自己有没有,都有就true 一个没有就false
    boolean     .equals( Coll... );         //想要返回 true,需要当前集合元素 和 参数集合元素值都相同 地址 ;根据new ArrayList 或其它集合/其顺序也要相同;
    Object      .get( int );                //返回指定索引的元素;( 注意这是Object 类型需要类型转换 );
    List        .sublist(int1,int2);        //返回集合 1-2 位置的子集合;
    Object      .set(int,Object);           //设置指定 int 位置的元素;
    
    
    注意不可以在,集合进行循环遍历的时候移除元素!   因为集合长度发送改变,循环遍历报错!
    Object      .remove( int );             //删除指定下标中的元素; 返回删除的元素;
    boolean     .remove( object/int );      //移除集合中指定元素; true移除成功 false移除失败:不存在要移除元素; 
                                            //虽然数值类型会装箱,int下标删除了..需要确保下标存在
    boolean     .removeAll( Collection );   //从集合中移除指定: 一个集合元素; 两个集合A B, A.removeAll(B); //把A B集合中一样的元素移除掉了(交集);
    
    正确的删除需要使用:Iterator对象~
    Iterator    .iterator();                //返回一个 Iterator对象; 
    
    数组集合相互转换
    Object[]    .toArray();                 //可以将集合返回一个 Object[] 数组;
    集合        Arrays.aslist( 数组 );       //Arrays类的静态方法: 将数组转换成集合;
    

    List 接口

    Arraylist

    Arraylist: 对数组进行封装,实现长度可变的数组(根据增删自动改变元素在集合中位置)

    • Arraylist 底层其实就是一个,可以动态调节长度的数组
      • JDK1.7:ArrayList饿汉式,直接创建一个初始容量为10的数组
      • JDK1.8:ArrayList懒汉式,一开始创建一个长度为0的数组,当添加第一个元素时再创建一个始容量为10的数组
    • 优: 更利于遍历 和随机访问元素

    基本使用:

    • 开发常用的就是这个,这里就以 Arraylist 举例了
    public void jh(){
        /** 创建集合对象 */
        //      Collection coll = new ArrayList();
        ArrayList arrayList = new ArrayList();      //List集合存储Object类型元素,支持重复元素!
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(2);
        arrayList.add(3);
        arrayList.add("2");
        arrayList.add(null);                        //可以存储null
        System.out.println("arrayList集合遍历:");
        for (Object o : arrayList) {
            System.out.println(o);
        }
    
        /** 常用方法 */
        //contains(Object obj):判断当前集合中是否包含obj   :判断时会调用obj对象所在类的equals(),自定义对象需要重写equals
        System.out.println("arrayList集合是否存在 1:"+arrayList.contains(1));
    
        //containsAll(Collection coll1) 判断形参coll1中的所有元素是否都存在于当前集合中
        ArrayList arrayList2 = new ArrayList();
        arrayList2.add(1);
        arrayList2.add(2);
        System.out.println("arrayList集合是否存在 arrayList2的所有元素:"+arrayList.containsAll(arrayList2));
    
        //retainAll(Collection coll1):交集:获取当前集合和coll1集合的交集,并返回给当前集合
        //        arrayList.retainAll(arrayList2);
        //        System.out.println("retainAll(Collection coll1):交集:获取当前集合和coll1集合的交集,并返回给当前集合");
        //        for (Object o : arrayList) {
        //            System.out.println(o);
        //        }
    
        /** 注释上面: retainAll() 让两个集合元素 A.retainAll(B) A集合只保留B 交集共同的数据了~ */
        //remove(Object obj):从当前集合中移除obj元素
        //removeAll(Collection coll1):差集:从当前集合中移除coll1中所有的元素
        arrayList.removeAll(arrayList2);
        System.out.println("removeAll(Collection coll1):差集:从当前集合中移除coll1中所有的元素");
        for (Object o : arrayList) {
            System.out.println(o);
        }
    
        /** 数组 ———》List集合 */
        System.out.println("使用: Arrays. asList(array) 进行转换");
        List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});
        System.out.println(list);
    
        /** List集合 ———》数组 */
        System.out.println("使用: List 自带的 toArray() 方法");
        Object[] objects = list.toArray();
        for (Object obj : objects) {
            if(obj instanceof String)           // 对象 instanceof 类型  比较对象是否属于改类型~
                System.out.print(obj+" ");
        }
    }
    

    LinkedList

    LinkedList:采用双向链表存储方式

    • 优:

      在插入删除时效率更高,同Arraylist一样都实现list接口比Arraylist多了一些方法 缺点就是查找遍历没有Arraylist 快

    LinkedList 新增方法

    void    .addFist( object );         //在集合 首部 添加元素 (索引0);
    void    .addLast( object );         //在集合 末尾 添加元素;
    Object  .getFist(  );               //返回 第一元素;
    Object  .getLast(  );               //返回 最后一个元素; 
    Object  .removrFist(  );            //删除 并 返回第一个元素;
    Object  .removeLast(  );            //删除 并 返回最后一个元素;    
    

    Vector

    Vector 读:歪课特 远古代码,jdk1.0就有的

    • 通过 Vector(); 构造器创建对象:

      底层就创建了长度为10的数组,扩容方面就是扩展为原数组长度的二倍;

    ArrayList/LinkedList/vector的异同?

    ArrayList和LinkedList的异同

    • 二者都是线程不安全的,相对于线程安全的Vector,执行效率高

    • ArrayList是实现了基于动态数组的数据结构,LinkedList是基于链表的数据结构

    • 对于随机访问get和set: ArrayList优于LinkedList, LinkedList要移动指针

    • 对于新增(特质插入)add和删除remove操作LinkedList占优,因为ArrayList要移动数据

    ArrayList和Vector的异同:

    • Vector和ArrayList几乎是完全相同的
    • Vector是同步类(Synchronized),强同步类: 开销就比ArrayList大,访问要慢~
    • Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍

    Set 接口

    简介:

    Set接口实现 Collection接口

    • 存储一组无序的(不等于随机,而是根据数据的哈希值决定的),不可重复的,集合数据

    • Set接口中没有额外定义新的方法,使用的都是Collection 中声明过的方法:

    • Set 接口的实现类:HashSet LinkedHashSet TreeSet

    HashSet

    • 唯一,无序。基于 HashMap 实现,底层采用 HashMap 保存数据

      20210425154507925
    • **它不允许集合中有重复的值, **

      将对象存储在HashSet之前,要先确保对象重写equals()和hashCode()方法,这样才能比较对象的值是否相等,以确保set中没有储存相等的对象

    LinkedHashSet: 作为HashSet子类,遍历器内部数据时,可以按照添加的顺序遍历

    • 作为HashSet类的子类,在添加数据同时,每个数据还维护两个引用, 记录了此数据前一个数据和后一个数据

    • 优:频繁的遍历操作,效率高

    ThreeSet

    • 有序,唯一): 红黑树(自平衡的排序二叉树

    向TreeSet添加的数据,要求是相同的类型的

    • 自定义类需要:指定排序规则

      自然排序: 定义类继承 Comparable 重写 compareTo(obj);

      **定制排序: ** TreeSet在new 时就传入一个 Comparator类对象

    List数据去重

    五种有效方法:

    1. [方案一:借助HashSet的特性进行去重]
    2. [方案二 : 利用set集合特性保持顺序一致去重] TreeSet LinkedHashSet
    3. [方案三 : 使用list自身方法remove()–>不推荐] 循环遍历判断,remove()
    4. [方案四 : 使用Java8特性去重]
    /** 方案一 */
    @org.junit.Test
        public  void distinct() {
        List<String> lists = Arrays.asList(new String[]{"AA", "BB", "CC","CC"});
        HashSet<String> sets = new HashSet<>();
        for (String s : lists) {
            sets.add(s);
        }
        System.out.println("去重后的Set集合: "+sets);
    }
    
    @org.junit.Test
        /** 方案二 */
        public  void distinct2() {
        List<String> lists = Arrays.asList(new String[]{"AA", "BB", "CC","CC"});
        //方式一
        List<String> listNew = new ArrayList<String>(new TreeSet<String>(lists));
        System.out.println("TreeSet"+listNew);
        //方式二
        List<String> listNew2 = new ArrayList<String>(new LinkedHashSet<String>(lists));
        System.out.println("LinkedHashSet"+listNew);
    }
    
    @org.junit.Test
        /** 方案四 */
        public  void distinct4() {
        List<String> lists = Arrays.asList(new String[]{"AA", "BB", "CC","CC"});
        List<String> myList = lists.stream().distinct().collect(Collectors.toList());
        System.out.println("JDK8.0新特性:"+myList);
    }
    

    Iterator 迭代器接口

    Iterator 中:迭代器 读:艾特瑞特~

    可以通过,Collection类的 .iterator() 方法返回一个 Iterator迭代器对象

    • Map中存在keySet()方法返回Set类型对象,Set接口实现Collection 因此间接存在 iterator(); 方法....

    Iterator接口的方法

    boolean hasNext();  //判断是否 存在下一个可访问的元素;
    Object  next();     //返回要访问的下一个元素;
    void    remove();   //移除当前的集合元素可保证从源集合中安全地删除对象;  注意(图:Iterator移除)
    
    • 迭代器是通过 集合.iterator() 方法获取 表示对该集合的迭代

    • 迭代器存在 游标的概念:

    • 20210411153710844
    • 默认游标都在集合的第一个元素之前因此:通过Next(); 获取下一个元素!

    • 注意: 在调用it.next()方法之前必须要调用it.hasNext()进行检测

    若不调用,且下一条记录无效直接调用 i.next()会抛出 NoSuchElementException异常。

    202104111540176

    Iterator 仅用于遍历集合

    边遍历边修改 集合 的唯一正确方式是使用 Iterator.remove() 方法,如下:

    Iterator<Integer> it = list.iterator();
    while(it.hasNext()){
       *// do something*
       it.remove();
    }
    

    一种最常见的错误代码如下:

    for(Integer i : list){
       list.remove(i)
    }
    
    • 运行以上错误代码会报 ConcurrentModificationException 异常
    • 这是因为当使用 foreach(for(Integer i : list)) 语句时,会自动生成一个iterator 来遍历该 list
    • 但同时该 list 正在被 .remove() 修改。Java 一般不允许一个线程在遍历 Collection 时另一个线程修改它

    Map 接口:

    20210425201023880

    简介

    双列集合,存储一对 key-value 数据---》

    • **K键:无序 唯一 **
    • V值:无序 重复;

    HashMap:作为Map的主要实现类;线程不安全的,效率高;允许存储null的key和value

    • LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历

      原因: 在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素

      对于频繁的遍历操作,此类执行效率高于HashMap

    TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序 底层使用红黑树

    Hashtable:作为古老的实现类

    • 线程安全的,效率低;不能存储null的key和value
    • Properties: 常用来处理配置文件。key和value都是String类型

    实现类有:

    • HashMap LinkedHasMap TreeMap Hashtable(jdk1.0) Properties

    Map常用方法:

    Object  .put(Object key,Object value);  //以 键值对 方式存储  注意(键 唯一 值 可以重复 //相同的键 后面的替代前面的 键值对 元素)    
    Object  .get(key);                      //根据键 返回对应的值对象不存在对应键 返回 null;
    Object  .remove(key);                   //根据键 删除指定 键值对 对象
    int     .size();                        //返回元素个数
    boolean .containsKey( object key );     //判断 是否存在 指定键值对 对象;
    boolean .isEmpty();                     //判断 是否存在 键值对 对象
    void    .clear();                       //清空该集合所有 元素;
    Set     .keySet();                      //返回 键的集合 Set类型集合;    set是Collection 的实现类, 通过 .iterator(); 方法返回一个Iterator对象,进行遍历!
    Collection  .values();                  //返回 值的集合 collection 类型集合;
    

    实例:

    @Test
    public void mapTest(){
        Map map = new HashMap();       //只能存储 K 唯一, 值可以重复的元素,重复的key 会替代之前的数据!
        map.put(1, "AA");
        map.put(1, "AA");
        map.put(1, "BB");
        map.put(2, "AA");
        map.put(2, "BB");
        map.put(3, "CC");
    
        System.out.println("遍历所有的key-value");
        System.out.println("方式一: entrySet()");
        Set entrySet = map.entrySet();                  //获取所有的: entrySet()方法返回一个实现Map.Entry接口的对象集合
        Iterator iterator1 = entrySet.iterator();       //set 属于 collection.iterator(); 获取迭代器!
        while (iterator1.hasNext()){
            Object obj = iterator1.next();
            Map.Entry entry = (Map.Entry) obj;
            System.out.println(entry.getKey() + "---->" + entry.getValue());
        }
        System.out.println();
        System.out.println("方式二: 迭代器");
        //方式二:
        Set keySet = map.keySet();
        Iterator iterator2 = keySet.iterator();
        while(iterator2.hasNext()){
            Object key = iterator2.next();
            Object value = map.get(key);
            System.out.println(key + "=====" + value);
    
        }
        System.out.println();
        System.out.println("方式三: 通过ForEach循环进行遍历: 此数省略~");
        System.out.println();
        System.out.println("方式四: 通过Java8 Lambda表达式遍历:");
        map.forEach((k, v) -> System.out.println("key: " + k + " value:" + v));
    
        System.out.println();
        System.out.println("根据Key 寻找value="+map.get(1));
    
        System.out.println("remove(Object key) 根据key移除元素~");
    
        System.out.println();
        System.out.println("boolean containsKey(Object key): 是否包含指定的key"+map.containsKey(1));
    
        System.out.println();
        System.out.println("map.clear()   清空map,与map = null操作不同");
        System.out.println("map.isEmpty() 判断map是否为空");
    }
    

    Map.Entry的定义

    Map的entrySet()方法返回一个实现Map.Entry接口的对象集合

    • 集合中每个对象都是底层Map中一个特定的键/值对 通过这个集合的迭代器 获得每一个条数据的键或值

    Map.Entry中的常用方法

    • Object getKey(): 返回条目的关键字
    • Object getValue(): 返回条目的值
    • Object setValue(Object value): 将相关映像中的值改为value,并且返回旧值

    常见面试题:

    集合源码解析

    HashMap的底层实现原理?

    在 JDK 1.7 中 HashMap 是以数组加链表的形式组成的

    JDK 1.8 之后新增了红黑树的组成结构,当链表大于 8 并且容量大于 64 时,链表结构会转换成红黑树结构

    HashMap 基于 Hash 算法实现的:

    • 当我们往Hashmap 中 put 元素时

    • 首先计算 keyhash值,这里调用了 hash方法

      hash方法实际是让key.hashCode()key.hashCode()>>>16进行, 异或操作 目的是减少碰撞

      >>> 无符号右移 3>>>1: 3无符号右移一位等于 3/2=1 (三 除 二的一次幂) 二进制码最高位无论是 0 或1 都0表示,结果为一个正数;
      &   与运算符  二进制码 0: true 1:false 将两个二级制码逐个位 码进行比较,返回成一个新的二级制码; 就是它的结果;
      ^   异运算符  二进制码 0: true 1:false 将两个二级制码逐个位 码进行比较,返回成一个新的二级制码; 就是它的结果;
      |   或运算符  二进制码 0: true 1:false 将两个二级制码逐个位 码进行比较,返回成一个新的二级制码; 就是它的结果;
      
      计算机的每个对象最终都会转义成二进制:
      0101010101001
      而: 与 异 或 就是针对这些二进制操作的运算符
      & 与运算符 
        a &= b; 等同于 a = a & b;      有0为0,否则为1
        0 & 0 = 0;
        1 & 1 = 1;
        1 & 0 = 0;
        
      | 或运算符    
        有1为1,否则为0
        0 | 0 = 0;
        1 | 1 = 1;
        1 | 0 = 1;
        
      ^ 异运算符
        相同为0,不同为1
        0 ^ 0 = 0;
          1 ^ 1 = 0;
          1 ^ 0 = 1;
      异 或 与 就是将两边的对象的 二进制每一个值进行比较,返回一个新的对象~      
      

    我们都知道HashMap 底层实现是: 数组+链表 JDK8: 数组+链表+红黑树

    • 根据K 的 hashCode() 计算出 哈希值 进行取模算法, 得到在一个在数组上的坐标.
    • 判断数组的坐标上是否存在元素, 没有就直接新增, 如果存在则: ③
    • 与该坐标的元素 hash值一样, 则比较两个元素的 equals(); 如果equals() 不同则新增, 如果相同则不新增覆盖该元素!
    • JDK8.0
    • 遍历table[i],判断链表长度是否大于8, 大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作;
    • 插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold,如果超过,进行扩容

    HashMap 和 Hashtable的异同?

    相同:

    • 都是 K-V 存储结构

    • HashMap和HashTable都实现了Serializable接口,因此它支持序列化,实现了Cloneable接口,能被克隆

    不同:

    • HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75
    • HashMap是非线程安全的,只是用于单线程环境下 多线程环境下可以采用concurrent并发包下的concurrentHashMap HashTable是线程安全的
    • HashMap中key和value都允许为null key为null的键值对永远都放在以table[0]为头结点的链表中
    • HashTable在遇到null时,会抛出NullPointerException异常

    HashMap中String、Integer这样的包装类适合作为K?

    String、Integer等包装类的特性能够保证Hash值的不可更改性和计算准确性,能够有效的减少Hash碰撞的几率

    • 都是final类型,即不可变性,保证key的不可更改性,不会存在获取hash值不同的情况

    • 内部已重写了equals()hashCode()等方法,遵守了HashMap内部的规范

    泛型

    JDK5.0新增

    • 是程序设计语言的一种风格或范式 , 泛型允许程序员在强类型程序,编写代码时使用一些特定的[类型]

    • 这种参数类型可以用在:接口方法中,==分别被称为泛型类、泛型接口、泛型方法==

    • 集合中存储的对象 什么都是 Object 类型因此 每次获取 都需要 进行类型判断转换 instanceof

      泛型集合 在集合的基础上 指定存储类型

      List<String>

    相关文章

      网友评论

          本文标题:Java 集合

          本文链接:https://www.haomeiwen.com/subject/hcdmtrtx.html