一、Java基础(1)

本章将针对常用的 Java 基础知识展开详细介绍,具体包含 Java 的集合异常分类及处理、反射机制、注解、内部类、泛型、序列化、I/O流这几部分内容。

本章概要

  • 集合
    • List
    • Queue
    • Set
    • Map

1.1 集合

Java 的集合被定义在 Java.util 包中,主要有 4 种集合,分别为 List、Queue、Set、Map,每种集合的具体分类如图。
在这里插入图片描述

1.1.1 List:可重复

List 是很常用的数据类型,是有序的集合,一共有三个实现类,分别是 ArrayList、Vector 和 LinkedList。

  1. ArrayList:数组实现,增删慢,查询快,线程不安全

ArrayList 是使用最广泛的 List 实现类,其内部数据结构基于数组实现,提供了对 List 的增加(add),删除(remove)和访问(get)功能。
ArrayList 的缺点是元素必须连续存储,当需要在 ArrayList 的中间位置插入或者删除元素时,需要将待插入或者删除的节点后的所有元素进行移动,其修改代价较高,因此, ArrayList 不适合随机插入和删除的操作,更适合随机查找和便利的操作。
ArrayList 不需要在定义时指定数组的长度,在数组长度不能存储要求时,ArrayList 会创建一个新的更大的数组并将数组中已有的数据复制到新的数组中。

  1. Vector:数组实现,增删慢,查询快,线程安全

Vector 的数据结构和 ArrayList 一样,都是基于数组实现的,不同的是 Vector 支持线程同步,即同一时刻只允许一个线程对 进行写操作(新增、删除、修改),以保证多线程环境下数据的一致性,但需要频繁地对 Vector 实例进行加锁和释放锁操作,因此,Vector 的读写效率整体上比 ArrayList 低。

  1. LinkedList:双向链表实现,增删快,查询慢,线程不安全

LinkedList 采用双向链表结构存储元素,在对 LinkedList 进行插入和删除操作时,数据改动较小,因此随机插入和删除的效率很高。但在对 LinkedList 进行随机访问时,需要从链表的头部一直遍历到该节点为止,因此随机访问速度很慢。LinkedList 还提供了在 List 接口中未定义的方法,用于操作链表头部和尾部的元素,因此有时可被当作堆栈和队列使用。
相关面试题:

  • ArrayList 和 LinkedList 有什么区别?★★★☆☆
  • 对 List 集合去重都有哪些方法?★★★☆☆
  • 数组和链表分别适用于什么场景?★★★☆☆
  • ArrayList 和 LinkedList 的底层数据结构是什么?★★★☆☆

1.1.2 Queue

Queue 是队列结构,Java 中的常用队列如下。

  • ArrayBlockingQueue:基于数组结构实现的有界阻塞队列
  • LinkedBlockingQueue:基于链表结构实现的有界阻塞队列
  • PriorityBlockingQueue:支持优先级排列的无界阻塞队列
  • DelayQueue:支持延迟操作的无界阻塞队列
  • SynchronousQueue:用于线程同步的阻塞队列
  • LinkedTransferQueue:基于链表数据结构实现的无界阻塞队列
  • LinkedBlockingQueue:基于链表数据结构实现的双向阻塞队列

相关面试题:

  • 你在开发中用过哪些队列,分别是在哪些场景下使用的?★☆☆☆☆

1.1.3 Set:不可重复

Set 的核心特性是独一无二,适用于存储无序且不相等的元素。对象的相等性在本质上是对象的 HashCode 值相同,Java 依据对象的内存地址计算出对象的 HashCode 值。如果想要比较两个对象是否相等,则必须同时覆盖对象的 hashCode 方法 和 equals 方法,并且 hashCode 方法和 equals 方法的返回值必须相同。

  1. HashSet:HashMap 实现,无序

HashSet 存放的是哈希值,它是按照元素的哈希值来存取元素的。元素的哈希值是通过元素的 hashCode 方法计算得到的,HashSet 首先判断两个元素的哈希值是否相等,如果哈希值相等则通过 equals 方法比较,如果 equals 方法返回的结果也为 true ,HashSet 就将其视为同一个元素;如果 equals 方法返回的结果为 false,HashSet 就将其视为不同的元素。

  1. TreeSet:二叉树实现

TreeSet 基于二叉树对新添加的对象按照指定的顺序排序(升序、降序),每添加一个对象都会进行排序,并将对象插入二叉树指定的位置。
integer 和 String 等基础对象类型可以直接根据 TreeSet 的默认排序进行存储,而自定义的数据类型必须实现 Comparable 接口,并且覆盖其中的 compareTo 函数才可以按照预定义的顺序存储。如果覆写 compareTo 函数,则在 this. 对象小于指定对象的条件下返回 -1,在 this. 对象大于指定对象的条件下返回 1 ,在 this. 对象等于指定对象的条件下返回 0 。

  1. LinkedHashSet:继承 HashSet,通过 HashMap 实现数据存储,双向链表记录顺序

