集合Set: 元素无序(元素的存取顺序不一致)
元素不重复(元素重写了 hashCode() 和 equals()方法 )
Set的三个常用子类:
1) HashSet: 底层数据结构是 哈希表
特点: 元素是无序的,元素唯一性(重写 hashCode() 和 equals() 方法),
线程不安全(不同步),允许键入 null 值
2) LinkedHashSet: 底层数据结构是 链表 和 哈希表
特点: 链表保证了元素的有序(存取顺序一致)
哈希表保证了元素的唯一性(重写了 hashCode() 和 equals() 方法)
线程不安全(不同步),允许键入 null 值
3)TreeSet:底层数据结构是 二叉树
特点:元素唯一并且可以自定义元素的排列顺序
线程不安全(不同步),不能键入 null 值
TreeSet 自定义比较的两种方式:
方式一: 通过 TreeSet() 的无参构造 + 元素实现 Comparable接口
并重写 它的 compareTo() ,通过返回的整数的 正 负 0 来排序元素
而且元素必须重写 hashCode() 和 equals()(自动生成)
方式二 : 通过 TreeSet( Comparator<? super E> comparator
) 的有参构造
并且重写Comparator接口中的 compare( ) 方法 来自定义排列顺序
通过返回的整数的 正 负 0 来排序元素
Map集合:
双列集合,里面存的是映射关系(键值对)
Map集合的所有数据结构只与 键 有关,且键唯一
一个键只能对应一个值,当键相同时,值被覆盖
Map的功能:
1)添加功能
V put(K key,V value):添加元素。还相当于替换
如果键是第一次存储,就直接存储元素,返回 null
如果键不是第一次存在,就用值把以前的值替换掉,并返回以前的值
2)删除功能
void clear(): 移除所有的键值对元素
V remove(Object key):根据 键 删除 键值对元素,并把 值 返回
3)判断功能
boolean containsKey(Object key): 判断集合是否包含指定的 键
boolean containsValue(Object value): 判断集合是否包含指定的 值
boolean isEmpty(): 判断集合是否为空
4)获取功能
Set<Map.Entry<K,V>> entrySet(): 返回一个 键值对 的Set集合(用于遍历集合 方式一)
Set<K> keySet(): 获取集合中所有 键 的集合 (用于遍历集合 方式二)
Collection<V> values(): 获取集合中所有值的集合
V get(Object key): 根据键获取值
5)长度功能
int size():返回集合中的键值对的对数
Map的子集合: Map集合的数据结构都只与 键 有关,与值无关
HashMap:
底层的数据结构是 哈希表
元素无序 (存取顺序不一致),且唯一(重写 hashCode() 和 equals() 方法)
线程不安全(非同步),但效率高
允许存入 null 键 和 null 值
LinkedHashMap:
底层数据结构是 链表 和 哈希表
元素有序(存取顺序一致,链表结构),且唯一(哈希表 重写 hashCode() 和 equals()方法 )
线程不安全(非同步),但效率高
允许存入 null 键 和 null 值
TreeMap:
底层数据结构是 二叉树
元素唯一,并且可以自己定义排列顺序(自然排序 / 比较器排序)
线程不安全,但效率高
不允许存入 null 键, 但允许存入 null 值
TreeMap必须实现的排序方式:
1)自然排序: TreeMap()的无参构造函数 + 元素类实现 Comparable 接口,并重写其中的 compareTo() 方法
2)比较器排序:TreeMap( Comparator< ? super E >comparator ) 有参构造 + 重写该接口的 compare() 方法
*********面试题:
HashTable 与 HashMap 的区别?
1)HashMap 线程不安全( 非同步的),效率高
HashTable线程安全(同步的),效率低
2)HashMap 允许存入 null 键 和 null 值
HashTable不允许存入 null 键 和 null 值
3 ) 初始容量大小和每次扩充容量大小的不同 :
①创建时如果不指定容量初始值,Hashtable 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。 HashMap 默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。 ②创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方 大小,也就是说 HashMap 总是使用2的幂次方作为哈希表的大小,后面会介绍到为什么是2的幂次方。
4 ) 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8) 时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。
5)HashMap 实现 Map接口
HashTable 也实现了Map接口,继承自Dictionary类
6)HashMap 有 containsKey() 和 containsValue() 方法
HashTable只有 contains()方法,容易引起误会使用
**********HashMap 与 HashSet 的区别:
HashMap 原理:
HashMap是基于哈希表的Map接口的非同步实现.在Java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造,HashMap也不例外.HashMap实际上是一个”链表的数组”的数据结构,每个元素存放链表头结点的数组,即数组和链表的结合体.HashMap底层就是一个数组结构,数组中的每一项又是一个链表.当新建一个HashMap的时候,就会初始化一个数组,Entry就是数组中的元素,每个Map.Entry其实就是一个key-value对,它持有一个指向下一个元素的引用,就构成了链表.
HashTable原理:
Hashtable是基于哈希表的实现.通过使用put(Object key,Object value)方法把两个对象进行关联,需要时用get(Object key)取得与key关联的值对象.还可以查询某个对象的索引值等等.这里的get方法查找一个对象时与Vector中的get方法在内部实现时有很大不同,在一个Hashtable中查找一个键对象要比在一个Vector中快的多.这是因为Hashtable使用了一种哈希表的技术,在Java每个对象缺省都有一个通过Object的hashCode()方法获得的哈希码,Hashtable就是利用这个哈希码实现快速查找键对象的.
HashMap的底层实现
JDK1.8之前
JDK1.8 之前 HashMap 由 数组+链表 组成的(“链表散列” 即数组和链表的结合体),数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的(HashMap 采用 “拉链法也就是链地址法” 解决冲突),如果定位到的数组位置不含链表(当前 entry 的 next 指向 null ),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度依然为 O(1),因为最新的 Entry 会插入链表头部,即需要简单改变引用链即可,而对于查找操作来讲,此时就需要遍历链表,然后通过 key 对象的 equals 方法逐一比对查找.
所谓 “拉链法” 就是将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。
JDK1.8之后
相比于之前的版本, JDK1.8之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。
TreeMap、TreeSet以及JDK1.8之后的HashMap底层都用到了红黑树。红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。
集合工具类Collections:针对集合操作的工具类
常用方法:
public static <T> void sort(List<T> list): 排序,默认按照自然顺序
public static <T> int binarySearch(List<?> list,T key): 二分查找
public static <T> T max(Collection<?> coll): 获取最大值
public static void reverse(List<?> list): 反转
public static void shuffle(List<?> list): 随机置换(应用:斗地主的洗牌功能)
集合框架图解: