JavaSE 之 集合

集合框架 - 封装数据结构

要求:
1.方法的使用
2.原理的掌握
3.数据结构的实现

1.概念

集合是java提供的一种容器,主要负责存储任意数量的数据.

2.集合和数组的异同
1)相同点:

​ 都是容器,可以存储多个数据

2)不同点:

​ a.数组的长度是不可变的,集合的长度是可变的.

​ b.数组可以存储基本数据类型和引用数据类型,集合只能存储引用数据类型,如果要存储基本数据类型,则需要存储对应的包装类.

3.集合体系
Collection(I) - 单列集合
	|- List(I)

​		|-	ArrayList(C) - 数组

​		|-	LinkedList(C) - 双向链表

​		|-	Vector(C) - 数组

 	|- Set(I)

​		|-	HashSet(C) - 散列表(哈希表)

​			|-	LinkedHashSet(C) - 双向链表 + 散列表

​		|-	SortedSet(I)

​			|- TreeSet(C) - 二叉树

 	|- Queue(I)

​		|-	Deque(I)

​			|-	LinkedList(C) - 双向链表
Map(I) - 双列集合
|-	**HashMap**(C) - 散列表

	|-	LinkedHashMap(C) - 双向链表 + 散列表

|-	HashTable(C) - 散列表

|-	**ConcurrentHashMap**(C) - 散列表

|-	SortedMap(I)

	|-	TreeMap(C) - 二叉树

一、Collection(I) - 单列集合

1)创建方式

Collection con = new 实现类()

2)常用API
a. 普通常用方法:
add(E e):添加
remove(Object):根据元素进行删除
size():获取集合长度
isEmpty():判断集合是否为空
clear():清空集合
b. 集合相关方法:
boolean addAll(Collection<? extends E> c) 
boolean containsAll(Collection<?> c) 
boolean removeAll(Collection<?> c) 
boolean retainAll(Collection<?> c) 
c. 跟数组相关方法:

​ 集合转数组:

List<String> strList = new ArrayList<>();
//new String[0]指定了返回数组的类型,0节约了空间
String[] strArray = strList.toArray(new String[0]); 

​ 数组转集合:

String[] s = new String[]{"hello", "world", "haha"};
List<String> strList = Arrays.asList(s);   //将数组转为集合
strList.add("heihei");   //抛出UnsupportedOperationException 异常

​ 这是因为Arrays.asList()方法返回的并不是java.util.ArrayList,而是java.util.Arrays的一个内部类,这个内部类并没有实现集合的修改方法。

正确的转换方式:

用ArrayList进行封转(最简单的方式):

String[] s = new String[]{"hello", "world", "haha"};
List<String> strList = new ArrayList<>(Arrays.asList(s));

1.Iterable(I)

1)有且只有一个iterator(),用于获取迭代器
2)Iterable是Collection集合的父接口,给该集合中的元素提供可遍历的方式.
3)Iterator(I) - 迭代器
a. Iterator是一个接口,他的作用就是用于遍历迭代所有的元素.
b. 使用前提: 必须实现Iterable.
c. 常用API
	①.hasNext(): 询问有没有下一个元素
	②.next(): 移动游标到下一个位置
	③.remove(): 删除元素
4)foreach - 增强for循环- 简易版迭代器

​ a. JDK1.5之后出现的,其中原理就是实现了一个Iterator迭代器.

​ b. 只有实现Iterable接口的类才能使用迭代器和增强for循环,进行遍历集合.

​ c. 语法结构
​ for(集合/数组中元素的类型 变量名 : 要遍历的集合/数组 ){

​ }

​ d. 作用:专门用于遍历/迭代集合或数组
缺点:只能用于获取集合中的元素,不能删除元素.


2、泛型编程

1)概念

泛型本质上是提供类型的"类型参数",也可以称之为参数化类型。
我们可以给类、接口、变量、方法指定类型参数,可以通过参数限制操作的数据类型,从而保证了类型转换的绝对安全。

2)泛型的作用:

强制了集合存储固定的数据类型,提高了程序的安全性.
避免了ClassCastException的发生.

3)泛型的使用
a.自定义泛型类

​ ①泛型类一般用于类中的属性类型不确定的情况下.

b.泛型方法

​ ①是否拥有泛型方法,与其所在类是不是泛型类没有关系.

​ ②如果是static方法需要使用泛型,则需要在方法上标记泛型.

c.泛型接口

​ ①实现类实现接口,不实现泛型,在new的时候进行指定类型

​ ②实现类实现接口的同时指定泛型,接口名<指定类型>

4)泛型的高级使用
a. 通配符
①概念

​ 当使用泛型类或接口时,传递的数据泛型类型不确定可以通过通配符 <?> 表示

