集合类java
A. 面试都喜欢问的java集合类
了一些所谓大公司的Java面试问题,发现对于JAVA集合类的使用都比较看重似的,而自己在这方面还真的是所真甚少,抽空也学习学习吧。
java.util包中就包含了一系列重要的集合类,而对于集合类,主要需要掌握的就是它的内部结构,以及遍历集合的迭代模式。
接口:Collection
所有集合类的根类型,主要的一个接口轿键方法:booleanadd(Ojbectc)
虽返回的是boolean,但不是表示添加成功与否,因为Collection规定:一个集合拒绝添加这个元素,无论什么原因,都必须抛出异常,这个返回值表示的意义是add()执行后,集合的内容是否改了(就是元素有无数量、位置等变化)。类似的addAll,remove,removeAll,remainAll也是一样的。
用Iterator模式实现遍历集合
Collection有一个重要的方法:iterator(),返回一个Iterator(迭代子),用于遍历集合的所有元素。Iterator模式可以把访问逻辑从不同类的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。
for(Iteratorit=c.iterator();it.hasNext();){...}
不需要维护遍历集合的“指针”,所有的内部状态都有Iterator来维护,而这个Iterator由集合类通过工厂方法生成。
每一种集合类返回的Iterator具体类型可能不同,但它们都实现了Iterator接口,因此,我们不需要关心到底是哪种Iterator,它只需要中庆获得这个Iterator接口即可,这就是接口的好处,面向对象的威力。
要确保遍历过程顺利完成,电脑培训认为必须保证遍历过程中不更改集合的内容(Iterator的remove()方闭培巧法除外),所以,确保遍历可靠的原则是:只在一个线程中使用这个集合,或者在多线程中对遍历代码进行同步。
B. 怎样从java集合类set中取出数据
创建set的iterator方法:
Set<Object> set = new HashSet<Object>();
Iterator<Object> it = set.iterator();
while(it.hasNext())//判断是否有下一个
it.next()取出元素。
以上早族方法便是从Set集合中取出数据。
(2)集合类java扩展阅读:
Java中使用Set接口描述一个集合(集合不允许有“重复值”,注意重复的枝睁模概念),集合Set是Collection的子接口,Set不允许其数据元猛缓素重复出现,也就是说在Set中每一个数据元素都是唯一的。Set接口定义的常用方法如下:
1、size() 获取Set尺寸(即Set包含数据元素的总数)。
2、 add(Object obj) 向Set中添加数据元素obj。
3、remove(Object obj) 从Set中移除数据元素obj。
4 、contains(Object obj) 判断当前Set中是否包含数据元素obj,如果包含返回true,否则返回false。
5、iterator() 将Set装入迭代器。
C. 怎么才能了解JAVA集合类集合类是什么
数组是集合的一种,是集合的一个子集,你理解了数组就理解了集合.
理解集合类
集合类存放于java.util包中。
集合类存放的都是对象的引用,而非对象本身,出于表达上的便利,我们称集合中的对象就是指集合中对象的引用(reference)。
集合类型主要有3种:set(集)、list(列表)和map(映射)。
(1)集
集(set)是最简单的一种集合,它的对象不按特定方式排序,只是简单的把对象加入集合中,就像往口袋里放东西。
对集中成员的访问和操作是通过集中对象的引用进行的,所以集中不能有重复对象。
集也有多种变体,可以实现排序等功能,如TreeSet,它把对象添加到集中的操作将变为按照某种比较规则将其插入到有序的对象序列中。它实现的是SortedSet接口,也就是加入了对象比较的方法。通过对集中的对象迭代,我们可以得到一个升序的对象集合。
(2)列表
列表的主要特征是其对象以线性方式存储,没有特定顺序,只有一个开头和一个结尾,当然,它与根本没有顺序的集是不同的。
列表在数据结构中分别表现为:数组和向量、链表、堆栈、队列。
关于实现列表的集合类,是我们日常工作中经常用到的,将在后边的笔记详细介绍。
(3)映射
映射与集或列表有明显区别,映射中每个项都是成对的。映射中存储的每个对象都有一个相关的关键字(Key)对象,关键字决定了对象在映射中的存储位置,检索对象时必须提供相应的关键字,就像在字典中查单词一样。关键字应该是唯一的。
关键字本身并不能决定对象的存储位置,它需要对过一种散列(hashing)技术来处理,产生一个被称作散列码(hash code)的整数值,散列码通常用作一个偏置量,该偏置量是相对于分配给映射的内存区域起始位置的,由此确定关键字/对象对的存储位置。理想情况下,散列处理应该产生给定范围内均匀分布的值,而且每个关键字应得到不同的散列码。
集合类简介
java.util中共有13个类可用于管理集合对象,它们支持集、列表或映射等集合,以下是这些类的简单介绍
集:
HashSet: 使用HashMap的一个集的实现。虽然集定义成无序,但必须存在某种方法能相当高效地找到一个对象。使用一个HashMap对象实现集的存储和检索操作是在固定时间内实现的.
TreeSet: 在集中以升序对对象排序的集的实现。这意味着从一个TreeSet对象获得第一个迭代器将按升序提供对象。TreeSet类使用了一个TreeMap.
列表:
Vector: 实现一个类似数组一样的表,自动增加容量来容纳你所需的元素。使用下标存储和检索对象就象在一个标准的数组中一样。你也可以用一个迭代器从一个Vector中检索对象。Vector是唯一的同步容器类??当两个或多个线程同时访问时也是性能良好的。
Stack: 这个类从Vector派生而来,并且增加了方法实现栈??一种后进先出的存储结构。
LinkedList: 实现一个链表。由这个类定义的链表也可以像栈或队列一样被使用。
ArrayList: 实现一个数组,它的规模可变并且能像链表一样被访问。它提供的功能类似Vector类但不同步。
映射:
HashTable: 实现一个映象,所有的键必须非空。为了能高效的工作,定义键的类必须实现hashcode()方法和equal()方法。这个类是前面java实现的一个继承,并且通常能在实现映象的其他类中更好的使用。
HashMap: 实现一个映象,允许存储空对象,而且允许键是空(由于键必须是唯一的,当然只能有一个)。
WeakHashMap: 实现这样一个映象:通常如果一个键对一个对象而言不再被引用,键/对象对将被舍弃。这与HashMap形成对照,映象中的键维持键/对象对的生命周期,尽管使用映象的程序不再有对键的引用,并且因此不能检索对象。
TreeMap: 实现这样一个映象,对象是按键升序排列的。
Set和List都是由公共接口Collection扩展而来,所以它们都可以使用一个类型为Collection的变量来引用。这就意味着任何列表或集构成的集合都可以用这种方式引用,只有映射类除外(但也不是完全排除在外,因为可以从映射获得一个列表。)所以说,把一个列表或集传递给方法的标准途径是使用Collection类型的参数
D. 在Java中,什么是集合类,跟普通类有什么区别
简单的说,集合类有list列,set集和map映射 三大类!x0dx0ax0dx0aSet(集):集合中的对象无排列顺序,并且没有重复的对象纯哗敬.x0dx0ax0dx0aList(队列):集合中的对象按照索引的顺序排列,可以有重复做慎的对象。x0dx0ax0dx0aMap(映射):集合中的每一个元素都是一对一对的,包括一个key对象,一个Value对象(一个Key指向一个Value).集合中没有重复的key对象,但是vaulue对象可以重复.x0dx0a集合类是进芦局行集合操作的。
E. java集合是什么
Java集合是什么:
Java 中的集合类库可以帮助我们在程序设计中实现传统的数据结构。
Java的集合类是一个用来存放对象的容器,有以下特点:
1、Java集合只能存放对象。加入添加了一个基本数据类型,会被自动装箱后存入集合。
2、集合存放的是多个对象的引用,对象本身是在堆内存中的。
3、集合可以存放不同类型,不限数量的数据类型。
集合分三种:1、Set 2 、List 3、Map,下面进行具体介绍。
扩展链接:
主要内容:
1)手写ArrayList
2)手写单链表
3)手写LinkedList
4)手写HashMap
5)手写HashSet
6)最新并发集合类
学习目标:
1. 掌握手写ArrayList
2. 掌握手写单链表
3. 掌握手写LinkedList
4. 掌握手写HashMap
5. 掌握手写HashSet
6. 理解最新并发集合类底层原理
视频课程小结:
01_集合提升训练_手写ArrayList_get_size_isEmpty_自定义异常
02_集合提升训练_手写ArrayList_构造方法_add
03_集合提升训练_手写ArrayList_toString_iterator
04_集合提升循环_手写单链表_get
05_集合提升训练_手写单链表_add_remove_toString
06_集合提升训练_手写LinkedList
07_集合提升训练_手写LinkedList_添加内存分配图
08_集合提升训练_HashMap的原理和代码准备
09_集合提升训练_手写HashMap的put
10_集合提升训练_手写HashMap的get_toString
11_集合提升训练_手写HashSet
12_集合提升训练_新一代并发集合类
F. JAVA的集合类型有哪些
集合类型主要有3种:set(集)、list(列表)和map(映射)。
1、List(有序、可重复)
List里存放的对象是有序的,同时也是可以重复的,List关注的是索引,拥有一系列和索引相关的方法,查询速度快。因为往list集合里插入或删除数据时,会伴随着后面数据的移动,所有插入删除数据速度慢。
2、Set(无序、不能重复)
Set里存放的对象是无序,不能重复的,集合中的对象不按特定的方式排序,只是简单地把对象加入集合中。
3、Map(键值对、键唯一、值不唯一)
Map集合中存储的是键值对,键不能重复,值可以重复。根据键得到值,对map集合遍历时先得到键的set集合,对set集合进行遍历,得到相应的值。
(6)集合类java扩展阅读:
JAVA集合类型四种常见输出方式:
1、Iterator:迭代输出,是使用最多的输出方式。
2、ListIterator:是Iterator的子接口,专门用于输出List中的内盯扒乱容。
3、foreach输出:JDK1.5之后提供的新功凯档能,可以输出数组或集合。
4、for循环。
代码示例如下此困:
for的形式:for(inti=0;i<arr.size();i++){...}
foreach的形式:for(inti:arr){...}
iterator的形式:
Iterator it = arr.iterator();
while(it.hasNext()){ object o =it.next(); ...}
参考资料来源:网络:java集合类
G. java怎么集合分类
Set 接口继承 Collection,但不允许重复,使用自己内部的乱念宴一个排列机制。
List 接口继承 Collection,允许重复,以元素安插的次序来放置元素,不会重新排列。
Map是以键值对存高猜放哗银!
现在主要用ArrayList和HashMap!
collection接口下由set,list两大集合实现,set又有HashSet,TreeSet实现,List又由ArrayList,LinkedList实现;另一Map接口由HashMap,TreeMap实现。
H. Java中最常用的集合类框架
一、HashMap的概述
HashMap可以说是Java中最常用的集合类框架之一,是Java语言中非常典型的数据结构。
HashMap是基于哈希表的Map接口实现的,此实现提供所有可选的映射操作。昌平镇电脑培训发现存储的是对的映射,允许多个null值和一个带念null键。但此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
除了HashMap是异步以及允许使用null外,HashMap类与Hashtable大致相同。
此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get和put)提供稳定的性能。迭代collection视图所需的时间与HashMap实例的“容量”(桶的数量)及其大小(键-值映射关系数)成比例。所以,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。
HashMap的实例有两个参数影响其性能:初始容量和加载因子。容量是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行rehash操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。
通常,默认加载因子(0.75)在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数HashMap类的操作中,包括get和put操作,都反映了这一点)。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少rehash操作次数。如果初始容量大于最大条目数除以加载因子,则不会发生rehash操作。
注意,此实现不是同步的。如果多个线程同时访问一个HashMap实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。这通常是通过同步那些用来封装列表的对象来实现的。但如果没有这样的对象存在,则应该使用{@linkCollections#synchronizedMapCollections.synchronizedMap}来进行“包装”,该方法最好是在创建时完成,为了避免对映射进行意外的异步操作。
Mapm=Collections.synchronizedMap(newHashMap(...));
二、构造函数
HashMap提供了三个构造函数:
HashMap():构造一个具有默认初始容量(16)和默认加载因子(0.75)的空HashMap。
HashMap(intinitialCapacity):构造一个带指定初始容量和默认加载因子(0.75)的空HashMap。
HashMap(intinitialCapacity,floatloadFactor):构造一个带指定初始容量和加载因子的空HashMap。
这里提到了两个参数:初始容量,加载因子。这两个参数是影响HashMap性能的重要参数,其中容量表示哈希表中桶的数量,初始容量是创建哈希表时的容量,加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度,它衡量的是一个散列表的空间的使用程度,负载因子越大表示散列表的装填程散唯度越高冲行培,反之愈小。对于使用链表法的散列表来说,查找一个元素的平均时间是O(1+a),因此如果负载因子越大,对空间的利用更充分,然而后果是查找效率的降低;如果负载因子太小,那么散列表的数据将过于稀疏,对空间造成严重浪费。系统默认负载因子为0.75,一般情况下我们是无需修改的。
HashMap是一种支持快速存取的数据结构,要了解它的性能必须要了解它的数据结构。
I. Java集合类框架的基本接口有哪些
在JAVA开发中框架给我们提供了很多方便的接口,但是接口到底是什么呢?在开发中又起到了什么作用呢,这就是java课程今天需要了解的!
一.为什么要使用接口
假如有一个需求:要求实现防盗门的功能。门有”开”和”关”的功能,锁有”上锁”和”开锁”的功能。
分析:首先防盗门是一个门,门有开门和关门的功能,还有一把锁,锁有开锁和上锁,按照面向对象的编程的思想,我们会将门和锁都作为一个类而单独存在,但是,不能让防盗门继承自门的同时又继承自锁,防盗门不是锁,不符合继承中isa的关系,在java中支持单继承。那么我们如何来解决这一问题,这时就要用到接口。
二.什么是接口
在软件中接口是一种规范和标准,他们可以约束类的行为,坦举慧是一些方法特征的集合,但是没有方法的实现,接口其实上也可以看做是一个特殊的抽象类,但是采用和抽象类完全不同的方法来表示,两者的设计理念也是不同的,抽象类有利于代码复用,接口利于代码的扩展和维护。
三.抽象类和接口的区别:
01抽象类可以提供成员方法的实现细节,而接口中只能存在publicabstract方法;
02.抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是publicstaticfinal类型的;
03.接口中不能含有静态代码块以及静态方法,而抽象类可以答扒有静态代码块和静态方法让答;
04.一个类只能继承一个抽象类,而一个类却可以实现多个接口。
J. java集合类哪个函数可以
java集合里面的函数
java集合里面的函数_java集合【1】——— 从集合接口框架说起
百里方欣
原创
关注
0点赞·155人阅读
(一) java集合分类
之前大概分为三种,Set,List,Map三种,JDK5之后,增加Queue.主要由Collection和Map两个接口衍生出来,同时Collection接口继承Iterable接口,所以我们也可以说java里面的集合类主要是由Iterable和Map两个接口以及他们的子接口或者其实现类组成。我们可以认为Collection接口定义了单列集合的规范,每次只能存储一个元素,而Map接口定义了双列集合的规范,每次能存储一对元素。
Iterable接口:主要是实现遍历功能
Collection接口: 允许重复
Set接口:无序,元素不可重复,访问元素只能通过元素本身来访问。
List接口:有序且可重复,可以根据元素的索引来访问集合中的元素。
Queue接口:队列集合
Map接口:映射关系,简单理解为键值对,Key不可重复,与Collection接口关系不大,只是个别函数使用到。
整个接口框架关系如下(来自网络):
(1) Iterable接口
1. 内部定义的方法
java集合最源头的接口,实现这个接口的作用主要是集合对象可以通过迭代器去遍历每一个元素。
源码如下:
// 返回一个内部元素为T类型的迭代器(JDK1.5只有这个接口)
Iterator iterator();
// 遍历内部元素,action意思为动作,指可以对每个元素进行操作(JDK1.8添加)
default void forEach(Consumer super T> action) {}
// 创建并返回一个可分割迭代器(JDK1.8添加),分割的迭代器主要是提供可以并行遍历元素的迭代器,可以适应现在cpu多核的能力,加快速度。
default Spliterator spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
从上面可以看出,foreach迭代以及可分割迭代,都加了default关键字,这个是Java 8 新的关键字,以前接口的所有接口,具体子类都必须实现,而对于deafult关键字标识的方法,其子类可以不用实现,这也是接口规范发生变化的一点。
下面我们分别展示三个接口的调用:
1.1 iterator方法
public static void iteratorHasNext(){
List list=new ArrayList();
list.add("Jam");
list.add("Jane");
list.add("Sam");
// 返回迭代器
Iterator iterator=list.iterator();
// hashNext可以判断是否还有元素
while(iterator.hasNext()){
//next()作用是返回当前指针指向的元素,之后将指针移向下个元素
System.out.println(iterator.next());
}
}
当然也可以使用for-each loop方式遍历
for (String item : list) {
System.out.println(item);
}
但是实际上,这种写法在class文件中也是会转成迭代器形式,这只是一个语法糖。class文件如下:
public class IterableTest {
public IterableTest() { }
public static void main(String[] args) {
iteratorHasNext();
}
public static void iteratorHasNext() {
List list = new ArrayList();
list.add("Jam");
list.add("Jane");
list.add("Sam");
Iterator iterator = list.iterator();
Iterator var2 = list.iterator();
while(var2.hasNext()) {
String item = (String)var2.next();
System.out.println(item);
}
}
}
需要注意的一点是,迭代遍历的时候,如果删除或者添加元素,都会抛出修改异常,这是由于快速失败【fast-fail】机制。
public static void iteratorHasNext(){
List list=new ArrayList();
list.add("Jam");
list.add("Jane");
list.add("Sam");
for (String item : list) {
if(item.equals("Jam")){
list.remove(item);
}
System.out.println(item);
}
}
从下面的错误我们可以看出,第一个元素是有被打印出来的,也就是remove操作是成功的,只是遍历到第二个元素的时候,迭代器检查,发现被改变了,所以抛出了异常。
Jam
Exception in thread "main" java.util.
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at IterableTest.iteratorHasNext(IterableTest.java:15)
at IterableTest.main(IterableTest.java:7)
1.2 forEach方法
其实就是把对每一个元素的操作当成了一个对象传递进来,对每一个元素进行处理。
default void forEach(Consumer super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
```java
当然像ArrayList自然也是有自己的实现的,那我们就可以使用这样的写法,简洁优雅。forEach方法在java8中参数是`java.util.function.Consumer`,可以称为**消费行为**或者说**动作**类型。
```java
list.forEach(x -> System.out.print(x));
同时,我们只要实现Consumer接口,就可以自定义动作,如果不自定义,默认迭代顺序是按照元素的顺序。
public class ConsumerTest {
public static void main(String[] args) {
List list=new ArrayList();
list.add("Jam");
list.add("Jane");
list.add("Sam");
MyConsumer myConsumer = new MyConsumer();
Iterator it = list.iterator();
list.forEach(myConsumer);
}
static class MyConsumer implements Consumer {
@Override
public void accept(Object t) {
System.out.println("自定义打印:" + t);
}
}
}
输出的结果:
自定义打印:Jam
自定义打印:Jane
自定义打印:Sam
1.3 spliterator方法
这是一个为了并行遍历数据元素而设计的迭代方法,返回的是Spliterator,是专门并行遍历的迭代器。以发挥多核时代的处理器性能,java默认在集合框架中提供了一个默认的Spliterator实现,底层也就是Stream.isParallel()实现的,我们可以看一下源码:
// stream使用的就是spliterator
default Stream stream() {
return StreamSupport.stream(spliterator(), false);
}
default Spliterator spliterator() {
return Spliterators.spliterator(this, 0);
}
public static Stream stream(Spliterator spliterator, boolean parallel) {
Objects.requireNonNull(spliterator);
return new ReferencePipeline.Head<>(spliterator,
StreamOpFlag.fromCharacteristics(spliterator),
parallel);
}
使用的方法如下:
public static void spliterator(){
List list = Arrays.asList("1", "2", "3","4","5","6","7","8","9","10");
// 获取可迭代器
Spliterator spliterator = list.spliterator();
// 一个一个遍历
System.out.println("tryAdvance: ");
spliterator.tryAdvance(item->System.out.print(item+" "));
spliterator.tryAdvance(item->System.out.print(item+" "));
System.out.println("\n-------------------------------------------");
// 依次遍历剩下的
System.out.println("forEachRemaining: ");
spliterator.forEachRemaining(item->System.out.print(item+" "));
System.out.println("\n------------------------------------------");
// spliterator1:0~10
Spliterator spliterator1 = list.spliterator();
// spliterator1:6~10 spliterator2:0~5
Spliterator spliterator2 = spliterator1.trySplit();
// spliterator1:8~10 spliterator3:6~7
Spliterator spliterator3 = spliterator1.trySplit();
System.out.println("spliterator1: ");
spliterator1.forEachRemaining(item->System.out.print(item+" "));
System.out.println("\n------------------------------------------");
System.out.println("spliterator2: ");
spliterator2.forEachRemaining(item->System.out.print(item+" "));
System.out.println("\n------------------------------------------");
System.out.println("spliterator3: ");
spliterator3.forEachRemaining(item->System.out.print(item+" "));
}
tryAdvance() 一个一个元素进行遍历
forEachRemaining() 顺序地分块遍历
trySplit()进行分区形成另外的 Spliterator,使用在并行操作中,分出来的是前面一半,就是不断把前面一部分分出来
结果如下:
tryAdvance:
1 2
-------------------------------------------
forEachRemaining:
3 4 5 6 7 8 9 10
------------------------------------------
spliterator1:
8 9 10
------------------------------------------
spliterator2:
1 2 3 4 5
------------------------------------------
spliterator3:
6 7
还有一些其他的用法在这里就不列举了,主要是trySplit()之后,可以用于多线程遍历。理想的时候,可以平均分成两半,有利于并行计算,但是不是一定平分的。
2. Collection接口 extend Iterable
Collection接口可以算是集合类的一个根接口之一,一般不能够直接使用,只是定义了一个规范,定义了添加,删除等管理数据的方法。继承Collection接口的有List,Set,Queue,不过Queue定义了自己的一些接口,相对来说和其他的差异比较大。
2.1 内部定义的方法
源码如下:
boolean add(Object o) //添加元素
boolean remove(Object o) //移除元素
boolean addAll(Collection c) //批量添加
boolean removeAll(Collection c) //批量移除
void retainAll(Collection c) // 移除在c中不存在的元素
void clear() //清空集合
int size() //集合大小
boolean isEmpty() //是否为空
boolean contains(Object o) //是否包含在集合中
boolean containsAll(Collection c) //是否包含所有的元素
Iterator iterator() // 获取迭代器
Object[] toArray() // 转成数组
default boolean removeIf(Predicate super E> filter) {} // 删除集合中复合条件的元素,删除成功返回true
boolean equals(Object o)
int hashCode()
default Spliterator spliterator() {} //获取可分割迭代器
default Stream stream() {} //获取流
default Stream parallelStream() {} //获取并行流
里面获取并行流的方法parallelStream(),其实就是通过默认的ForkJoinPool(主要用来使用分治法(Divide-and-Conquer Algorithm)来解决问题),提高多线程任务的速度。我们可以使用ArrayList来演示一下平行处理能力。例如下面的例子,输出的顺序就不一定是1,2,3...,可能是乱序的,这是因为任务会被分成多个小任务,任务执行是没有特定的顺序的。
List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
list.parallelStream()
.forEach(out::println);
2.2 继承Collection的主要接口
graph LR;
Collection -->List-有顺序,可重复
List-有顺序,可重复 -->LinkedList-使用链表实现,线程不安全
List-有顺序,可重复 -->ArrayList-数组实现,线程不安全
List-有顺序,可重复 -->Vector-数组实现,线程安全
Vector-数组实现,线程安全 -->Stack-堆栈,先进后出
Collection-->Set-不可重复,内部排序
Set-不可重复,内部排序-->HashSet-hash表存储
HashSet-hash表存储-->LinkHashSet-链表维护插入顺序
Set-不可重复,内部排序-->TreeSet-二叉树实现,排序
Collection-->Queue-队列,先进先出
2.2.1 List extend Collection
继承于Collection接口,有顺序,取出的顺序与存入的顺序一致,有索引,可以根据索引获取数据,允许存储重复的元素,可以放入为null的元素。
最常见的三个实现类就是ArrayList,Vector,LinkedList,ArrayList和Vector都是内部封装了对数组的操作,唯一不同的是,Vector是线程安全的,而ArrayList不是,理论上ArrayList操作的效率会比Vector好一些。
里面是接口定义的方法:
int size(); //获取大小
boolean isEmpty(); //判断是否为空
boolean contains(Object o); //是否包含某个元素
Iterator iterator(); //获取迭代器
Object[] toArray(); // 转化成为数组(对象)
T[] toArray(T[] a); // 转化为数组(特定位某个类)
boolean add(E e); //添加
boolean remove(Object o); //移除元素
boolean containsAll(Collection> c); // 是否包含所有的元素
boolean addAll(Collection extends E> c); //批量添加
boolean addAll(int index, Collection extends E> c); //批量添加,指定开始的索引
boolean removeAll(Collection> c); //批量移除
boolean retainAll(Collection> c); //将c中不包含的元素移除
default void replaceAll(UnaryOperator operator) {}//替换
default void sort(Comparator super E> c) {}// 排序
void clear();//清除所有的元素
boolean equals(Object o);//是否相等
int hashCode(); //计算获取hash值
E get(int index); //通过索引获取元素
E set(int index, E element);//修改元素
void add(int index, E element);//在指定位置插入元素
E remove(int index);//根据索引移除某个元素
int indexOf(Object o); //根据对象获取索引
int lastIndexOf(Object o); //获取对象元素的最后一个元素
ListIterator listIterator(); // 获取List迭代器
ListIterator listIterator(int index); // 根据索引获取当前的位置的迭代器
List subList(int fromIndex, int toIndex); //截取某一段数据
default Spliterator spliterator(){} //获取可切分迭代器
上面的方法都比较简单,值得一提的是里面出现了ListIterator,这是一个功能更加强大的迭代器,继承于Iterator,只能用于List类型的访问,拓展功能例如:通过调用listIterator()方法获得一个指向List开头的ListIterator,也可以调用listIterator(n)获取一个指定索引为n的元素的ListIterator,这是一个可以双向移动的迭代器。
操作数组索引的时候需要注意,由于List的实现类底层很多都是数组,所以索引越界会报错IndexOutOfBoundsException。
说起List的实现子类:
ArrayList:底层存储结构是数组结构,增加删除比较慢,查找比较快,是最常用的List集合。线程不安全。
LinkedList:底层是链表结构,增加删除比较快,但是查找比较慢。线程不安全。
Vector:和ArrayList差不多,但是是线程安全的,即同步。
2.2.2 Set extend Collection
Set接口,不允许放入重复的元素,也就是如果相同,则只存储其中一个。
下面是源码方法:
int size(); //获取大小
boolean isEmpty(); //是否为空
boolean contains(Object o); //是否包含某个元素
Iterator iterator(); //获取迭代器
Object[] toArray(); //转化成为数组
T[] toArray(T[] a); //转化为特定类的数组
boolean add(E e); //添加元素
boolean remove(Object o); //移除元素
boolean containsAll(Collection> c); //是否包含所有的元素
boolean addAll(Collection extends E> c); //批量添加
boolean retainAll(Collection> c); //移除所有不存在于c集合中的元素
boolean removeAll(Collection> c); //移除所有在c集合中存在的元素
void clear(); //清空集合
boolean equals(Object o); //是否相等
int hashCode(); //计算hashcode
default Spliterator spliterator() {} //获取可分割迭代器
主要的子类:
HashSet
允许空值
通过HashCode方法计算获取hash值,确定存储位置,无序。
LinkedHashSet
HashSet的子类
有顺序
TreeSet
如果无参数构建Set,则需要实现Comparable方法。
亦可以创建时传入比较方法,用于排序。
2.2.3 Queue extend Collection
队列接口,在Collection接口的接触上添加了增删改查接口定义,一般默认是先进先出,即FIFO,除了优先队列和栈,优先队列是自己定义了排序的优先顺序,队列中不允许放入null元素。
下面是源码:
boolean add(E e); //插入一个元素到队列,失败时返回IllegalStateException (如果队列容量不够)
boolean offer(E e); //插入一个元素到队列,失败时返回false
E remove(); //移除队列头的元素并移除
E poll(); //返回并移除队列的头部元素,队列为空时返回null
E element(); //返回队列头元素
E peek(); //返回队列头部的元素,队列为空时返回null
主要的子接口以及实现类有:
Deque(接口):Queue的子接口,双向队列,可以从两边存取
ArrayDeque:Deque的实现类,底层用数组实现,数据存贮在数组中
AbstractQueue:Queue的子接口,仅实现了add、remove和element三个方法
PriorityQueue:按照默认或者自己定义的顺序来排序元素,底层使用堆(完全二叉树)实现,使用动态数组实现,
BlockingQueue: 在java.util.concurrent包中,阻塞队列,满足当前无法处理的操作。
(2) Map接口
定义双列集合的规范Map,每次存储一对元素,即key和value。
key的类型可以和value的类型相同,也可以不同,任意的引用类型都可以。
key是不允许重复的,但是value是可以重复的,所谓重复是指计算的hash值系统。
下面的源码的方法:
V put(K key, V value); // 添加元素
V remove(Object key); // 删除元素
void putAll(Map extends K, ? extends V> m); // 批量添加
void clear() // 移除所有元素
V get(Object key); // 通过key查询元素
int size(); // 查询集合大小
boolean isEmpty(); // 集合是否为空
boolean containsKey(Object key); // 是否包含某个key
boolean containsValue(Object value); // 是否包含某个value
Set keySet(); // 获取所有key的set集合
Collection values(); // 获取所有的value的set集合
Set> entrySet(); // 返回键值对的set,每一个键值对是一个entry对象
boolean equals(Object o); // 用于比较的函数
int hashCode(); // 计算hashcode
default V getOrDefault(Object key, V defaultValue) // 获取key对应的Value,没有则返回默认值()
default void forEach(BiConsumer super K, ? super V> action) {} // 遍历
default void replaceAll(BiFunction super K, ? super V, ? extends V> function) {} // 批量替换
// 缺少这个key的时候才会添加进去
// 返回值是是key对应的value值,如果不存在,则返回的是刚刚放进去的value
default V putIfAbsent(K key, V value) {}
default boolean remove(Object key, Object value) {} // 移除元素
default boolean replace(K key, V oldValue, V newValue) {} // 替换
default V replace(K key, V value) {} //替换
// 和putIfAbsent有点像,只不过传进去的mappingFunction是映射函数,也就是如果不存在key对应的value,将会执行函数,函数返回值会被当成value添加进去,同时返回新的value值
default V computeIfAbsent(K key,Function super K, ? extends V> mappingFunction) {}
// 和computeIfAbsent方法相反,只有key存在的时候,才会执行函数,并且返回
default V computeIfPresent(K key,BiFunction super K, ? super V, ? extends V> remappingFunction) {}
// 不管如何都会执行映射函数,返回value
default V compute(K key,BiFunction super K, ? super V, ? extends V> remappingFunction) {}
default V merge(K key, V value,BiFunction super V, ? super V, ? extends V> remappingFunction) {}
值得注意的是,Map里面定义了一个Entry类,其实就是定义了一个存储数据的类型,一个entry就是一个.
Map的常用的实现子类:
HashMap:由数组和链表组成,线程不安全,无序。
LinkedHashMap:如果我们需要是有序的,那么就需要它,时间和空间效率没有HashMap那么高,底层是维护一条双向链表,保证了插入的顺序。
ConcurrentHashMap:线程安全,1.7JDK使用锁分离,每一段Segment都有自己的独立锁,相对来说效率也比较高。JDK1.8抛弃了Segment,使用Node数组+链表和红黑树实现,在线程安全控制上使用Synchronize和CAS,可以认为是优化的线程安全的HashMap。
HashTable:对比与HashMap主要是使用关键字synchronize,加上同步锁,线程安全。
(二)总结
这些集合原始接口到底是什么?为什么需要?
我想,这些接口其实都是一种规则/规范的定义,如果不这么做也可以,所有的子类自己实现,但是从迭代以及维护的角度来说,这就是一种抽象或者分类,比如定义了Iterator接口,某一些类就可以去继承或者实现,那就得遵守这个规范/契约。可以有所拓展,每个子类的拓展不一样,所以每个类就各有所长,但是都有一个中心,就是原始的集合接口。比如实现Map接口的所有类的中心思想都不变,只是各有所长,各分千秋,形成了大千集合世界。
【作者简介】:
秦怀,公众号【秦怀杂货店】作者,技术之路不在一时,山高水长,纵使缓慢,驰而不息。个人写作方向:Java源码解析,JDBC,Mybatis,Spring,redis,分布式,剑指Offer,LeetCode等,认真写好每一篇文章,不喜欢标题党,不喜欢花里胡哨,大多写系列文章,不能保证我写的都完全正确,但是我保证所写的均经过实践或者查找资料。遗漏或者错误之处,还望指正。
平日时间宝贵,只能使用晚上以及周末时间学习写作,关注我,我们一起成长吧~