Java教程

Java SE : 第14章 集合

本文主要是介绍Java SE : 第14章 集合,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

食用前说明:

本文章内容来自B站韩顺平老师的课堂笔记,本人只是将其进行自我整理,内容有所删减,韩顺平老师的B站课程AV号:BV1fh411y7R8

本文章内容来自韩顺平老师的课堂笔记的 第14章 集合,重点学习:ArrayList,Vector,HashSet,HashMap等内容。

本章链接直达

14.1.1 数组和集合

名称长度
数组长度开始必须指定,且不能更改1. 保存必须为同一类型元素
  1. 进行增加 / 删除元素,比较麻烦 | | 集合 | 动态保存任意多个对象 | 1. 提供了一系列方便的操作对象的方法:add、remove、set、get等

  2. 使用集合添加,删除新元素,简洁明了 |

14.2 集合的框架体系

  • Java 的集合类很多,主要分为两大类,如图 :[最好背下来]

 

14.3 Collection 接口和常用方法

14.3.1 Collection 接口实现类的特点

  1. collection 实现子类可以存放多个元素,每个元素可以是 Object

  2. Collection 接口没有直接的实现子类,是通过它的子接口 Set 和 List 来实现的

  • 有些 Collection 的实现类,可以存放重复的元素,有些不可以

  • 有些 Collection 的实现类,有些是有序的 (List),有些不是有序的 (Set)

14.3.2 Collection 接口遍历元素方式 1-使用 Iterator(迭代器)

  1. Iterator对象称为迭代器,主要用于遍历 Collection 集合中的元素。

  2. 所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器。

  3. Iterator 仅用于遍历集合,Iterator 本身并不存放对象。

  • 案例演示

     public static void main(String[] args) {
         Collection col = new ArrayList();
         col.add(new Book("三国演义", "罗贯中", 10.1));
         col.add(new Book("小李飞刀", "古龙", 5.1));
         col.add(new Book("红楼梦", "曹雪芹", 34.6));
         
         //现在老师希望能够遍历 col 集合
         //1. 先得到 col 对应的 迭代器
         Iterator iterator = col.iterator();
         //2. 使用 while 循环遍历
         // while (iterator.hasNext()) {//判断是否还有数据
         // //返回下一个元素,类型是 Object
         // Object obj = iterator.next();
         // System.out.println("obj=" + obj);
         // }
         //老师教大家一个快捷键,快速生成 while => itit
         //显示所有的快捷键的的快捷键 ctrl + j
         while (iterator.hasNext()) {
             Object obj = iterator.next();
             System.out.println("obj=" + obj);
         }
         //3. 当退出 while 循环后 , 这时 iterator 迭代器,指向最后的元素
         // iterator.next();//NoSuchElementException
         //4. 如果希望再次遍历,需要重置我们的迭代器
         iterator = col.iterator();
         System.out.println("===第二次遍历===");
         while (iterator.hasNext()) {
             Object obj = iterator.next();
             System.out.println("obj=" + obj);
         }
     }

14.3.3 Collection 接口遍历对象方式 2-for 循环增强

增强for循环,可以代替 iterator 迭代器,特点:增强for就是简化版的 iterator ,本质一样。只能用于遍历集合或数组。

  • 案例演示

     public static void main(String[] args) {
         List list = new ArrayList();
         list.add(new Dog("小黑", 3));
         list.add(new Dog("大黄", 100));
         list.add(new Dog("大壮", 8));
         
         //先使用 for 增强
         for (Object dog : list) {
             System.out.println("dog=" + dog);
         }
         //使用迭代器
         System.out.println("===使用迭代器来遍历===");
         Iterator iterator = list.iterator();
         while (iterator.hasNext()) {
             Object dog = iterator.next();
             System.out.println("dog=" + dog);
         }
     }

14.4 List 接口和常用方法

  1. List 集合中元素有序 (即添加顺序和取出顺序一致)、且可重复

  1. List 集合中的每个元素都有其对应的顺序素引,即支持索引。

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

  • 案例演示

     //1. List 集合类中元素有序(即添加顺序和取出顺序一致)、且可重复 [案例]
     List list = new ArrayList();
     list.add("jack");
     list.add("tom");
     list.add("mary");
     list.add("hsp");
     list.add("tom");
     System.out.println("list=" + list);
     //2. List 集合中的每个元素都有其对应的顺序索引,即支持索引
     // 索引是从 0 开始的
     System.out.println(list.get(3));//hsp