②注意

​ 一旦使用了通配符,那么只能使用Object类中的共性方法;集合中元素的自身方法无法使用

b. 泛型的上限与下限
①上限

​ 语法:类型名称**<? extends E>** 对象名称

​ 范围:E只能取 E的子类 和 E本身

②下限

​ 语法:类型名称**<? super E>** 对象名称

​ 范围:E只能取 E的父类 和 E本身

3.List(I)

1.特点:

​ a.元素有序

​ b.元素可重复

2.常用方法
1)从Collection继承过来的方法
2)带有index下标的方法

​ get(int index)

​ set(int index,Object obj)

​ remove(int index)

​ List subList(int fromIndex, int toIndex)

​ boolean addAll(int index, Collection<? extends E> c)


4.ArrayList©

1)底层实现:数组
2)特点:
a.按照顺序排列,每个元素都带有标号.
b.除了有标号是连续,内存中物理空间也是连续的.
3)优缺点:
优点:查询元素快(可以通过下标(索引),快速访问到指定位置上的元素)

缺点:插入/删除慢,需要连续的物理空间,空间使用率低(插入,删除都是需要移动元素,所以元素移动需要执行时间,导致效率降低,练习的物理空间比较占用内存)

5.LinkedList(C)

1)底层数据结构:双向链表

​ 底层实现:节点(数据 + 下一个/上一个节点的引用)

2)特点:
a.链表的存储结构固定顺序,物理结构空间不连续.
b.没有标号,有头节点和尾节点,所以所有节点的访问都可以从头/尾节点出发.
3)优缺点:
优点:插入 / 删除效率高,不需要连续的物理内存空间,所以孔家使用率高.
缺点:查询效率低.
4)特有方法(带有First / Last的方法)
void addFirst(E e)  
void addLast(E e)  
E getFirst() 
E getLast() 
E removeFirst() 
E removeLast()

6.Vector© - 和ArrayList一样

1)底层数据结构:顺序表

​ 底层实现:数组

2)特点:
a.按照顺序排列,每个元素都带有标号.
b.除了有标号是连续,内存中物理空间也是连续的.
3)优缺点:
a.优点:

​ 查询元素快(可以通过下标(索引),快速访问到指定位置上的元素)

b.缺点:

​ 插入/删除慢,需要连续的物理空间,空间使用率低(插入,删除都是需要移动元素,所以元素移动需要执行时间,导致效率降低,练习的物理空间比较占用内存)

4)ArrayList 和 Vector的区别
a.效率问题
	①.ArrayList线程不安全,效率高
	②.Vector线程安全,效率低
b.扩容问题
	①.ArrayList容量为原容量的1.5倍
	②.Vector为原容量的2倍

7.Queue(I) - 队列

1.特点:

​ a. 先进先出

​ b. 队列也是线性结构,是有顺序的,但是没有标号,不能从中间插入 / 删除数据

2.特有方法

​ offer():向队列尾部追加元素

​ peek():从队列头部获取元素(队列不变)

​ poll():从队列头部获取元素(队列改变)

3.Deque(I)
1)Queue的子接口,可以作为双端队列 / 栈实现
2)作为双端队列实现
a 特点:

​ ① 先进先出

​ ② 两头可进,两头可出

b 特有方法

​ 带有 First / Last方法

​ 例如:offerFirst( ) / offerLast( )

3)作为栈实现(LIFO)
a 压栈

​ ① push( )

b 弹栈

​ ② pop( )


8、Set(I) - 无序且唯一

1.特点

​ a.set集合的物理空间是不连续的,添加没有顺序(不是随机)
​ b.不允许有重复值
​ c.最多只允许包含一个null值

2.HashSet©
1)底层实现: 哈希表(散列表) - 不是数据结构!!!
2)保证元素的唯一性的方式
a.HashsSet是根据对象的哈希值来确定元素在集合中存储的位置.
b.想要保证元素的唯一性就必须依赖于两个方法: hashCode() 和 equals()
3)HashSet的存储过程
①.调用自身hashCode方法,计算储存位置
②.如果位置上没有元素,则直接存入
③.如果位置上有元素,则调用自身的equals()和该位置上的元素进行比较
④.如果不相同,遍历下一个节点继续使用equals进行判断,直到链表末尾,如果都不一致,则直接存入链表末尾.
⑤.如果相同,则进行覆盖.
3.LinkedHashSet(C)
1)底层实现:哈希表 + 双向链表
2)特点:有序且唯一
4.TreeSet(C)
1)底层结构:二叉树(红黑树)
2)父接口:SortedSet(I),可排序集合
3)特点:

​ a 元素唯一

