Java容器
容器
1.Iterable 接口和 Iterator 接口
Iterable
接口从 JDK 1.5 开始出现,是 Java 容器的最顶级的接口之一,该接口的作用是使容器具备迭代元素的功能。
Iterator
接口从 JDK 1.2 开始出现,其含义是迭代器,可以用于迭代容器中的元素。
Iterable 接口的方法
iterator
方法 iterator
是 Iterable
接口的核心方法,返回 Iterator
类的迭代器实例。
forEach
方法 forEach
从 JDK 1.8 开始出现。该方法有默认实现,其作用是对容器中的每个元素进行处理。
spliterator
方法 spliterator
从 JDK 1.8 开始出现。该方法有默认实现,其作用是并行遍历元素。
Iterator 接口的方法
hasNext
方法 hasNext
的作用是检测容器中是否还有需要迭代的元素。
next
方法 next
的作用是返回迭代器指向的元素,并且更新迭代器的状态,将迭代器指向的元素后移一位。
remove
方法 remove 的作用是将迭代器指向的元素删除。
该方法有默认实现,默认实现为抛出 UnsupportedOperationException
异常,如果迭代器迭代的容器不支持 remove 操作,则对迭代器调用方法 remove 时会抛出 UnsupportedOperationException
异常。
forEachRemaining
方法 forEachRemaining
从 JDK 1.8 开始出现。
该方法有默认实现,其作用是对容器中的剩余元素进行处理,直到剩余元素处理完毕或者抛出异常。
2.Collection 接口
Collection
接口统一定义了单列容器,该接口继承了Iterable
接口。
Collection 接口的常用方法
添加元素
添加元素的方法有 add
和 addAll
,其中 add
一次添加一个元素,addAll
一次将另一个容器中的元素全部添加到当前容器中。
方法 addAll
和集合的并集运算相似。
删除元素
删除元素的方法有 remove
、removeAll
和 clear
remove
一次删除一个元素removeAll
一次将另一个容器中的元素全部从当前容器中删除clear
删除当前容器中的全部元素
方法 removeAll
和集合的差集运算相似。
保留元素
保留元素的方法有 retainAll
。
该方法保留既在当前容器中又在另一个容器中的元素。
和集合的交集运算相似。
判断包含元素
判断包含元素的方法有 contains
和 containsAll
contains
判断当前容器是否包含一个指定元素containsAll
判断当前容器是否包含另一个容器中的全部元素
其他方法
-
方法
isEmpty
判断当前容器是否为空(即不包含元素) -
方法
size
返回容器中的元素数目 -
方法
toArray
将容器转化成 Object 数组并返回
Collection 接口和其他容器类的关系
List
和 Set
是 Collection
接口的子接口。
List
是线性表,存储一组顺序排列的元素。Set
是集合,存储一组互不相同的元素。
另一个描述容器的接口是 Map
,该接口与 Collection
并列存在。
不同于*** Collection
接口存放单值元素,Map
接口存放的是键值对。***
3.线性表
List
接口定义线性表,该接口继承了 Collection
接口。
List
接口的实现类有 ArrayList
、LinkedList
和 Vector
。
List 接口的常用方法
由于 List
接口继承了 Collection
接口,因此 List
接口支持 Collection
接口的一切方法。
List
接口还支持通过下标访问元素,根据下标进行添加和删除元素的操作,以及可以生成双向遍历线性表的新迭代器。
以下列举的方法是 List 接口中新定义的方法(和 Collection 相比新定义的方法)。
添加元素
添加元素的方法有 add
和 addAll
add
一次添加一个元素addAll
一次将另一个容器中的元素全部添加到当前线性表中。
这两个方法都可以指定下标,在指定下标处添加元素。
删除元素
删除元素的方法有 remove
可以指定下标,删除指定下标处的元素。
修改元素
修改元素的方法有 set
,将指定下标处的元素修改成新的元素。
获得元素以及获得元素下标
获得元素的方法有 get
,返回指定下标处的元素。
获得元素下标的方法有 indexOf
和 lastIndexOf
,分别返回第一个匹配元素的下标和最后一个匹配元素的下标。
获得子线性表
获得子线性表的方法有 subList
,返回指定下标范围的子线性表。
生成线性表迭代器
生成线性表迭代器的方法有 listIterator
-
该方法如果没有参数则返回线性表中元素的迭代器
-
如果指定下标则返回从指定下标开始的元素的迭代器
线性表迭代器的接口是 ListIterator
,该接口继承了 Iterator
接口,支持双向遍历。
向量类 Vector
Vector
类和 ArrayList
类的方法基本是相同的,主要区别是 Vector
类的所有方法都是同步的,因此可以保证线程安全。
虽然 Vector
是线程安全的,但是同步操作会花费更多的时间。
在不需要保证线程安全的情况下,使用 ArrayList
比使用 Vector
效率更高。
思考
ArrayList
和 LinkedList
的主要区别是什么?从底层实现、随机访问元素、插入和删除元素方面回答。
ArrayList
的底层实现是数组,LinkedList
的底层实现是双向链表。ArrayList
可以快速访问指定下标处的元素,LinkedList
需要遍历元素才能得到指定下标处的元素。ArrayList
插入和删除元素时需要移动其他元素(除了在尾部插入和删除元素的情况),LinkedList
可以快速插入和删除元素,但是在指定位置(除了头部和尾部)插入和删除元素时则需要首先遍历元素到指定位置。
ArrayList
和 Vector
的主要区别是什么?什么情况下使用 ArrayList
比使用 Vector
更好?
ArrayList
和 Vector
的主要区别是:
-
Vector
的所有方法都是同步的,因此可以保证线程安全, -
ArrayList
不能保证线程安全。
在不需要保证线程安全的情况下,使用 ArrayList
比使用 Vector
效率更高,因此使用 ArrayList
更好。
4.映射和集合
Map
接口定义映射,存储一组键值对的映射关系。
Set
接口定义集合,存储一组互不相同的元素,该接口继承了 Collection
接口。
Map 接口的概念和常用方法
Map
接口存储一组键值对的映射关系,映射中的每个键对应一个值。映射中不能有重复的键,否则会出现一个键对应多个值的情况,这违背了映射的定义。
放置键值对
放置键值对的方法有 put
和 putAll
,
put
一次放置一个键值对,putAll
一次将另一个映射中的键值对全部添加道当前映射中。
在映射中放置键值对时,如果映射中没有对应的键,则在映射中新建一个键值对,否则用新的键值对覆盖原来的相同键的键值对。
删除键值对
删除键值对的方法有 remove
和 clear
remove
删除指定键的键值对clear
删除当前映射中的全部键值对
判断包含键或值
判断包含键或值的方法有 containsKey
和 containsValue
containsKey
判断映射中是否包含指定的键containsValue
判断映射中是否包含指定的值
根据键获得值
根据键获得值得方法有 get
,该方法返回映射中指定键的值。
获得键或键值对的集合
获得键或键值对的集合的方法有 entrySet
和 keySet
entrySet
返回映射的所有键值对的集合keySet
返回映射的所有键的集合
获得值的容器
获得值的容器的方法有 values
,该方法返回映射的所有值的容器。
其他方法
-
方法
isEmpty
判断当前映射是否为空(即不包含键值对) -
方法
size
返回映射中的键值对数目。
Map 接口的实现类 HashMap
、Hashtable
和 TreeMap
HashMap
和 Hashtable
HashMap
类是散列映射,通过散列函数计算键对应的存储位置。
因此可以快速地完成放置键值对、删除键值对、根据键获得值的操作。
JDK 1.8 之前的 HashMap
的底层通过数组和链表实现,如果出现冲突则通过拉链法解决冲突。
JDK 1.8 在解决冲突时的实现有较大变化,当链表长度大于阈值(默认为 8)时,将链表转化为红黑树,以减少搜索时间。
Hashtable
类是散列表,其功能和 HashMap
相似。
以下是 HashMap
和 Hashtable
的部分区别。
HashMap
不是线程安全的,Hashtable
的大多数方法用关键字synchronized
修饰,因此Hashtable
是线程安全的。- 在不需要保证线程安全的情况下,
HashMap
的效率高于Hashtable
。 HashMap
允许键或值为null
,只能有一个键为null
,可以有一个或多个键对应的值为null
,Hashtable
不允许键或值为null
。- 从 JDK 1.8 开始,
HashMap
在链表长度大于阈值(默认为 8)时,将链表转化为红黑树以减少搜索时间,Hashtable
没有这样的机制。
TreeMap
TreeMap
是有序映射,键可以使用 Comparable
接口或 Comparator
接口排序。
TreeMap
的底层实现是红黑树,通过红黑树维护映射的有序性。由于要维护映射的有序性,因此 TreeMap
的各项操作的平均效率低于 HashMap
,但是 TreeMap
可以按照顺序获得键值对。
Set 接口的定义和常用方法
Set
接口存储一组互不相同的元素,一个集合中不存在两个相等的元素。
Set
接口继承了 Collection
接口,没有引入新的方法或常量,只是规定其实例不能包含相等的元素。
Set 接口的实现类 HashSet 和 TreeSet
HashSet
HashSet
类是散列集合,其底层实现基于 HashMap
。
- 当对象加入散列集合时,需要判断元素是否重复,
- 首先通过方法
hashCode
计算对象的散列码检查是否有对象具有相同的散列码, - 如果没有相同的散列码则没有重复元素,否则再通过方法
equals
检查是否有相等的对象。
- 首先通过方法
根据散列约定,如果两个对象相同,它们的散列码一定相同。
因此如果在子类中重写了 equals
方法,必须在该子类中重写 hashCode
方法,以保证两个相等的对象对应的散列码是相同的。
TreeSet
TreeSet
类是有序集合,其底层实现基于 TreeMap
。
和 TreeMap
相似,TreeSet
可以使用 Comparable
接口或 Comparator
接口对元素排序。
HashSet
的底层实现基于HashMap
,元素是无序的,通过方法hashCode
和equals
保证元素没有重复;TreeSet
的底层实现基于TreeMap
,元素是有序的,通过Comparable
接口或Comparator
接口保证元素没有重复。