14.4.1 List 的三种遍历方式 [ArrayList, LinkedList,Vector]

  • 案例演示

     //List 接口的实现子类 Vector LinkedList
     //List list = new ArrayList();
     //List list = new Vector();
     List list = new LinkedList();
     list.add("jack");
     list.add("tom");
     list.add("鱼香肉丝");
     list.add("北京烤鸭子");
     //遍历
     //1. 迭代器
     Iterator iterator = list.iterator();
     while (iterator.hasNext()) {
         Object obj = iterator.next();
         System.out.println(obj);
     }
     System.out.println("=====增强 for=====");
     //2. 增强 for
     for (Object o : list) {
         System.out.println("o=" + o);
     }
     System.out.println("=====普通 for====");
     //3. 使用普通 for
     for (int i = 0; i < list.size(); i++) {
         System.out.println("对象=" + list.get(i));
     }

14.5 ArrayList 底层结构和源码分析

14.5.1 ArrayList 的注意事项

  1. permits all elements, including null,ArrayList可以加入null,并且多个

  2. ArrayList 是由数组来实现数据存储的

  1. ArrayList 基本等同于Vector,除了 ArrayList 是线程不安全 (执行效率高) 看源码。 在多线程情况下,不建议使用 ArrayList

14.5.2 ArrayList 的底层操作机制源码分析(重点,难点.)

  1. ArrayList 中维护了一个 Object 类型的数 elementData。 transient Object[] elementData; //transient 表示瞬间,短暂的,表示该属性不会被序列号

  2. 当创建 ArrayList 对象时,如果使用的是无参构造器,则初始 elementData 容量为0,第1次添加,则扩容 elementData 为10,如需要再次扩容,则扩容elementData 为1.5倍。

  3. 如果使用的是指定大小的构造器,则初始 elementData 容量为指定大小,如果需要扩容,则直接扩容 elementData 为1.5倍。

14.6 Vector 底层结构和源码剖析

14.6.1 Vector 的基本介绍

  1. Vector 类的定义说明

    • public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable

  2. Vector 底层也是一个对象数组,protected Object[] elementData;

  3. Vector 是线程同步的,即线程安全,Vector 类的操作方法带有 synchronized

  • public synchronized E get(int index) { f (index >= elementCount) throw new ArraylndexOutOfBoundsException(index); return elementData(index); }

  1. 在开发中,需要线程同步安全时,考虑使用Vector

14.6.2 Vector 和 ArrayList 的比较

底层结构版本线程安全(同步)效率扩容倍数
ArrayList可变数组jdk1.2不安全,效率高如果有参构造1.5倍
如果是无参
  1. 第一次 10

  2. 从第二次开始按1.5倍扩容 | | Vector | 可变数组Object[] | jdk1.0 | 安全,效率不高 | 如果是无参,默认10,满后,就按2倍扩容 如果指定大小,则每次直接按2倍扩容 |

14.7 LinkedList 底层结构

14.7.1 LinkedList 的全面说明

  1. LinkedList 底层实现了双向链表和双端队列特点

  2. 可以添加任意元素 (元素可以重复),包括 null

  3. 线程不安全,没有实现同步

14.7.2 LinkedList 的底层操作机制

  1. LinkedList 底层维护了一个双向链表

  2. LinkedList 中维护了两个属性 first 和 last 分别指向,首节点和尾节点

  3. 每个节点(Node对象),里面又维护了 prev、next、item 三个属性,其中通过 prev 指向前一个,通过 next 指向后一个节点。最终实现双向链表。

  4. 所以 LinkedList 的元素的添加和删除,不是通过数组完成的,相对来说效率较高

  5. 模拟一个简单的双向链表

14.8 ArrayList 和 LinkedList 比较

底层结构增删的效率改查的效率
ArrayList可变数组较低,通过数组扩容较高
LinkedList双向链表较高,通过链表追加较低

如何选择 ArrayList 和 LinkedList :

  1. 如果我们改查的操作多,选择 ArrayList

  2. 如果我们增删的操作多,选择 LinkedList

  1. 一般来说,在程序中,80%-90%都是查询,因此大部分情况下会选择 ArrayList

  2. 在一个项目中,根据业务灵活选择,也可能这样,一个模块使用的是 ArrayList ,另外一个模块是 LinkedList , 也就是说,要根据业务来进行选择