​ b 没有下标

4)作用:对元素进行排序

9、Comparable - 自然顺序排序

1)使用:

​ a 在实体类中实现Comparable接口

​ b 重写Comparable的compareTo(Object o)

2)比较规则:

​ 比较当前对象(this)和传入的对象(o)

​ this > o:返回负数,小的放左边

​ this = o:返回0, 不存入

​ this < o:返回正数,大的放右边

正序 和 倒叙

​ this在前,o在后 -> 前比后 -> 正序
​ o在前,this在后 -> 后比前 -> 倒序

10、Comparator - 比较器排序

1)使用:

​ a 在构造方法中以匿名内部类参数实现Comparator接口

​ b 重写Comparator的compare(Object o1,Object o2)

2)比较规则:

​ 比较当前对象(o1)和传入的对象(o2)

​ o1 < o2:返回负数,小的放左边

​ o1 = o2:返回0, 不存入

​ o1 >o2:返回正数,大的放右边

正序 和 倒叙

​ o1在前,o2在后 -> 前比后 -> 正序
​ o2在前,o1在后 -> 后比前 -> 倒序


二、Map(I) - 双列集合(key - value)

	|	-	HashMap(C)

​				|	-	LinkedHashMap(C)

​	|	-	TreeMap(C)

​	|	-	HashTable(C)

​				|	-	properties(C)

​	|	-	ConcurrentHashMap(C)

1.Map

1)概述

​ Map是一种键值对(key - value)集合,每一个元素都包含一个key对象和value对象;用于保存有映射关系的数据

2)特点

​ a.key和value都可以使用泛型,意味着可以使用所有的引用类型

​ b.key不可重复(Set),value可重复(List)

​ c.key和value一对一映射,可以通过指定key找到value

3)常用方法

​ V put(K key, V value)

​ V get(Object key)

​ V remove(Object key)

Map集合的遍历方式:

方式一:获取所有的key,并获取对应的Set类型

​ Set keySet()

方式二:获取所有的value,并获取对应的Collection类型

​ Collection values()

方式三:获取键值对entry,并获取对应的Set类型

​ Set<Map.Entry<K,V>> entrySet()

​ ①.在Set中只能放一种泛型类型,所以键值对(Entry)也是一个整体泛型类型

​ ②.Entry属于Map的内部接口,用法跟内部类一样

​ ③.Entry的结构:(key : value)

​ a.entry.getKey() - 获取key

​ b.entry.getValue() - 获取value

2.HashMap

1)底层实现:
JDK1.7:数组 + 链表

​ 采用头插法:新进来的entry节点会取代原有头节点位置,原有节点会顺延到链表中

优点:后进的元素查找效率高

缺点:容易死循环

JDK1.8:数组 + 链表 | 红黑树

​ 采用尾插法:在进行扩容操作之后,链表会保持原本的顺序,也就是保存之前节点的引用关系,

优点:后进的元素查找效率高

缺点:容易死循环

2)HashMap的存储过程

​ a.计算key的hashCode值,确定内存中的位置

​ b.如果位置上有元素,使用equals判断是否一致

​ c.如果一致,则进行覆盖

​ d.如果不一致,则遍历下一个节点继续进行equals判断,直到链表末尾都不一致,

​ 则添加到链表末尾,能解决链表成环的问题(死循环)

3)初始化空间大小

​ a.如果太小,则会造成链表多,效率低

​ b.如果太大,空间浪费

4)扩容机制

​ 加载因子:扩容的条件(默认0.75f),调用resize()方法扩容

​ *— 和时间成本成反比,和空间成本成正比

3.LinkedHashMap

​ 1)底层实现:散列表 + 双链表

​ 2)特点:key按照存储顺序存放

4.TreeMap

​ 1)底层实现: 二叉树

​ 2)作用:依赖于自然排序/比较器排序,对key进行排序

​ 3)创建方式

​ new TreeMap(): 默认排序方式

​ new TreeMap(new Comparator):指定比较器排序

5.ConcurrentHashMap

1)底层实现:
JDK1.7:数组 + 链表

​ 具体实现:Segment数组 + hashEntry[ ]

​ :重新计算hashCode值

// 根据 key 的 keyCode 重新计算 hash 值。
int hash = hash(key.hashCode());
// 搜索指定 hash 值在对应 table 中的索引。
int i = indexFor(hash, table.length);	//i = (n - 1) & hash

JDK1.8:数组 + 链表 | 红黑树

​ 具体实现:CAS + synchronized

​ :直接使用hashCode值

if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
2)CAS(Compare And Swap)比较交换 - 乐观策略

​ a.存储位置(V)

​ 原值(A)

​ 新值(B)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值