• JavaSE基础day14集合



    一. 集合概述

    (一) 集合和数组的区别

    1. 相同点: 两者都是数据存储容器,可以存储多个数据

    2. 不同点:

      数组:

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

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

              int[] arr = new int[3];   String[] arr1 = new String[5];

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

    (二) 集合体系结构

    List :

      1. 元素存取有序

      2. 元素有索引

      3. 存储的元素可以重复

     

    Set:

      1. 元素存取无序

      2. 没有索引

      3. 不存储重复元素

    二. 单列集合体系的详细讲解

    (一) 顶层接口Collection常用功能

    1. 概述: Collection是单列集合的顶层接口,定义的是所有单列集合中共有的功能. JDK不提供此接口的任何直接实现.它提供更具体的子接口(如Set和List)实现.

    2. 创建Collection集合的对象:

      Collection接口,不能直接创建对象,因此找一个实现类ArrayList创建对象

      1) 多态: 多态的方式 -- 父类引用指向子类对象 Collection c = new ArrayList();

      2) 具体的实现类创建 -- 本类引用指向本类对象 ArrayList a = new ArrayList();

    3. Collection集合常用方法:

      1) boolean add(Object e): 添加元素

      2) boolean remove (Object o): 从集合中移除指定的元素

      3) void clear(): 清空集合中的元素

      4) boolean contains(Object o): 判断集合中是否存在指定的元素

      5) boolean isEmpty(): 判断集合是否为空(集合存在,没有元素), 如果集合为空, 那么返回true, 如果集合不为空 false

      6) int size(): 返回集合中元素的数量,返回集合的长度。

      7) Object[] toArray(): 返回集合的对象的数组

    import java.util.ArrayList;
    import java.util.Collection;
    
    public class CollectionMethod {
        public static void main(String[] args) {
            Collection c = new ArrayList();
            // 一个集合中只存储相同的数据类型
            // 1. boolean add(Object e): 添加元素
            c.add("abc");
            c.add("hello");
            c.add("he");
            c.add("hi");
            System.out.println(c);// [abc, hello, he, hi]
    
            // 2. boolean remove (Object o): 从集合中移除指定的元素
            c.remove("hello");
            System.out.println(c);// [abc, he, hi]
            c.remove("world");
            System.out.println(c);// [abc, he, hi]
    
            // 3. boolean contains(Object o): 判断集合中是否存在指定的元素
            System.out.println(c.contains("abc"));// true
            System.out.println(c.contains("world"));// false
    
            // 4.boolean isEmpty(): 判断集合是否为空(集合存在,没有元素),
            // 如果集合为空, 那么返回true, 如果集合不为空 false
            System.out.println(c.isEmpty());// fasle
            System.out.println(c.size());// 3
    
            // 5. void clear(): 清空集合中的元素
            c.clear();
    
            System.out.println(c);// []
            System.out.println(c.isEmpty());// true
    
            // 6. int size(): 返回集合中元素的数量,返回集合的长度
            System.out.println(c.size());// 0
        }
    }

    toArray遍历

    import java.util.ArrayList;
    import java.util.Collection;
    public class ConnectionToArray遍历 {
        public static void main(String[] args) {
            Collection c = new ArrayList();
            c.add(15);
            c.add(1);
            c.add(-8);
            System.out.println(c);
    
            // 1. 将集合容器c转换成一个数组容器
            Object[] objArr = c.toArray();
            // 2. 遍历数组相当于间接遍历容器
            for(int index = 0; index < objArr.length; index++){
                Object obj = objArr[index];
                Integer i = (Integer)obj;
                System.out.println(i);
            }
        }
    }

    (二) 单列集合的遍历

    2.1迭代器遍历

    通过迭代器添加add()会在当前位置后加入值

    1. 迭代: 更新换代, 从一个到下一个概念, 迭代相当于遍历的含义

    2. 迭代器: 专门对于单列集合进行遍历的对象, 称为迭代器

    3. 获取集合迭代器的方法

            Iterator<E> iterator(): 返回此集合中元素的迭代器,通过集合对象的iterator()方法得到

           方法的返回值结果是Iterator接口, 证明这个方法实际返回的应该是这个接口的实现类对象

    4. Iterator中的常用方法:

            - boolean hasNext(): 判断当前位置是否有元素可以被取出

            - E next(): 获取当前位置的元素,将迭代器对象移向下一个索引位置

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

    hashNext和next功能的运行方式:

     

    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Iterator;
    
    public class Connection迭代器遍历 {
        public static void main(String[] args) {
            Collection c = new ArrayList();
            c.add(3.14);
            c.add(2.1);
            c.add(-0.3);
            c.add(99.0);
    
            // 1. 获取到c集合的迭代器对象
            Iterator it = c.iterator();
            // 2. 循环获取出集合c中的每一个元素
            // hasNext() : 判断集合中是否还有下一个元素
            while(it.hasNext()){
                // 获取出集合中的元素
                Object obj = it.next();
                Double d = (Double)obj;
                System.out.println(d);
            }
    
            // System.out.println(it.next());
    
            /*System.out.println(it.next());
            System.out.println(it.next());
            System.out.println(it.next());
            System.out.println(it.next());
            // NoSuchElementException : 将集合中所有元素都遍历完毕,还想继续向下获取元素,报出次异常
            // 没有这个元素异常
            System.out.println(it.next());*/
        }
    }

    2.2增强for(foreach)遍历

    1.概述:

      1) 增强for是JDK5之后出现的,其内部原理是一个Iterator迭代器

      2) 实现Iterable接口的类才可以使用迭代器和增强for简化数组和Collection集合遍历

     2.格式:

            for(集合/数组中元素的数据类型 变量名 : 集合/数组名){

           // 已经将当前遍历到的元素封装到变量中了,直接使用变量即可

            }

    import java.util.ArrayList;
    import java.util.Collection;
    
    public class Collection增强for遍历 {
        public static void main(String[] args) {
            Collection c = new ArrayList();
            c.add(15);
            c.add(1);
            c.add(-8);
    
            /*
               for(数据类型  变量名 : 遍历的集合或者数组){
               }
               数据类型就是集合或者数组中存储的数据类型; 变量名表示每一次循环中, 依次表达集合或数组中的每一个元素
             */
    
            for(Object obj : c){
                System.out.println((Integer)obj);
            }
    
            int[] arr = {12,13,14,15};
            for(int i : arr){
                System.out.println(i);
            }
        }
    }

    (三) 有序单列集合List

    3.1概述

    1. 概述: List集合是Collection接口的子接口,其下有两个常用实现类分别为 ArrayList 和 LinkedList

    2. List集合特点:

      1) 有序:元素存入集合与从集合中取出的顺序一致

      2) 有索引:每个元素都有自己的索引编号,从0开始,到集合长度-1为止

      3) 元素可以重复:即使是值相同的几个元素,位置和索引也各不相同,可以区分这几个值

    3. List集合的特有方法:

      1) void add(int index,E element): 在此集合中的指定位置插入指定的元素

      2) E remove(int index): 删除指定索引处的元素,返回被删除的元素

      3) E set(int index,E element): 修改指定索引处的元素,返回被修改的元素

      4) E get(int index):返回指定索引处的元素

     

    4. 针对List集合特有的遍历方式

      可以通过集合的size方法获取list集合索引的范围,根据索引通过get方法可以获取指定索引的值。

    特有方法代码

    import java.util.ArrayList;
    import java.util.List;
    public class ListSpecialMethod {
        public static void main(String[] args) {
            List list = new ArrayList();
            list.add(12);
            list.add(99);
            list.add(1);
            System.out.println(list);// [12, 99, 1]
    
           // 1)void add(int index,E element): 在此集合中的指定位置插入指定的元素
            list.add(2,22);// [12, 99, 22, 1]
            System.out.println(list);
    
            // 2)E remove(int index): 删除指定索引处的元素,返回被删除的元素
            Object obj = list.remove(1);
            System.out.println(obj);// 99
            System.out.println(list);// [12, 22, 1]
    
            //3)E set(int index,E element): 修改指定索引处的元素,返回被修改的元素
            Object obj1 = list.set(0,888);
            System.out.println(obj1);// 12
            System.out.println(list);// [888, 22, 1]
    
            //4)E get(int index):返回指定索引处的元素
            Object obj2 = list.get(2);
            System.out.println(obj2);// 1
        }
    }

    List集合特有遍历方式

    import java.util.ArrayList;
    import java.util.List;
    
    public class List遍历 {
        public static void main(String[] args) {
            List list = new ArrayList();
            list.add(12);
            list.add(99);
            list.add(1);
    
            for(int index = 0; index < list.size(); index++){
                Object obj = list.get(index);
                System.out.println(obj);
            }
        }
    }

    3.2 并发修异常的产生原因和解决办法

    1. ConcurrentModificationException 并发修改异常

     

    2.异常发生原因: 在迭代器遍历过程中使用集合的引用进行元素的添加或删除

    3. 解决方法:

            (1) 通过for循环(索引遍历法)遍历集合,在遍历过程中可以进行添加和删除元素

        (2) 使用迭代器中的remove()方法删除集合元素

    (3) 使用List集合的特有迭代器ListIterator<E>, 通过List中的方法listIterator()获取, 该迭代器允许迭代期间修改列表      

    -- add(E e) 添加元素

            -- remove() 移除元素

     

    案例 : 定义一个List集合, 存储字符串数据”ok”,”java”,”hello”,”world”, 迭代器遍历集合元素, 如果集合中存在”hello”字符串,向集合中添加”end”字符串

    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    import java.util.ListIterator;
    
    public class 并发修改异常 {
        public static void main(String[] args) {
            List list = new ArrayList();
            list.add("ok");
            list.add("java");
            list.add("hello");
            list.add("world");
            // changeList(list);
            // changeListIterator(list);
            changeListFor(list);
        }
    
        // 案例 : 定义一个List集合, 存储字符串数据”ok”,”java”,”hello”,”world”,
        // 迭代器遍历集合元素, 如果集合中存在”hello”字符串,向集合中添加”end”字符串
        // changeList方法会发生并发修改问题
        public static void changeList(List list){
            // 1. 使用迭代器遍历list集合
            Iterator it = list.iterator();
            while(it.hasNext()){
                String str = (String)it.next();
                if("hello".equals(str)){
                    // ConcurrentModificationException
                    // 并发          修改        异常
                    // 发生原因: 如果对于集合一边使用迭代器遍历,一边修改集合中的数据内容,那么可能并发修改异常
                    list.add("end");
                }
            }
            System.out.println(list);
        }
    
        // changeListIterator可以解决并发修改问题
        public static void changeListIterator(List list){
            ListIterator it = list.listIterator();
            while(it.hasNext()){
                String str = (String)it.next();
                if("hello".equals(str)){
                    // 解决并发修改异常的关键: 就是迭代的过程中,不允许通过集合对象修改集合元素(新增,删除)
                    // 必须使用迭代器进行集合中元素的修改, 才能解决并发修改异常问题
                    it.add("end");
                }
            }
            System.out.println(list);
        }
    
        // List集合特有索引遍历方式, 可以解决并发修改问题
        public static void changeListFor(List list){
              for(int index = 0; index < list.size(); index++){
                  String str = (String)list.get(index);
                  if("hello".equals(str)){
                      // list.add(0,"end"); 死循环
                      list.add("end");
                  }
              }
            System.out.println(list);
        }
    
    // 增强for循环也会有并发修改问题: 因为底层也是迭代器实现的
    public static void changeListForEach(List list){
        for(Object obj : list){
            String str = (String)obj;
            if("hello".equals(str)){
                // list.add(0,"end"); 死循环
                list.add("end");
            }
        }
        System.out.println(list);
    }
    }

    (四) 数据结构之栈和队列

    数据结构是计算机存储,组织数据的方式,通常情况下, 精心选择的数据结构可以带来更高的运行和存储效率

    4.1栈

    栈: stack,又称堆栈,它是运算受限的线性表,其限制是仅允许在标的一端进行插入和删除操作,不允许在其他任何位置进行添加、查找、删除等操作。

    特点: 先进后出,(即,存进去的元素,要在后它后面的元素依次取出后,才能取出该元素)。例如,子弹压进弹夹,先压进去的子弹在下面,后压进去的子弹在上面,当开枪时,先弹出上面的子弹,然后才能弹出下面的子弹。

     压栈:就是存元素。即,把元素存储到栈的顶端位置,栈中已有元素依次向栈底方向移动一个位置。

     弹栈:就是取元素。即,把栈的顶端位置元素取出,栈中已有元素依次向栈顶方向移动一个位置。

    4.2队列

       queue,简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行删除。

       特点:

    1) 先进先出(即,存进去的元素,要在它前面的元素依次取出后,才能取出该元素)。例如,小火车过山洞,车头先进去,车尾后进去;车头先出来,车尾后出来。

    2) 队列的入口、出口各占一侧。例如,下图中的左侧为入口,右侧为出口

    4.3数据结构之数组和链表

    1.数组:

      Array:是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。就像是一排出租屋,有100个房间,从001到100每个房间都有固定编号,通过编号就可以快速找到租房子的人。

    特点查找元素快:

            通过可以快速访问指定位置的元素

    增删元素慢:

            指定索引位置增加元素:需要创建一个新数组,将指定新元素存储在指定索引位置,再把原数组元素根据索引,复制到新数组对应索引的位置。如下图

    指定索引位置删除元素:需要创建一个新数组,把原数组元素根据索引,复制到新数组对应索引的位置,原数组中指定索引位置元素不复制到新数组中。如下图

    2.链表:linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。我们常说的链表结构有单向链表与双向链表,那么这里给大家介绍的是单向链表。

    3.简单的说,采用该结构的集合,对元素的存取有如下的特点:

    多个结点之间,通过地址进行连接。例如,多个人手拉手,每个人使用自己的右手拉住下个人的左手,依次类推,这样多个人就连在一起了。

    查找元素慢:想查找某个元素,需要通过连接的节点,依次向后查找指定元素

    增删元素快: 增加元素 --> 只需要修改连接下个元素的地址即可。

    删除元素:只需要修改连接下个元素的地址即可。

     

    4.4 ArrayList和LinkedList对比分析

    1. ArrayList集合:

      (1) 在创建ArrayList集合对象的时候,会维护一个长度为10的Object类型的数组.

      (2) 当插入数据10个长度不够,这时候以1.5倍的形式进行扩容

      (3) 存储特点 : 查询快, 增删慢

    3.LinkedList:集合数据存储的结构是链表结构。方便元素添加、删除的集合。

    LinkedList是一个双向链表,特点 : 查询慢, 增删快, 链表结构不需要连续内存空间, 可以利用内存中零散空间

    实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。这些方法我们作为了解即可:

     

    public void addFirst(E e):将指定元素插入此列表的开头.

    public void addLast(E e):将指定元素添加到此列表的结尾。

    public E getFirst():返回此列表的第一个元素。

    public E getLast():返回此列表的最后一个元素。

    public E removeFirst():移除并返回此列表的第一个元素。

    public E removeLast():移除并返回此列表的最后一个元素。

    import java.util.LinkedList;
    public class LinkedListDemo {
        public static void main(String[] args) {
            LinkedList list = new LinkedList();
            list.addFirst("1");
            list.addFirst("2");
            System.out.println(list);// [2, 1]
    
            list.addLast("end");
            list.addLast("ok");
            System.out.println(list);// [2, 1, end, ok]
    
            list.removeFirst();
            System.out.println(list);// [1, end, ok]
            list.removeLast();
            System.out.println(list);// [1, end]
    
            String str = (String)list.getFirst();
            System.out.println(str);// 1
            Object obj = list.getLast();// Object obj = "end";  String s = (String)obj;
            System.out.println(list.getLast());// end
        }
    }

    案例:

    测试: ArrayList和linkedList首尾增加元素, 首尾删除元素, 首尾获取元素的性能

    import java.util.ArrayList;
    import java.util.LinkedList;
    
    public class ArrayListAndLinkedList比较 {
        public static void main(String[] args) {
            ArrayList list = new ArrayList();
            ArrayListAdd(list);
            LinkedList list1 = new LinkedList();
            LinkedListAdd(list1);
    
            ArrayListGet(list);
            LinkedListGet(list1);
    
            ArrayListDelete(list);
            LinkedListDelete(list1);
        }
        // 测试: ArrayList和LinkedList首尾增加元素, 首尾删除元素, 首尾获取元素的性能
        // 1. 测试ArrayList首尾新增功能
        public static void ArrayListAdd(ArrayList list){// 0x12
             long beginTime = System.currentTimeMillis();
             for(int i = 1; i <= 10000; i++){
                 list.add(0,"first");
                 list.add(list.size()-1,"end");
             }
            long endTime = System.currentTimeMillis();
            System.out.println(endTime - beginTime);
        }
    
        // 2. 测试LinkedList首尾新增功能
        public static void LinkedListAdd(LinkedList list){
            long beginTime = System.currentTimeMillis();
            for(int i = 1; i <= 10000; i++){
                list.addFirst("first");
                list.addLast("end");
            }
            long endTime = System.currentTimeMillis();
            System.out.println(endTime - beginTime);
        }
    
        // 3. 测试ArrayList首尾获取功能
        public static void ArrayListGet(ArrayList list){
            long beginTime = System.currentTimeMillis();
            for(int i = 1; i <= 10000; i++){
                list.get(0);
                list.get(list.size()-1);
            }
            long endTime = System.currentTimeMillis();
            System.out.println(endTime - beginTime);
        }
    
        // 4. 测试ArrayList首尾获取功能
        public static void LinkedListGet(LinkedList list){
            long beginTime = System.currentTimeMillis();
            for(int i = 1; i <= 10000; i++){
                list.getFirst();
                list.getLast();
            }
            long endTime = System.currentTimeMillis();
            System.out.println(endTime - beginTime);
        // 5. 测试ArrayList首尾删除功能
        public static void ArrayListDelete(ArrayList list){
            long beginTime = System.currentTimeMillis();
            for(int i = 1; i <= 10000; i++){
                list.remove(0);
                list.remove(list.size()-1);
            }
            long endTime = System.currentTimeMillis();
            System.out.println(endTime - beginTime);
        }
    
        // 6. 测试ArrayList首尾删除功能
        public static void LinkedListDelete(LinkedList list){
            long beginTime = System.currentTimeMillis();
            for(int i = 1; i <= 10000; i++){
                list.removeFirst();
                list.removeLast();
            }
            long endTime = System.currentTimeMillis();
            System.out.println(endTime - beginTime);
        }
    }
  • 相关阅读:
    小程序工程化探索:大规模场景下的问题和解决方案----------------引用
    对Taro Next小程序跨框架开发的探索与实践-----------------引用
    对Node.js 中的依赖管理------------引用
    对redux的研究--------引用
    JavaScript 中的 for 循环---------------引用
    对JavaScript 模块化的深入-----------------引用
    对Webpack 应用的研究-----------------引用
    webpack5持久化缓存
    设置x 轴斜体(每次我都百度,这次单独为它发一个)
    字典元组列表常用方法
  • 原文地址:https://www.cnblogs.com/lw1095950124/p/16006099.html
Copyright © 2020-2023  润新知