集合
存储结构
存储结构:顺序存储(数组)、链式存储(链表)
逻辑结构:栈、队列、线性表、树、图
线性表有两种存储方式,顺序表和链表
栈有顺序栈和链栈
分类
集合分为两大类
单列集合、双列集合
单列集合(Collection)又分为List(ArrayList、LinkedList、Vector)和Set(HashSet、LinkedHashSet、TreeSet)
双列集合(Map)又分为HashMap、TreeMap、Hashtable、Properties
ArrayList线程是不安全的、Vector线程是安全的、LinkedList线程是不安全的、Map里面的Hashtable、Properties线程是安全的
抑制警告错误
@SuppressWarnings("{all}");
线程安全
Vector、Hashtable、Properties
List接口
方法 | 作用 |
---|
add | 添加一个元素 |
remove | 删除指定元素(可以是下标也可以是数据,如果是int类型就需要转化为包装类Integer) |
size | 获取元素个数(集合长度) |
isEmpty | 判断是否为空 |
clear | 清空 |
addAll | 添加多个元素(添加另一个集合中的所有元素) |
containsAll | 查找多个元素是否都存在 |
removeAll | 删除多个元素 |
注意:所有带All的方法里面传的都是集合
List list = new ArrayList<>();
list.add(12);
list.add(13);
list.add(14);
System.out.println(list);
System.out.println("=========");
list.remove((Integer)13);
System.out.println(list);
System.out.println("=========");
System.out.println(list.size());
System.out.println("=========");
list.clear();
System.out.println(list);
List list1 = new ArrayList();
list1.add("红楼梦");
list1.add("三国演义");
list.addAll(list1);
System.out.println(list);
System.out.println(list.containsAll(list1));
list.add(4);
list.removeAll(list1);
System.out.println(list);
迭代器的执行原理
Iterator it = set.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
扩展:当创建迭代器对象后(Iterator it = set.iterator()),可以使用itit快捷键快速生成迭代器的while循环
for (Object s:list
) {
System.out.println(s);
}
Vector
| 底层结构 | 线程安全效率 | 扩容机制 |
---|
ArrayList | 可变数组 | 不安全,效率高,查询快 | 如果有参构造1.5倍。无参第一次是10,第二次扩容1.5倍 |
Vector | 可变数组 | 安全,效率不高,查询快 | 如果无参,默认是10,满后,2倍扩容。如果指定大小,则每次直接按2倍扩容 |
LinkedList | 双向链表 | 不安全,增删快,查询慢 | |
LinkedList
LinkedList实现了双向链表和双端队列的特点
可以添加任意元素,包括null
线程是不安全的,没有实现同步
Set接口
(1)无序(添加和取出的顺序不一致,没有索引)
(2)不允许重复元素,所以最多包含一个null
(3)JDK中Set实现的子类有HashSet、TreeSet、LinkedHashSet等等
遍历方式
(1)迭代器
(2)增强for
(3)不能使用索引的方式来获取
HashSet
集合中的元素是无序排列的
HashSet类是非线程安全的允许集合元素为null(只能有一个)
只能使用增强for循环或迭代器遍历
哈希表是以数组加链表加红黑树来存储数据的。
当相同哈希值的元素大于等于8个时(即哈希冲突的元素超过8个),就会使用红黑树来储存,因为红黑树查询效率很高。
哈希表的存储是首先通过哈希函数来计算哈希值(hashcode())来确定你要放到哈希表里面的哪一个位置。
当往里面存储元素时,首先会调用hashcode()方法,计算哈希值,然后判断哈希表中有没有这个值,如果没有那么直接储存,如果有,即发生了哈希冲突,然后再调用equals方法判断所存储的元素和相同哈希值的元素是否一样,如果不同,则将新的也进行存储,如果相同,则不存储。
如果新添加的元素与集合中已有的某个元素哈希值相同,此时还需要调用equals(Object obj)比较
如果equals(Object obj)方法返回true,说明新添加的元素与集合中已有的某个元素的属性值相同,那么新添加的元素不存入集合
如果equals(Object obj)方法返回false, 说明新添加的元素与集合中已有的元素的属性值都不同, 那么新添加的元素存入集合
hashCode是对象的一个int类型的哈希值,散列值,是由对象的还是从的()方法提供的,通过哈希值来确定存储的位置
LinkedHashSet
LinkedHashSet继承自HashSet,它的添加、删除、查询等方法都是直接用的HashSet的,唯一的不同就是它使用LinkedHashMap存储元素
(1)LinkedHashSet的底层使用LinkedHashMap存储元素。
(2)LinkedHashSet是有序的,它是按照插入的顺序排序的。
(3)LinkedHashSet是不支持按访问顺序对元素排序的,只能按插入顺序排序
TreeSet
TreeSet 是一个有序的集合,它的作用是提供有序的Set集合。它继承于AbstractSet抽象类,实现了NavigableSet<E>, Cloneable, java.io.Serializable接口。
TreeSet 继承于AbstractSet,所以它是一个Set集合,具有Set的属性和方法。
TreeSet 实现了NavigableSet接口,意味着它支持一系列的导航方法。比如查找与指定目标最匹配项。
TreeSet 实现了Cloneable接口,意味着它能被克隆。
TreeSet 实现了java.io.Serializable接口,意味着它支持序列化。
TreeSet是有序的Set集合,因此支持add、remove、get等方法。
NavigableSet一样,TreeSet的导航方法大致可以区分为两类,一类时提供元素项的导航方法,返回某个元素;另一类时提供集合的导航方法,返回某个集合。
lower、floor、ceiling 和 higher 分别返回小于、小于等于、大于等于、大于给定元素的元素,如果不存在这样的元素,则返回 null。
Map接口
HashMap
Properties继承自Hashtable,LinkedHashMap继承自HashMap、实现了Map接口
Map与Collection并列从在,用于保存具有映射关系的数据:Key-Value
Map中的Key和Value可以是任何引用类型的数据,会封装到HashMap对象中
Map中的key不允许重复,原因和HashSet一样
Map中的value可以重复
Map中的key可以为null,value也可以为null,注意可以null只有一个,value为null可以有多个
常用String类作为Map的key
key和value之间存在单向一对一的关系,即通过指定的key可以找到对应的value
**注意**:当key相同时,会将value的值进行替换
String key = null;
Integer integ = null;
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
key = (String)iter.next();
integ = (Integer)map.get(key);
}
Integer value = null;
Collection c = map.values();
Iterator iter= c.iterator();
while (iter.hasNext()) {
value = (Integer)iter.next();
}
JDK1.7与1.8区别
不同 | JDK 1.7 | JDK 1.8 |
---|
存储结构 | 数组 + 链表 | 数组 + 链表 + 红黑树 |
初始化方式 | 单独函数:inflateTable() | 直接集成到了扩容函数resize() 中 |
hash值计算方式 | 扰动处理 = 9次扰动 = 4次位运算 + 5次异或运算 | 扰动处理 = 2次扰动 = 1次位运算 + 1次异或运算 |
存放数据的规则 | 无冲突时,存放数组;冲突时,存放链表 | 无冲突时,存放数组;冲突 & 链表长度 < 8:存放单链表;冲突 & 链表长度 > 8:树化并存放红黑树 |
插入数据方式 | 头插法(先讲原位置的数据移到后1位,再插入数据到该位置) | 尾插法(直接插入到链表尾部/红黑树) |
扩容后存储位置的计算方式 | 全部按照原来方法进行计算(即hashCode ->> 扰动函数 ->> (h&length-1)) | 按照扩容后的规律计算(即扩容后的位置=原位置 or 原位置 + 旧容量) |
Hashtable
和HashMap一样,Hashtable 也是一个散列表,它存储的内容是键值对(key-value)映射。
Hashtable 继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口。
Hashtable 的函数都是同步的,这意味着它是线程安全的。它的key、value都不可以为null。此外,Hashtable中的映射不是有序的。
Hashtable 的实例有两个参数影响其性能:初始容量 和 加载因子。容量 是哈希表中桶 的数量,初始容量 就是哈希表创建时的容量。注意,哈希表的状态为 open:在发生“哈希冲突”的情况下,单个桶会存储多个条目,这些条目必须按顺序搜索。加载因子 是对哈希表在其容量自动增加之前可以达到多满的一个尺度。初始容量和加载因子这两个参数只是对该实现的提示。关于何时以及是否调用 rehash 方法的具体细节则依赖于该实现。
通常,默认加载因子是 0.75, 这是在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查找某个条目的时间(在大多数 Hashtable 操作中,包括 get 和 put 操作,都反映了这一点)。
HashMap与Hashtable区别
HashMap和Hashtable都实现了Map接口
HashMap是非synchronized,而Hashtable是synchronized
HashTable使用Enumeration,HashMap使用Iterator
HashMap允许将 null 作为一个 entry 的 key 或者 value,而 Hashtable 不允许。
LinkedHashMap
LinkedHashMap是HashMap的子类,与HashMap有着同样的存储结构,但它加入了一个双向链表的头结点,将所有put到LinkedHashmap的节点一一串成了一个双向循环链表,因此它保留了节点插入的顺序,可以使节点的输出顺序与输入顺序相同。
LinkedHashMap可以用来实现LRU算法
LinkedHashMap同样是非线程安全的,只在单线程环境下使用。
LinkedHashMap是否允许空 | Key和Value都允许空 |
---|
LinkedHashMap是否允许重复数据 | Key重复会覆盖、Value允许重复 |
LinkedHashMap是否有序 | 有序 |
LinkedHashMap是否线程安全 | 非线程安全 |
TreeMap
TreeMap是一个有序的key-value集合,它是通过红黑树实现的。
TreeMap继承于AbstractMap,所以它是一个Map,即key-value集合。
TreeMap实现了NavigableMap接口,意味着它支持一系列的导航方法。比如返回有序的key集合。
TreeMap实现了Clonable接口,意味着它能被克隆。
TreeMap实现了java.io.Serializable接口,意味着它支持序列化。
TreeMap基于红黑树实现,该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的Comparator进行排序,具体取决于使用的构造方法
TreeMap是非同步的,它的iterator方法返回的迭代器是fail-fastl的
Properties
表示一个持久的集,可以存在流中或者从流中加载。用来读取Java的配置文件,在Java中为.properties为后缀名的文本文件。
是 Hashtable子类,map集合方法都可以用。
常用的方法
getProperties(String key):通过指定的键搜索属性。
setProperties(String key,String value):相当于Hashtable中的put方法
load(InputStream inStream):方法读取属性列表(键和元素对)从输入字节流。输入流是一个简单的面向行的格式为负载器(Reader)指定的。
store(OutputStream outStream):将此属性列表(键和元素对)写入此Properties表中,以适合使用load(InputStream)方法加载到Properties表中的格式输出流。
clear() 清除所有加载。
方法
containsKey:查找键是否存在
keySet:获取所有的键
entrySet:获取所有关系
values:获取所有的值
六大遍历
Set keyset = map.keySet();
for (Object key:keyset
) {
System.out.println(key+"-"+map.get(key));
}
Iterator iterator = keyset.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
/取出所有的value,只能显示value
Collection values = map.values();
for (Object obj:values
) {
System.out.println(obj);
}
Iterator iterator = values.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
Set entrySet = map.entrySet();
for (Object obj:entrySet
) {
System.out.println(obj);
}
Iterator iterator = entrySet.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
扩容
HashMap当链表的数超过8,并且数组的长度超过64,就会进行树化,而不是进行扩容
集合和数组的区别
(1)数组是固定长度的,集合可变长度的
(2)数组可以存储基本数据类型,也可以存储引用数据类型
(3)数组存储的元素必须是同一个数据类型,集合存储的对象可以是不同数据类型
使用集合框架的好处
(1)容量自增长
(2)提供了高性能的数据结构和算法,使编码更轻松,提高了程序速度和质量
(3)允许不同API之间的互操作,API之间可以来回传递集合
(4)可以方便地扩展或改写集合,提高代码复用性和操作性
(5)通过使用JDK自带的集合类,可以降低代码维护和学习新API成本
List、Set、Map三者的区别
List:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多个null元素,元素都有索引。常用的实现类有ArrayList、LinkedList和Vector
Set:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一个null元素,必须保证元素唯一性。Set接口常用实现类是HashSet、LinkedHashSet和TreeSet
Map:是一个键值对集合,存储键、值和之间的映射,Key无序、唯一,value不要求有序,允许重复。Map没有继承于Collection接口,从Map集合中检索元素时,只要给出键对象,就会返回对饮的值对象