• Java-Day19 Java集合(集合框架、Collection接口、List接口及List接口实现类)


    目录

    1. Java集合框架概述

    1.1 什么是集合

    1.2 集合框架的由来

    2. Collection接口常用功能

    2.1 Collection接口方法

    2.2 Iterator迭代器接口

    2.2.1 遍历iterater中元素

    2.2.3 使用 foreach 循环遍历集合元素

    3. Collection子接口之一:List接口

    3.1 List接口特有方法

    3.2 List实现类之一:ArrayList

    3.3 List实现类之二:LinkedList

    3.3.1 LinkedList新增方法

    3.3.2 LinkedList的底层实现原理

    3.4 List 实现类之三:Vector

    3.4.1 Vector新增方法

    3.4.2 ArrayList和LinkedList的异同

    3.4.3 ArrayList和Vector的区别


    1. Java集合框架概述

    1.1 什么是集合

           一方面,面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象的操作,就要对对象进行存储。另一方面,使用Array存储对象方面具有一些弊端,而Java 集合就像一种容器,可以动态地把多个对象的引用放入容器中。

    集合框架,设计好了大量的好用的数据结构:

    线性表:数组、栈、队列、链表,哈希表,树,图:多维结构,矩阵

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

    说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt,.jpg,.avi,数据库中)

    数组在内存存储方面的特点:

    • 数组初始化以后,长度就确定了。

    • 数组声明的类型,就决定了进行元素初始化时的类型

    数组在存储数据方面的弊端:

    • 数组初始化以后,长度就不可变了,不便于扩展

    • 数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,且效率不高。 同时无法直接获取存储元素的个数

    • 数组存储的数据是有序的、可以重复的。====>存储数据的特点单一

    Java 集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组。解决数组存储数据方面的弊端。

    集合的使用场景

    应用在各种应用的数据列表中,如下图,等等

    在Android客户端,将JSON对象或JSON数组 转换为Java对象或Java对 象构成的List

    在服务器端,将Java对象或Java对象构 成的List转换为JSON对象 或JSON数组,

    在数据库端拿到的是List对象

    集合貌似看起来比较强大,它啥时用呢?当当对象多的时候,先进行存储。

    1.2 集合框架的由来

    集合本身是一个工具,它存放在java.util包中。

    JDK最早的1.0版本中。提供的集合容器很少。升级到1.2版,为了更多的需求,出现了集合框架。有了更多的容器。可以完成不同的需求。

    这些容器怎么区分?区分的方式:每一个容器的数据结构(数据存储的一种方式)不一样。

    Java 集合框架图:

    从上面的集合框架图可以看到,Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。Collection 接口又有 3 种子类型,List、Set 和 Queue,再下面是一些抽象类,最后是具体实现类,常用的有ArrayList 、LinkedList、HashSet、LinkedHashSet、HashMap 、LinkedHashMap 等等。

    集合框架是一个用来代表和操纵集合的统一架构。所有的集合框架都包含如下内容:

    • 接口:是代表集合的抽象数据类型。例如 Collection、List、Set、Map 等。之所以定义多个接口,是为了以不同的方式操作集合对象

    • 实现(类):是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构,例如:ArrayList、LinkedList、HashSet、HashMap。

    • 算法:是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现。

    除了集合,该框架也定义了几个 Map 接口和类。Map 里存储的是键/值对。尽管 Map 不是集合,但是它们完全整合在集合中。

    集合框架体系如图所示:

    不同的线性容器进行不断的向上抽取,最后形成了一个集合框架,这个框架就是Collection接口。在 Collection接口定义着集合框架中最最共性的内容。在学习时:我们需要看最顶层怎么用, 创建底层对象即可。因为底层继承了父类中的所有功能。

    Collection接口继承树:

    Map接口继承树

    2. Collection接口常用功能

    Collection 接口是 List、Set 和 Queue 接口的父接口,该接口里定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。

    JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如:Set和List) 实现。

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

    • List:元素有序、可重复的集合,“动态”数组 --- 线性表

    • Queue:非连续、非顺序的存储结构 --- 队列

    • Set:元素无序、不可重复的集合 --- 哈希表

      • HashSet、TreeSet、LinkedHashSet

    在 JDK 5.0 之前,Java 集合会丢失容器中所有对象的数据类型,把所有对象都当成 Object 类型处理;从 JDK 5.0 增加了泛型以后,Java 集合可以记住容器中对象的数据类型。

    2.1 Collection接口方法

    1、添加

    • add(Object obj)
    • addAll(Collection coll)

    2、获取有效元素的个数

    • int size()

    3、清空集合

    • void clear()

    4、是否是空集合

    • boolean isEmpty()

    5、是否包含某个元素

    • boolean contains(Object obj):是通过元素的equals方法来判断是否是同一个对象
    • boolean containsAll(Collection c):也是调用元素的equals方法来比较的。拿两个集合的元素挨个比较。

    6、删除

    • boolean remove(Object obj) :通过元素的equals方法判断是否是要删除的那个元素。只会删除找到的第一个元素
    • boolean removeAll(Collection coll):取当前集合的差集

    7、取两个集合的交集

    • boolean retainAll(Collection c):把交集的结果存在当前集合中,不影响c

    8、集合是否相等

    • boolean equals(Object obj)

    9、转成对象数组

    • Object[] toArray()

    10、获取集合对象的哈希值

    • hashCode()

    11、遍历

    • iterator():返回迭代器对象,用于集合遍历
    1. package day19.exer;
    2. import java.util.*;
    3. public class CollectionTest {
    4. // 压制警告
    5. @SuppressWarnings({"rawtypes", "unchecked"})
    6. public static void main(String[] args) {
    7. // Collection是一个接口,不能创建实例
    8. Collection coll = new ArrayList();
    9. Collection coll1 = new ArrayList(2);
    10. coll1.add(12);
    11. coll1.add(21);
    12. // 添加一个元素
    13. coll.add("AA");
    14. coll.add(1000); // 自动装箱
    15. coll.add(new Date());
    16. // 添加另一个集合的元素
    17. coll.addAll(coll1);
    18. System.out.println(coll); // [AA, 123, Fri Aug 05 22:41:50 GMT+08:00 2022, 12, 21]
    19. // 获取有效元素的个数
    20. System.out.println(coll.size()); // 5
    21. // 清空集合
    22. coll1.clear();
    23. System.out.println(coll1); // []
    24. // 是否是空集合
    25. System.out.println(coll1.isEmpty()); // true
    26. // 是否包含某个元素
    27. System.out.println(coll.contains(new String("AA")));
    28. // true, 这里包含的原因是调用String中的equals, 比较两个字符串的值是否相等
    29. coll.add(new Person("小王", 12));
    30. System.out.println(coll.contains(new Person("小王", 12)));
    31. // false, 这里不包含的原因是调用元素的equals方法,比较的两个的内存地址
    32. System.out.println(coll.containsAll(coll1)); // true, 空集合默认包含在另一个集合中
    33. coll1.add(111);
    34. System.out.println(coll.containsAll(coll1)); // false
    35. // 删除元素
    36. coll.add("AA");
    37. System.out.println(coll); // [AA, 1000, Fri Aug 05 23:16:39 GMT+08:00 2022, 12, 21, day19.exer.Person@16795f1, AA]
    38. coll.remove("AA");
    39. System.out.println(coll); // [1000, Fri Aug 05 23:16:39 GMT+08:00 2022, 12, 21, day19.exer.Person@16795f1, AA]
    40. Collection coll2 = new ArrayList(2);
    41. coll2.add(1000);
    42. coll2.add("AA");
    43. coll.removeAll(coll2);
    44. System.out.println(coll); // [Fri Aug 05 23:16:39 GMT+08:00 2022, 12, 21, day19.exer.Person@16795f1]
    45. // 取两个集合的交集
    46. coll.add(111);
    47. System.out.println(coll); // [Fri Aug 05 23:24:44 GMT+08:00 2022, 12, 21, day19.exer.Person@16795f1, 111]
    48. coll1.add(222);
    49. System.out.println(coll1); // [111, 222]
    50. System.out.println(coll.retainAll(coll1)); // true
    51. System.out.println(coll); // [111], retainAll把交集的结果存在当前集合coll中,不影响coll1
    52. System.out.println(coll1); // [111, 222]
    53. // 集合是否相等
    54. System.out.println(coll.equals(coll1)); // false
    55. coll.add(222);
    56. System.out.println(coll); // [111, 222]
    57. System.out.println(coll.equals(coll1)); // true
    58. // 转成对象数组
    59. Object[] objArr = coll2.toArray();
    60. System.out.println(Arrays.toString(objArr)); // [1000, AA]
    61. // 获取集合对象的哈希值
    62. System.out.println(coll.hashCode()); // 4624
    63. // 遍历
    64. // foreach
    65. for (Object o : coll) {
    66. System.out.print(o + ", "); // 111, 222,
    67. }
    68. // 迭代器遍历迭代数据,foreach底层就是使用迭代器实现的
    69. Iterator iterator = coll.iterator();
    70. while (iterator.hasNext()) {
    71. System.out.print(iterator.next() + ", "); // 111, 222,
    72. }
    73. }
    74. }
    75. class Person {
    76. public String name;
    77. public int age;
    78. public Person(String name, int age) {
    79. this.name = name;
    80. this.age = age;
    81. }
    82. public Person() {
    83. }
    84. @Override
    85. public boolean equals(Object o) {
    86. System.out.println(o + " 调用Person对象的equals方法");
    87. return this == o;
    88. }
    89. @Override
    90. public int hashCode() {
    91. return Objects.hash(name, age);
    92. }
    93. }

    集合的使用细节:

    • 集合中,不能保存基本数据类型!如果一定要使用基本数据类型,但是jdk1.5以后可以这么写,则需要使用它们的包装类,所以存储的还是对象(基本数据类型包装类对象)。

    • 集合中存储其实都是对象的地址。

    • 删除基本数据类型,需要手动装箱,否则,int默认是下标,而不是数据本书

    • 向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()

    • 存储时提升了Object。取出时要使用元素的特有内容,必须向下转型。

    2.2 Iterator迭代器接口

    Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素

    GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元 素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生。类似于“公 交车上的售票员”、“火车上的乘务员”、“空姐”。

    Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,那么所 有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了 Iterator接口的对象。

    Iterator 仅用于遍历集合,Iterator 本身并不提供承装对象的能力。如果需要创建 Iterator 对象,则必须有一个被迭代的集合。

    集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合 的第一个元素之前。

    Iterator迭代器接口中的方法:

    • boolean hasNext():判断Iterator中是否存在下一个元素

    • E next():返回Iterator中的下一个元素

    • void remove():从基础集合中移除迭代器返回的最后一个元素(可选操作)。

    2.2.1 遍历iterater中元素

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

    在调用it.next()方法之前必须要调用it.hasNext()进行检测。若不调用,且下一条记录无效,直接调用it.next()会抛出NoSuchElementException异常。

    注意:

    • Iterator可以删除集合的元素,但是是遍历过程中通过迭代器对象的remove方 法,不是集合对象的remove方法。

    • 如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法, 再调用remove都会报IllegalStateException。

    2.2.3 使用 foreach 循环遍历集合元素

    Java 5.0 提供了 foreach 循环迭代访问 Collection和数组。

    遍历操作不需获取Collection或数组的长度,无需使用索引访问元素。

    遍历集合的底层调用Iterator完成操作。

    foreach还可以用来遍历数组。

    语法格式:元素类型

    1. // for (要遍历的元素类型 自定义的元素名称 : 要遍历的结构对象名称)
    2. for (Object o : coll) {
    3. System.out.print(o);
    4. }

    3. Collection子接口之一:List接口

    鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组

    List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引

    List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素

    JDK API中List接口的实现类常用的有:ArrayList、LinkedList和Vector

    3.1 List接口特有方法

    List除了从Collection集合继承的方法外,List 集合里添加了一些根据索引来操作集合元素的方法。

    • 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 位置的子集合

    1. public class ListText {
    2. public static void main(String[] args) {
    3. List l1 = new LinkedList();
    4. List l2 = new Vector();
    5. List l3 = new ArrayList();
    6. l3.add(",happy ");
    7. l3.add("new ");
    8. l3.add("years !");
    9. l3.add(0,"马克");
    10. Iterator it1 = l3.iterator();
    11. while (it1.hasNext()){
    12. Object obj1 = it1.next();
    13. System.out.print(obj1);
    14. }
    15. System.out.println();
    16. System.out.println(l3.get(0)+"?");
    17. l3.add(",happy ");
    18. System.out.println(l3.indexOf(",happy "));
    19. System.out.println(l3.lastIndexOf(",happy "));
    20. l3.remove(4); //删除下标为4的元素
    21. Iterator it2 = l3.iterator();
    22. while (it2.hasNext()){
    23. Object obj2 = it2.next();
    24. System.out.print(obj2);
    25. }
    26. System.out.println();
    27. l3.set(3,"years!!");
    28. Iterator it3 = l3.iterator();
    29. while (it3.hasNext()){
    30. Object obj3 = it3.next();
    31. System.out.print(obj3);
    32. }
    33. }
    34. }

    3.2 List实现类之一:ArrayList

    ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。ArrayList 继承了 AbstractList ,并实现了 List 接口。

    ArrayList 类位于 java.util 包中,使用前需要引入它,语法格式如下:

    1. import java.util.ArrayList; // 引入 ArrayList 类
    2. ArrayList objectName =new ArrayList<>();  // 初始化
    • E: 泛型数据类型,用于设置 objectName 的数据类型,只能为引用数据类型
    • 对象名称: 对象名。

    ArrayList 是 List 接口的典型实现类、主要实现类;是线程不安全的,效率高;底层使用Object[] elementData存储

    本质上,ArrayList是对象引用的一个”变长”数组

    ArrayList的JDK1.8之前与之后的实现区别?

    • JDK1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组

    • JDK1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元 素时再创建一个始容量为10的数组

    Arrays.asList(…) 方法返回的 List 集合,既不是 ArrayList 实例,也不是 Vector 实例。

    Arrays.asList(…) 返回值是一个固定长度的 List 集合

    List与数组间的转换:

    1. // 集合 --->数组:toArray()
    2. Object[] arr = coll.toArray();
    3. for(int i = 0;i < arr.length;i++){
    4. System.out.println(arr[i]);
    5. }
    6. // 拓展:数组 --->集合:调用Arrays类的静态方法asList(T ... t)
    7. List list = Arrays.asList(new String[]{"AA", "BB", "CC"});
    8. System.out.println(list);
    9. List arr1 = Arrays.asList(new int[]{123, 456});
    10. System.out.println(arr1.size());//1
    11. List arr2 = Arrays.asList(new Integer[]{123, 456});
    12. System.out.println(arr2.size());//2

    常用方法:

    • 增: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循环;③ 普通的循环

    1. List list = new ArrayList();
    2. list.add("AA");
    3. list.add(123); // 存储基本数据时,会自动装箱
    4. list.add(1);
    5. list.add(2);
    6. list.add(0, "BB");
    7. list.add(1, "呵呵呵");
    8. System.out.println(list);
    9. System.out.println(list.get(0));
    10. System.out.println(list.get(1));
    11. System.out.println(list.remove("AA"));
    12. System.out.println(list);
    13. // 更新值
    14. System.out.println(list.set(0, "test"));
    15. System.out.println(list);
    16. // 注意:删除基本数据类型,需要手动装箱
    17. // 否则,int默认是下标,而不是数据本书
    18. System.out.println(list.remove(Integer.valueOf(2)));
    19. System.out.println(list);
    20. System.out.println(list.contains("呵呵呵"));
    21. // 使用泛型,表示nums只能存储字符串
    22. List nums = new ArrayList<>();
    23. nums.add("张三");
    24. nums.add("李四");
    25. nums.add("王五");
    26. nums.add("赵六");
    27. // System.out.println(nums.get(0));
    28. // 遍历list中的数据
    29. // for (int i = 0; i < nums.size(); i++) {
    30. // System.out.println(nums.get(i));
    31. // }
    32. // foreach
    33. // for (String str : nums) {
    34. // System.out.println(str);
    35. // }
    36. // 迭代器遍历迭代数据,foreach底层就是使用迭代器实现的
    37. // Iterator it = nums.iterator();
    38. // while(it.hasNext()) {
    39. // System.out.println(it.next());
    40. // }

    3.3 List实现类之二:LinkedList

    链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址。

    对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高,底层使用双向链表存储

    3.3.1 LinkedList新增方法

    • void addFirst(Object obj)

    • void addLast(Object obj)

    • Object getFirst()

    • Object getLast()

    • Object removeFirst()

    • Object rem oveLast()

    3.3.2 LinkedList的底层实现原理

    LinkedList:双向链表,内部没有声明数组,而是定义了Node类型的first和last, 用于记录首末元素。同时,定义内部类Node,作为LinkedList中保存数据的基本结构。Node除了保存数据,还定义了两个变量:

    • prev变量记录前一个元素的位置

    • next变量记录下一个元素的位置

    1. private static class Node {
    2. E item;
    3. Node next;
    4. Node prev;
    5. Node(Node prev, E element, Node next) {
    6. this.item = element;
    7. this.next = next;
    8. this.prev = prev;
    9. }
    10. }

    3.4 List 实现类之三:Vector

    Vector 是一个古老的集合,JDK1.0就有了。大多数操作与ArrayList 相同,区别之处在于Vector是线程安全的。

    在各种list中,最好把ArrayList作为缺省选择。当插入、删除频繁时, 使用LinkedList;Vector总是比ArrayList慢,所以尽量避免使用。

    3.4.1 Vector新增方法

    • void addElement(Object obj)

    • void insertElementAt(Object obj,int index)

    • void setElementAt(Object obj,int index)

    • void removeElement(Object obj)

    • void removeAllElements()

    面试题:请问ArrayList/LinkedList/Vector的异同?谈谈你的理解?ArrayList底层 是什么?扩容机制?Vector和ArrayList的最大区别?

    3.4.2 ArrayList和LinkedList的异同

    二者都线程不安全,相对线程安全的Vector,执行效率高。 此外,ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。对于 随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。对于新增 和删除操作add(特指插入)和remove,LinkedList比较占优势,因为ArrayList要移动数据。

    3.4.3 ArrayList和Vector的区别

    Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于 强同步类。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用 ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。Vector每次扩容请求其大 小的2倍空间,而ArrayList是1.5倍。Vector还有一个子类Stack。

  • 相关阅读:
    【计算轨迹误差】
    06 【函数(下)】
    《MATLAB科研绘图与学术图表绘制从入门到精通》示例:绘制伊甸火山3D曲面图
    Go-Excelize API源码阅读(十六)——GetSheetViewOptions、SetPageLayout
    css知识点总结1
    【无人机协同】基于matlab无人飞行器协同车辆物资配送【含Matlab源码 1899期】
    javaScript能做什么
    Unity --- UI --- 画布准备
    ST-link/V2引脚定义和更新固件说明
    《艾尔登法环 黄金树幽影》是什么?Mac电脑怎么玩《艾尔登法环》艾尔登法环下载
  • 原文地址:https://blog.csdn.net/weixin_51612062/article/details/126185094