14.9 Set 接口和常用方法

14.9.1 Set 接口基本介绍

  1. 无序(添加和取出的顺序不一致),没有索引

  2. 不允许重复元素,所以最多包含一个 null

  3. JDK API 中 Set 接口的实现类有:

14.9.2 Set 接口的遍历方式

同 Collection 的遍历方式一次,因为 Set 接口是 Collection 接口的子接口。

  1. 可以使用迭代器

  2. 增强for

  3. 不能使用索引的方式来获取

14.10 Set 接口实现类-HashSet

1. HashSet 实现了 Set 接口

2. HashSet 实际上是 HashMap,看下源码

 public HashSetk() {
     map = new HashMap<>();
 }

3. 可以存放 null 值,但是只能有一个 null

4. HashSet 不保证元素是有序的,取决于hash后,再确定索引的结果。(即,不保证存放元素的顺序和取出顺序一致)

5. 不能有重复元素 / 对象. 在前面 Set 接口使用已经讲过

14.10.1 HashSet 底层机制说明

  • 分析 HashSet 底层是 HashMap,HashMap 底层是 (数组+链表+红黑树)

  • 分析 HashSet 的添加元素底层是如何实现 (hash() + equals())

    1. HashSet 底层是 HashMap

    2. 添加一个元素时,先得到 hash 值 -会转成 -> 索引值

    3. 找到存储数据表 table,看这个索引位置是否已经存放的有元素

    4. 如果没有,直接加入

    5. 如果有,调用 equals 比较,如果相同,就放弃添加,如果不相同,则添加到最后

    6. 在Java8中,如果一条链表的元素个数到达 TREEIFY_THRESHOLD (默认是 8),并且 table 的大小 >= MIN_TREEIFY_CAPACITY (默认64),就会进行树化 (红黑树)

14.11 Set 接口实现类-LinkedHashSet

14.11.1 LinkedHashSet 的全面说明

  1. LinkedHashSet 是 HashSet 的子类

  2. LinkedHashSet 底层是一个 LinkedHashMap ,底层维护了一个 数组+ 双向链表 (hash表 和 双向链表)

  3. LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,同时使用链表维护元素的次序(图),这使得元素看起来是以插入顺序保存的。

  4. LinkedHashSet 不允许添重复元素

14.12 Map 接口和常用方法

14.12.1 Map 接口实现类的特点 [很实用]

  1. Map 与 Collection 并列存在。用于保存具有映射关系的数据:Key-Value

  2. Map 中的 key 和 value 可以是任何引用类型的数据,会封装到 HashMap$Node 对象中

  3. Map 中的 key 不允许重复,原因和 HashSet 一样,前面分析过源码。

  4. Map 中的 value 可以重复

     

  5. Map 的 key 可以为 null ,value 也可以为 null ,注意 key 为 null, 只能有一个;value 为 null ,可以多个。

  6. 常用 String 类作为 Map 的 key

  7. key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到对应的 value

14.13 Map 接口实现类-HashMap

14.13.1 HashMap 小结

  1. Map 接口的常用实现类:HashMap、Hashtable 和 Properties。

  2. HashMap 是 Map 接口使用频率最高的实现类。

  3. HashMap 是以 key-val 对的方式来存储数据(HashMap$Node类型)

  4. key 不能重复,但是值可以重复,允许使用 null 键和 null 值。

  5. 如果添加相同的 key ,则会覆盖原来的 key-val ,等同于修改。(key 不会替换,val 会替换)

  6. 与 HashSet 一样,不保证映射的顺序,因为底层是以 hash 表的方式来存储的。(jdk8的 hashMap 底层 数组+链表+红黑树)

  7. HashMap 没有实现同步,因此是线程不安全的,方法没有做到同步互斥的操作,没有 synchronized