LinkedHashSet 在底层使用 LlinkedHashMap 存储元素,它继承了 HashSet ,所有的方法和操作都与 HashSet 相同,因此 LinkedHashSet 的实现比较简单,只提供了 4 个构造方法,并通过传递一个标识参数调用父类的构造器,在底层构造一个 LlinkedHashMap 来记录数据访问,其它相关操作与父类 HashSet 相同,直接调用父类 HashSet 的方法即可。
相关面试题:

  • Set 如何保证元素不重复?★★★☆☆
  • HashSet 的原理是什么?★★☆☆☆
  • TreeSet 在排序时是如何比较元素的?★★☆☆☆

1.1.4 Map

  1. HashMap:数组+链表存储数据,线程不安全

HashMap 基于键的 HashCode 值唯一标识一条数据,同时基于键的hashCode 值进行数据的存取,因此可以快速地更新和查询数据,但其每次遍历的顺序无法保证相同。HashMap 的 Key 和 Value 允许为 null。
HashMap 是非线程安全的,即在同一时刻有多个线程同时写 HashMap 时可能导致数据不一致。如果需要满足线程安全的条件,则可以使用 Collections 的 synchronizeMap 方法使 HashMap具有线程安全的能力,或者使用 ConcurrentHashMap 代替 HashMap。
Java 8 中 HashMap 的数据结构如下所示:
在这里插入图片描述

其中,HashMap 的数据结构为数组+链表或红黑树。数组中的每个 Entry 实例(元素)都是一个链表或红黑树,默认数组中的 Entry 实例数据结构为链表,在链表中的元素个数超过 8 个以后,HashMap 会将链表结构转换为红黑树结构以提高查询效率。
HashMap 在查找数据时,首先根据 HashMap 的哈希值快速定位到数组的具体下标,但是在定位到数组下标后,如果数组中的 Entry 实例为链表,则需要对链表进行顺序遍历,直到找到需要的数据。但是如果数组中的 Entry 实例的数据结构为红黑树时,在效率上有很大提升。
HashMap 常用的参数如下:

  • capacity:当前数组的容量,默认为 16 ,可以扩容,扩容后数组的大小为当前的两倍,因此该值始终为 2 的 n 次方。
  • loadFactor:负载因子,默认为 0.75.
  1. ConcurrentHashMap 在 JDK 1.7 和 1.8 中的实现方式不同。

JDK 1.7 中 ConcurrentHashMap 采用分段锁的思想实现,因此是线程安全。ConcurrentHashMap 由多个 Segment 组成(Segment 的数量也是锁的并发度),每个 Segment 均继承自 ReentrantLock 并单独加锁,所以每次进行加锁操作时所著的都是一个 Segment,这样只要保证每个 Segment 都是线程安全的,也就实现了全局的线程安全。
在 ConcurrentHashMap 中,concurrencyLevel 参数表示并行级别,默认是 16 。也就是说 ConcurrentHashMap 默认由 16 个 Segment 组成,在这种情况下最多同事支持 16 个线程并发执行写操作,只要它们的操作分布在不同的 Segment 上即可。并行级别 concurrencyLevel 可以在初始化时设置,一旦初始化就不可更改。ConcurrentHashMap 的每个 Segment 内部的数据结构都和 HashMap 相同,如下:
在这里插入图片描述

JDK 1.8 中 ConcurrentHashMap 的实现:JDK 1.8 中的 ConcurrentHashMap 弃用了 Segment 分段锁,改用 Synchronize + CAS 实现对多线程的安全操作。同时,JDK 1.8 在 ConcurrentHashMap 中引入了红黑树,具体数据结构如下:
在这里插入图片描述

  1. HashTable:线程安全

HashTable 是遗漏类,很多映射的常用功能都与 HashMap 类似,不同的是,它继承自 Dictionary 类,并且是线程安全的,同一时刻只有一个线程能写 HashTable ,并发性不如 ConcurrentHashMap。

  1. TreeMap:基于二叉树数据结构

TreeMap 基于二叉树数据结构,同事实现了 SortedMap 接口,以保证元素的顺序存取,默认按键值的升序排序,也可以自定义排序比较器。
TreeMap 常用于实现排序的映射列表。在使用 TreeMap 时,其 Key 必须实现 Compareable 接口或采用自定义的比较器,否则会抛出 java.lang.ClassCastException 异常。

  1. LlinkedHashMap:继承 HashMap,使用链表保存插入顺序

LlinkedHashMap 为 HashMap 的子类,其内部使用链表保存元素的插入顺序,当通过 Iteator 遍历 LlinkedHashMap 时,会按照元素的插入顺序访问元素。
相关面试题:

  • HashMap 是如何快速定位到数据的?★★★☆☆
  • ConcurrentHashMap 是如何保障线程安全的?★★★☆☆
  • 常用的 Map 集合有哪些?★★☆☆☆
  • HashMap 是线程安全的吗?★★☆☆☆
  • HashMap 的内部数据结构是什么?★★☆☆☆
  • HashMap 在 JDK 1.8 中引入红黑树可以带来什么好处?★★☆☆☆
  • HashMap 和 HashTable 的区别是什么?★★☆☆☆
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只小熊猫呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值