集合概念
集合是Java提供的储存数据的一种容器,长度不限,类型不限。
Java提供的关于集合的类和接口都在java.util包里面。
框架概览
Java集合,也叫做容器,由一组接口、抽象类、实现类构成。主要由两大接口派生而来,分别是Collection接口和Map接口,Collection接口存储单列元素,Map接口存储双列元素。整个集合框架围绕一组标准接口而设计,我们可以直接使用这些接口的标准实现,也可以通过这些接口实现自己的集合。
从上面的集合框架图可以看到,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 不是集合,但是它们完全整合在集合中。
Iterable接口
迭代器接口,用于创建迭代器遍历集合元素,它并不规范数据的存储,而只是规范了集合元素的迭代。所以还是认为Collection接口是集合中的顶层接口。
Collection接口
Collection接口是集合中的顶层接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。这由它的实现类所决定。
方法概览:
修饰符和返回类型 | 方法描述 |
---|---|
boolean |
add(E e)
确保此集合包含指定的元素(可选操作)。 |
boolean |
addAll(Collection<? extends E> c)
将指定集合中的所有元素添加到此集合(可选操作)。 |
void |
clear()
从此集合中删除所有元素(可选操作)。 |
boolean |
contains(Object o)
如果此集合包含指定的元素,则返回 true 。 |
boolean |
containsAll(Collection<?> c)
如果此集合包含指定 集合中的所有元素,则返回true。 |
boolean |
equals(Object o)
将指定的对象与此集合进行比较以获得相等性。 |
int |
hashCode()
返回此集合的哈希码值。 |
boolean |
isEmpty()
如果此集合不包含元素,则返回 true 。 |
Iterator<E> |
iterator()
返回此集合中的元素的迭代器。 |
default Stream<E> |
parallelStream()
返回可能并行的 |
boolean |
remove(Object o)
从该集合中删除指定元素的单个实例(如果存在)(可选操作)。 |
boolean |
removeAll(Collection<?> c)
删除指定集合中包含的所有此集合的元素(可选操作)。 |
default boolean |
removeIf(Predicate<? super E> filter)
删除满足给定谓词的此集合的所有元素。 |
boolean |
retainAll(Collection<?> c)
仅保留此集合中包含在指定集合中的元素(可选操作)。 |
int |
size()
返回此集合中的元素数。 |
default Spliterator<E> |
spliterator()
创建一个 |
default Stream<E> |
stream()
返回以此集合作为源的顺序 |
Object[] |
toArray()
返回一个包含此集合中所有元素的数组。 |
<T> T[] |
toArray(T[] a)
返回包含此集合中所有元素的数组; 返回的数组的运行时类型是指定数组的运行时类型。 |
List接口
List代表了有序可重复集合,可直接根据元素的索引来访问,这类似于Java的数组。
List接口为Collection直接接口,实现List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。
ArrayList
ArrayList是一个动态数组,它允许任何符合规则的元素插入甚至包括null。每一个ArrayLis实例都有一个初始容量(10), 随着容器中的元素不断增加,容器的大小也会随着增加。在每次向容器中增加元素的同时都会进行容量检查,当快溢出时,就会进行扩容操作 (扩展为原来的1.5倍)。所以如果我们明确所插入元素的多少,最好指定一个初始容量值,避免过多的进行扩容操作而浪费时间、效率。
ArrayList擅长于随机访问,同时ArrayList是非同步的。
LinkedList
LinkedList是一个双向链表。所以它除了有ArrayList的基本操作方法外还额外提供了get,remove,insert等方法,可以在LinkedList的首部或尾部操作元素。
LinkedList不能随机访问,因为它所有的操作都是要按照双向链表的顺序执行 , 所以双向循环链表的查询效率低但是增删效率高。在此链表上每一个数据节点都由三部分组成:前指针(指向前面的节点的位置),数据,后指针(指向后面的节点的位置)。最后一个节点的后指针指向第一个节点的前指针,形成一个循环。
与ArrayList一样,LinkedList也是非同步的。如果多个线程同时访问一个List,则必须自己实现访问同步。
一种解决方法是在创建List时构造一个同步的List:
List list = Collections.synchronizedList(new LinkedList(...));
Vector
与ArrayList相似,但是Vector是同步的。所以说Vector是线程安全的动态数组。它的操作与ArrayList几乎一样。
Stack
Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop 方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。
Set接口
Set是一种不包含重复的元素的Collection,无序,Set最多有一个null元素。需要注意的是:虽然Set中元素没有顺序,但是元素在set中的位置是由该元素的HashCode决定的,其具体位置其实是固定的。
Set接口有三个具体实现类,分别是散列集HashSet、链式散列集LinkedHashSet和树形集TreeSet。
HashSet
HashSet 是一个没有重复元素的集合。它是由HashMap实现的,不保证元素的顺序(元素插入的顺序与输出的顺序不一致),而且HashSet允许使用null 元素。HashSet是非同步的。HashSet按Hash算法来存储集合的元素,因此具有很好的存取和查找性能。
HashSet的实现方式大致如下,通过一个HashMap存储元素,元素是存放在HashMap的Key中,而Value统一使用一个Object对象。
LinkedHashSet
LinkedHashSet继承自HashSet,其底层是基于LinkedHashMap来实现的,有序,非同步。LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起来像是以插入顺序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。
TreeSet
TreeSet是一个有序集合,其底层是基于TreeMap实现的,非线程安全。TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序和定制排序,其中自然排序为默认的排序方式。当我们构造TreeSet时,若使用不带参数的构造函数,则TreeSet的使用自然比较器;若用户需要使用自定义的比较器,则需要使用带比较器的参数。
注意:TreeSet集合不是通过hashcode和equals函数来比较元素的。它是通过compare或者comparaeTo函数来判断元素是否相等,compare函数通过判断两个对象的id,相同的id判断为重复元素,不会被加入到集合中。
Queue接口
队列通常但不一定是以FIFO(先进先出)方式排序元素。 除了优先级队列之外,优先级队列是根据提供的比较器对元素进行排序,还是元素的自然排序,以及对元素LIFO(先进先出)进行排序的LIFO队列(或堆栈)。 无论使用什么顺序,队列的头都是通过调用remove()
或poll()
删除的元素。 在一个FIFO队列,所有新元素插入到队列的尾部 。 其他类型的队列可以使用不同的布局规则。 每个Queue
实现必须指定其排序属性。
PriorityQueue
PriorityQueue保存队列元素的顺序并不是按照加入的顺序,而是按照队列元素的大小进行排序的。
PriorityQueue不允许插入null元素。
Deque
Deque接口是Queue接口的子接口,它代表一个双端队列,当程序中需要使用“栈”这种数据结构时,推荐使用ArrayDeque。
Map接口
Map用于保存具有映射关系的数据,Map里保存着两组数据:key和value,它们都可以使任何引用类型的数据,但key不能重复。
HashMap
Map接口基于哈希表的实现,是使用频率最高的用于键值对处理的数据类型。
它根据键的hashCode值存储数据,大多数情况下可以直接定位到它的值,特点是访问速度快,遍历顺序不确定,线程不安全,最多允许一个key为null,允许多个value为null。
可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap类。
LinkedHashMap继承了HashMap,是Map接口的哈希表和链接列表实现,它维护着一个双重链接列表,此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。
HashTable
Hashtable和HashMap从存储结构和实现来讲有很多相似之处,不同的是它承自Dictionary类,而且是线程安全的,另外Hashtable不允许key和value为null,并发性不如ConcurrentHashMap。
Hashtable不建议在新代码中使用,不需要线程安全的场合可以用HashMap替换,需要线程安全的场合可以用ConcurrentHashMap替换。
TreeMap
TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序(自然顺序),也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。
迭代器
通常情况下,你会希望遍历一个集合中的元素。例如,显示集合中的每个元素。
一般遍历数组都是采用for循环或者增强for,这两个方法也可以用在集合框架,但是还有一种方法是采用迭代器遍历集合框架,它是一个对象,实现了Iterator 接口或 ListIterator接口。
迭代器,使你能够通过循环来得到或删除集合的元素。ListIterator 继承了 Iterator,以允许双向遍历列表和修改元素。
序号 | 迭代器方法描述 |
---|---|
1 | 使用 Java Iterator 这里通过实例列出 Iterator 和 ListIterator 接口提供的所有方法。 |
如何使用比较器
TreeSet和TreeMap的按照排序顺序来存储元素. 然而,这是通过比较器来精确定义按照什么样的排序顺序。
这个接口可以让我们以不同的方式来排序一个集合。
序号 | 比较器方法描述 |
---|---|
1 | 使用 Java Comparator 这里通过实例列出Comparator接口提供的所有方法 |
总结
Java集合框架为程序员提供了预先包装的数据结构和算法来操纵他们。
集合是一个对象,可容纳其他对象的引用。集合接口声明对每一种类型的集合可以执行的操作。
集合框架的类和接口均在java.util包中。
任何对象加入集合类后,自动转变为Object类型,所以在取出的时候,需要进行强制类型转换。
参考资料
部分资料参考:
https://www.runoob.com/java/java-collections.html