14.13.2 HashMap 底层机制及源码剖析

  1. (k,v)是一个Node 实现了 Map.Entry<K,V>,查看 HashMap 的源码可以看到。

  2. jdk7.0 的 hashmap 底层实现 [数组+链表] ,jdk8.0 底层 [数组+ 链表+红黑树]

  • 扩容机制 [和 HashSet 相同]

    1. HashMap 底层维护了 Node 类型的数组 table ,默认为 null

    2. 当创建对象时,将加载因子 (loadfactor) 初始化为0.75。

    3. 当添加 key-val 时,通过 key 的哈希值得到在 table 的索引。然后判断该索引处是否有元素,如果没有元素直接添加。

      如果该索引处有元素,继续判断该元素的 key 和准备加入的 key 是否相等,如果相等,则直接替换 val;

      如果不相等,需要判断是树结构还是链表结构,做出相应处理。如果添加时发现容量不够,则需要扩容。

    4. 第1次添加,则需要扩容 table 容量为16,临界值 (threshold) 为12(16*0.75)

    5. 以后再扩容,则需要扩容 table 容量为原来的2倍 (32),临界值为原来的2倍,即24,依次类推。

    6. 在Java8中,如果一条链表的元素个数超过 TREEIFY_THRESHOLD(默认是8),并且 table 的大小 >= MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)

14.14 Map 接口实现类-Hashtable

14.14.1 HashTable 的基本介绍

  1. 存放的元素是 键值对 : 即 K-V

  2. hashtable 的键和值都不能为 null,否则会抛出 NullPointerException

  3. hashTable 使用方法基本上和 HashMap 一样

  4. hashTable 是线程安全的(synchronized),hashMap 是线程不安全的

14.14.2 Hashtable 和 HashMap 对比

版本线程安全(同步)效率允许null键null值
HashMap1.2不安全可以
Hashtable1.0安全较低不可以

14.15 Map 接口实现类-Properties

14.15.1 基本介绍

  1. Properties 类继承 Hashtable 类并且实现了 Map 接口,也是使用一种键值对的形式来保存数据。

  2. 他的使用特点和 Hashtable 类似

  3. Properties 还可以用于 从 xxx.properties 文件中,加载数据到 Properties 类对象,并进行读取和修改

  4. 说明:工作后 xxx.properties 文件通常作为配置文件,这个知识点在 IO流举例,有兴趣可先看文章

14.16总结-开发中如何选择集合实现类(记住)

在开发中,选择什么集合实现类,主要取决于业务操作特点,然后根据集合实现类特性进行选择,分析如下:

  1. 先判断存储的类型(一组对象 [单列] 或一组键值对 [双列] )

  2. 一组对象 [单列]:Collection 接口

  • 允许重复:List

    • 增删多:LinkedList [底层维护了一个双向链表]

    • 改查多:ArrayList [底层维护 Object类型的可变数组]

  • 不允许重复:Set

    • 无序:HashSet [底层是HashMap,维护了一个哈希表 即(数组+链表+红黑树)]

    • 排序:TreeSet [老韩举例说明]

    • 插入和取出顺序一致:LinkedHashSet,维护数组+双向链表

  1. 一组键值对 [双列]:Map

  • 键无序:HashMap [底层是:哈希表 jdk7:数组+链表,jdk8:数组+链表+红黑树]

  • 键排序:TreeMap [老韩举例说明]

  • 键插入和取出顺序一致:LinkedHashMap

  • 读取文件: Properties

14.17 Collections 工具类总结

14.17.1 Collections 工具类介绍

  1. Collections 是一个操作 Set、List 和 Map 等集合的工具类

  2. Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作

14.17.2 排序操作:(均为 static 方法)

功能
reverse(List)反转 List 中元素的顺序
shuffle(List)对 List 集合元素进行随机排序
sort(List)根据元素的自然顺序对指定 List 集合元素按升序排序
sort(List, Comparator)根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
swap(List, int, int)将指定 list 集合中的 i 处元素和 j 处元素进行交换

14.17.3 查找、替换

功能
Object max(Collection)根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection,Comparator)根据 Comparator 指定的顺序,返回给定集合中的最大元素
Object min(Collection)根据元素的自然顺序,返回给定集合中的最小元素
Object min(Collection,Comparator)根据 Comparator 指定的顺序,返回给定集合中的最小元素
int frequency(CollectionObject)返回指定集合中指定元素的出现次数
void copy(List dest,List src)将src中的内容复制到dest中
boolean replaceAll (List list,Object oldVal,Object newVal)使用新值替换 List 对象的所有旧值

这篇关于Java SE : 第14章 集合的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!