一.集合
-
引入集合
集合和数组的比较
集合和数组的相似点
都可以存储多个对象,对外作为一个整体存在
数组的缺点
长度必须是在初始化的时候指定 而且固定不变集合框架:
集合框架使用方便的接口和类 它们位于java.util包中,存放在集合中的数据,被叫元素(element)
容器的分类图
集合框架:
Collection接口存储一组不唯一,无序的对象
List接口存储一组不唯一,有序(索引顺序)的对象
Set接口存储一组唯一,无序的对象
Map接口存储一组键值对象,提供Key和value的映射
key唯一 无序
value 不唯一,无序的
二.List集合
List集合主要的实现类有ArrayList和LinkedList,分别是数据结构中的顺序结构表额链表的实现
List:
特点 :有序的 不唯一(可重复)
ArrayList:
在内存中分配连续的空间,实现长裤可变的数组 (顺序表结构)
优点:遍历元素和随机访问元素的效果比较高
缺点:添加和删除需要大量的元素移动效率低,按照内容查询效率低
顺序图
LinkedList:
采用双向链表存储方式
缺点:遍历和随机访问元素效率低
优点:插入.删除元素效率高(但是前提是先低效率查询才可,如果插入删除发生在头尾可以减少查询的次数)
双向链表
Arraylist使用:
public class TestArrayList {
public static void main(String[] args) {
//创建一个ArrayList对象
ArrayList<Integer> list = new ArrayList();
//向集合中添加多分数
list.add(78);//加到最后
list.add(89);
list.add(59);
list.add(89);
list.add(2,50);//加到指定位置,底层发生了大量的元素后移
ArrayList list2 = new ArrayList();
list2.add(20);//加到最后
list2.add(60);
list2.add(99);
list1.addAll(0,list2);
System.out.println(list.size());
System.out.println(list);
//遍历元素
//方法1:使用for循环
for (int i = 0; i <list.size() ; i++) {
//获取第i个元素
int elem = list.get(i);
//输出第i个元素
System.out.println(i+" "+elem);
}
//方法2:使用增城的for循环
for (Object elem:list) {
System.out.println(elem);
}
//方法3 Iteratorx使用迭代器
Iterator<Integer> it = list.iterator();
while (it.hasNext()){//还有元素,没有元素就结束循环
//如果有,就取出来
int elem = it.next();
//输出
System.out.println(elem);
}
}
}
类的方法
ArrayList底层就是一个长度可以动态增长的Object数组;在JDK1.7中无参数构造方法创建ArrayList对象是,默认长度是10,
在JDK1.8 中使用无参数购房者方法创建ArrayList对象,默认数组长度是0 第一次添加元素,容量不足就要进行扩容
LinkedList的使用
public class TestLinkedayList {
public static void main(String[] args) {
//创建一个LinkedList对象
List<Integer> list = new LinkedList();
//向集合中添加多分数
list.add(78);//加到最后
list.add(89);
list.add(59);
list.add(89);//自动封装
list.add(new Integer(78));
list.add(2,50);//加到指定位置,底层发生了大量的元素后移
ArrayList list2 = new ArrayList();
list2.add(20);//加到最后
list2.add(60);
list2.add(99);
list.addAll(0,list2);
System.out.println(list.size());//元素个数
System.out.println(list);
list.remove(1);//删除索引1的内容
System.out.println(list);
System.out.println(list.isEmpty());
//遍历元素
//方法1:使用for循环
for (int i = 0; i <list.size() ; i++) {
//获取第i个元素
int elem = list.get(i);
//输出第i个元素
System.out.println(i+" "+elem);
}
//方法2:使用增城的for循环
for (Object elem:list) {
System.out.println(elem);
}
//方法3 Iteratorx使用迭代器
Iterator<Integer> it = list.iterator();
while (it.hasNext()){//还有元素,没有元素就结束循环
//如果有,就取出来
int elem = it.next();
//输出
System.out.println(elem);
}
}
}
将ArrayList替换成LinkedList之后不变
运算结果没有变
执行结果的功能代码没有变
ArrayList替换成LinkedList之后变化
底层结构变了
ArrayList是数组 LinkedList 双向链表
具体的执行过程变化了list.add(2,99)//在指定位置添加元素
ArrayList:大量的后移元素
LinkedList:不需要大量的移动元素,修改节点的指向就可以
使用那个会更好
根据使用场合来定
大量的根据索引查询操作,大量的遍历操作(按照索引0-n-1逐个查询般),建议使用ArrayList
如果存在较多的添加,删除操作建议使用LinkedList
LinkedList增加了那些方法
增加了对添加.删除.获取首位元素的方法
addFirst(); addLast(); removeFirst(); removeLast(); getFirst(); getLast();
LinkedList的底层是一个双向链表,实现了Deque接口 所以除了可以作为线性表来使用,还可以当做队列和栈来使用
Deque和Queue的实现类
1.ArrayDeque 顺序栈 数组
2.LinkedList 链栈 链表
理解java中栈和队列的接口的实现类
public class TestLinkedList2 {
public static void main(String[] args) {
//摞盘子
Deque<String >deque1 = new LinkedList<>();
deque1.push("盘子1");
deque1.push("盘子2");
deque1.push("盘子3");
System.out.println(deque1.size());
System.out.println(deque1.peek());
System.out.println(deque1.peek());
while(!deque1.isEmpty()){
String elem = deque1.pop();
System.out.println(elem);
}
System.out.println(deque1.size());
}
}
三. Set集合的使用
Set:
t特点:无序 唯一的(不重复)
HashSet
采用Hashtable哈希表存储结构
优点:添加速度快 查询速度快,删除速度快
缺点:无序
LinkedHashSet
采用哈希表存储结构,同时使用链表维护次序
有序(添加顺序)
TreeSet
采用二叉树(红黑树)的存储结构
优点:有序 查询速度比List快(按照内容查询)
缺点:查询速度没有HashSet快
使用各种Set集合类存储课程名称
public class TestSet1 {
public static void main(String[] args) {
//创建一个集合Set对象
// Set<String > set = new HashSet<>();
//Set<String> set = new LinkedHashSet<>();
Set<String> set = new TreeSet<>();
//添加多个课程
set.add("java");
set.add("Otacle");
set.add("HTML");
//输出课程
System.out.println(set.size());
System.out.println(set);
//不可以使用for循环变量set
/*for (int i = 0; i <set.size() ; i++) {
set.get(i);
}*/
//支持增强的for循环
for (String elem:set
) {
System.out.println(elem);
}
//支持Iterator
Iterator<String> it = set.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
}
}
总结:
HashSet 哈希表 唯一的 无序的
LinkedHashSet 哈希表+链表 唯一 有序的 (添加顺序)
TreeSet 红黑树 一种二叉平衡树 唯一的 有序的 (自然顺序)
List针对Collection增加了一些关于索引位置的操作的方法get(i)
add(i,elem),remove(i),set(i,ele,)
Set是无序的,不可能提供关于索引位置操作的方法,set针对Collection没有增加任何方法
List的遍历方式 for循环,for-eacch循环 Iterator迭代器 流失编程 forEach
Set的遍历方式 for- each循环 Iterator迭代器 流式编程 forEach
HashSet LinkedHashSet 需要类实现hashCode()和equals();
TreeSet 需要类实现 Comparable接口并指定比较规则
四.哈希表
哈希表的结构和特点
hashtable 也叫散列表,特点快 很快 神奇的快
结构 结构有很多种 最流行的最容易理解 顺序表+链表
主要结构:顺序表每个顺序表的节点在一个单独引出一个链表
哈希表是如何添加数据的
计算哈希 码(调用hashCode(),结果是一个int值,整数的哈希码取自身即可)
计算在哈希表中的存储位置 y=k(x)=x%元素长度
x:哈希码 k(x) 函数y:在哈希表中的存储位置
存入哈希表
情况1:一次添加成功
情况2:多次添加成功(出现了冲突,调用equals()和对应链表的元素进行比较,比较到最后,结果都是false,创建新节点,存储数据,并加入链表末尾)
情况3:不添加(出现了冲突,调用equals()和对应链表的元素进行比较, 经过一次或者多次比较后,结果是true,表明重复,不添加)
结论1:哈希表添加数据快(3步即可,不考虑冲突)
结论2:唯一、无序
哈希表是如何查询数据的
和添加数据的过程是相同的
情况1 一次找到
情况2 多次找到
情况3 找不到
哈希表查询数据块
hashCode和equals有什么作用
HashCode();计算哈希码,是一个整数,根据哈希码可以计算出数据在哈希表中的存储位置
equals(); 添加时出现了冲突, 徐亚通过equals进行比较,判断是否相同查询时也需要使用equals();进行比较,判断是否相等
如何减少冲突
哈希表的长度和表中的记录数的比例--装填因子:
如果Hash表的空间远远大于最后实际存储的记录个数,则造成了很大的空间浪费, 如果选取小了的话,则容易造成冲突。 在实际情况中,一般需要根据最终记录存储个数和关键字的分布特点来确定Hash表的大小。还有一种情况时可能事先不知道最终需要存储的记录个数,则需要动态维护Hash表的容量,此时可能需要重新计算Hash地址。
装填因子=表中的记录数/哈希表的长度, 4/ 16 =0.25 8/ 16=0.5
如果装填因子越小,表明表中还有很多的空单元,则添加发生冲突的可能性越小;而装填因子越大,则发生冲突的可能性就越大,在查找时所耗费的时间就越多。 有相关文献证明当装填因子在0.5左右时候,Hash性能能够达到最优。
因此,一般情况下,装填因子取经验值0.5。
哈希函数的选择
直接定址法 平方取中法 折叠法 除留取余法(y = x%11)
处理冲突的方法
链地址法 开放地址法 再散列法 建立一个公共溢出区