集合框架
基本概念
集合出现为了解决数组在开发过程中遇到的问题
特点:
-元素类型可以不同
-长度不固定,可以随时扩容
-提供很多种集合,不同的算法,采用的是集合框架,可以提高开发的效率
集合的结构
Collection:
Collection 是集合框架的一个顶层接口,提供了集合的各种特征
Collections 用来调用集合里的各种方法
collection是一个接口,不是提供直接的实现,List、set这两个子接口都是继承于collection,代表的是一种规则,它所包含的元素必须遵循一条或者多条规则,比如:有些允许数据重复,有些不允许数据重复,有些数据有顺序,有些又没有顺序。
在实际开发中,一般不适用collection来开发,使用它的子接口来进行开发。
Collection与Collections区别:
Collection:接口,集合的顶层框架,主要用于制定集合的规范
Collections:集合工具包,用于操作Collection所定义的各种集合,所有方法都是静态的,排序、反转、对元素进行搜索、线程安全的内容。
List
List接口,是collection子接口,代表的是有序的collection,list采用某种特定的插入顺序保证里面的数据都是有顺序的,用户可以对list中存放的数据位置进行精确的控制,同时可以根据元素的索引获取到元素的内容。
-特点:
1. list中元素是有顺序的
2. list允许元素的重复出现
3. list可以支持null数据
4. 可以使用索引来获取list中的元素
ArrayList
ArrayList是一个动态数组,最常用的最常见的集合,允许任何符合规则的元素插入,包括null也可以插入,ArrayList底层是一个数组,但是这个数字可以进行动态扩容。底层默认容量大小为10,随着集合中元素中内容的不断增加,容器的大小也会随着变化,每次向容器中加入数据的时候都要去检查当前容量是否够用,当快要溢出的时候,进行扩容。但是扩容的效率比较低。
-扩容:创建一个新的数组,将以前的数据赋值到新的数组,将以前的数组废弃。
LinkedList
-ArrayList和LinkedList都是实现了list接口,ArrayList是一个动态数组,底层是一个双向链表,linkedlist除了有ArrayList的一些方法外,还提供了额外的一些方法,通过一些方法操作链表的头和尾。
-由于两种集合底层的数据结构不同,linkedlist不能随机访问,所有的操作都是按照双向列表来执行,在列表中索引会采用从头或者结尾来遍历数据,这样做的目的在与操作集合时删除、增加可以提高效率。
双向列表:
列表中的每一个元素都是独立的,每个空间都存放本身的数据,以及上的一个对象的引用和下一个对象的引用(索引)。
Vector
与ArrayList相似,底层都是动态数组,但是vector是线程同步的,在操作数据的方法上面加上了synchronized关键字线程安全的。jdk1.0就开发出来了vector,jdk1.2后才开始提出集合框架,有很多程序还是用vector来完成的,为了向后兼容,将vector放在list下面,保留以前的vector的特性,但是现在一般用ArrayList。
-效率比ArrayList低
三种集合框架的各自优缺点
1.ArrayList底层是一个动态数组,linkedlist底层是双向链表来实现的
2.对于随机访问数据,get set效率差异
-ArrayList效率比linkedlist效率高,因为-ArrayList是一个动态数组,查找元素在一个连续的空间找,速度很快 。
-linkedlist移动索引去判断当前元素下一个是否我们需要的。
3.ArrayList结构对数据进行增删效率比较低,中间插入一个元素,以后的所有元素都能挪动位置,下标都变化。linkedlist前一个元素只需要改变引用,后一个元素只需要改变引用。
4.vector是线程安全的,效率非常低
5.ArrayList扩容的效率比较低,能提前确定空间大小,优先使用构造器初始化数组容量。
ArrayList源码解析:
-当调用add的时候,检查数组是否够用,如果不够用,将创建一个容量大一倍的数组,将原来数据的数据拷贝到新数组中,在将原来的数组废弃。
-当调用remove方法时,将指定元素移除数组后,将后面的元素整体向前移动。system.copyof()来实现,一次删除会有多个元素移动,add添加的时候,添加到指定的位置也会整体移动。
LinkedList源码解析:
LinkedList基于链表的数据结构,每个元素都包含了上一个和下一个元素的引用,所以add/remove只会影响上一个和下一个元素
链表结构:
-单链表:单链表中链表对象维护了一个first引用,该引用指向节点链表的第一个节点元素,每个节点对象维护了一个next引用,next指向下一个元素引用(节点对象两部分:自己的数据,下一个元素的引用Node)
-双向链表:链表对象维护了一个first引用和last引用,这两个引用分别指向链表的首和尾,每个节点对象维护了对象的数据,prev、next引用,用来指向前后对象
泛型
在集合中存放数据的时候,可以存放任何类型的数据,底层是object,取出数据的时候默认也是object对象,如果要将当前对象转化为要使用的对象必须知道取出来的值是什么类型。
否则经常出现java.lang.ClassCastException 数据转换错误
泛型在定义接口时指定参数的类型,参数类型默认用E(element)来表示元素,类型参数的值是在声明变量或者创建对象的时候确定。
如果说给集合指定泛型,那就意味着操作内容必须传递指定的参数类型。在获取集合中的数据的时候,不用再转换对象类型,默认返回传递的泛型类型。
E表示一个占位符。
问题:一旦确定了参数类型,泛型就确定了,传递参数时必须跟泛型同样的数据。
场景:在公司有员工领导,我们要在不同情况下表示不同泛型
List<?> list = new ArrayList<User>(); //根据后面new的对象来确定“?”的对象。
List<?>
通过?来设置占位符,在运行时确定具体的内容,根据子类的泛型确定父类的泛型
Set 接口
set继承collection
1.set存放的元素不能出现重复,当加入一个元素到容器中时,要比较元素的内容是否重复,自动去掉重复的内容。如果存放的是对象,需要比较对象是否一样,需要重写hashcode和equals方法
2.存放在容器中的元素可能有顺序,也肯能没顺序
3.元素在容器中没有顺序,所以不能通过下标获取里面的元素
set有两个实现类:hashset treeset
Hashset:基于hash算法的set接口实现
1.set元素不能重复
2.元素没有顺序
3.不能使用get、set随机访问
4.元素允许为null
在hashset集合中存在对象,只要调用add方法就需要进行比较,调用对象hashcode方法和equals方法比较对象是否一样,如果一样不会存放进去
判断两个对象是否相等:首先调用对象的hashcode(),在比较equals()
1.set判断两个对象的hashcode码是否一样(hashcode一旦确定,元素在内存中的位置也将确定,桶运算),如果说两个对象的hashcode码不一样,这两个对象不相等
2.如果说hashcode码相等,再调用对象的equals方法来比较值是否相等。如果值相等,才能确定这两个对象相等。
Set<UserBean> set2 = new HashSet<UserBean>();
UserBean bean1 = new UserBean(1,"xiaowang","1355");
UserBean bean2 = new UserBean(2,"xiaozhang","1389");
UserBean bean3 = new UserBean(2,"xiaozhang","1389");
//存放的时候对象的引用是不同,set集合识别三个不同的对象
set2.add(bean1); //bean1调用hashcode得到一个hashcode码,在内存中存放起来,位置就是hashcode码
set2.add(bean2); //bean2调用hashcode得到一个hashcode码,在内存中存放起来,位置就是hashcode码
set2.add(bean3); //bean3调用hashcode得到一个hashcode码, 发现hashcode码一样。调用equals一样。
System.out.println(bean3.hashCode());
System.out.println("元素是否存在"+set2.contains(bean3));
for (UserBean userBean : set2) {
System.out.println(userBean);
}
深入挖掘:
hashset数据结构:采用了hash表
jdk1.8之前:哈希表=数组+链表
jdk1.8及之后:哈希表=数组+链表
数组+红黑树(查询效率非常快)
哈希冲突:hashcode码一样,值不一样
jdk1.8中,hash存储采用数组+链表+红黑树存储数据,当产生哈希冲突的时候,值就会用链表来村,但是当链表长度超过8时,自动变成红黑树来进行排序。
Set set = new HashSet();
String s = new String("abc");
String s2 = new String("abc");
set.add(s); //存放s 计算s的hashcode码96354,在内存里面找到这个位置将引用存放
set.add(s2); //存放s2 计算s2的hashcode码 96354,equals比较 值一样,去掉
set.add("重地"); //存放“重地” 计算重地hashcode 1179395在当前位置存放"重地"
set.add("通话");//存放"通话",计算通话的hashcode 1179395,equals比较 值不一样。
常用方法:
add()、clear()、contains()返回布尔值,判断对象是否存在,也要用到hashcode方法和equals方法、isEmpty()
treeset
hashset:速度快 不排序
treeset:速度慢 排序
1.可以允许排序的一种集合,继承SortedSet接口
2.元素是不能重复的
3.底层基于treemap(按键值对来存储数据,存入的数据为键,值默认为空对象)来实现,基于红黑树算法。steeset的数据来源于treemap的key值,采用自然排序或者用户自定义排序(加载对象时,需要自定义排序)
特点:
在存储数据的时候,存放的数据是数值或者字符串(自带排序算法),当存放的是对象的时候,treeset默认是不知到怎么比较,需要在存放的对象身上实现comparable接口,重写comparaTo方法来实现自定义排序
public class UserBean implements Comparable<Object>{
}
public int compareTo(Object o) {
if(o!=null){
UserBean user = (UserBean) o;
if(this.id<user.getId()){
return 1;
}else if(this.id==user.getId()){
return 0;
}else{
return -1;
}
}
return -1;
}
Comparable称为比较器,常用于集合框架里面
迭代器
iterator接口我们称为迭代器,用于遍历集合的一种接口
遍历:从集合中取出每一个元素的过程,在conncetion中有一个对象iterator对象,通过这个对象可以遍历集合中的所有元素。
提供给集合用,输出数据
//所有的集合类都可以使用迭代器来完成数据的获取。
//通过调用iterator获取到迭代器接口对象,返回迭代器泛型,值根据集合的泛型来定的
//next方法可以获取到集合中的元素
Iterator<UserBean> it = set.iterator();
while(it.hasNext()){
UserBean res = it.next();
System.out.println(res);
}
Map 接口
有一系列键值对组成的接口,提供了key-value的映射,并没有继承collection
特点:
1.key-value有一一对应的关系,一个key对应一个唯一的值
2.因为map的键采用set来存放,键不能重复,value可以一样
3.hashMap和treeMap:HashMap 采用hash算法,TreeMap采用红黑数结构
4.如果将map的所有key放在一起,那就是一个set集合,key可能有顺序,可能没顺序。
5.如果你将所有的值取出来放在一起,变成collection
map存对象、存集合
分析源码:
1.hashmap是基于hash表来实现的,数组+链表 数组+红黑树
2.底层采用Entry(jdk1.7及以前 Entry 1.8Node来表示)+链表
3.hashmap采用key-value 的形式来存数据,允许为null,但只能出现一次
4.hashmap线程不安全
5.hashmap存入的顺序和取出的顺序可能不一样
6.hashmap保存数据的时候,根据key的hash值来决定存放的位置(hash冲突)
分析代码的属性和方法:
1.initialCapacity:初始容量,指的是hashmap在初始化的时候自身的容量,可以在构造方法中传递进来,如果不指定值,默认16.
2.size:当前hashmap的键值对数量
3.loadFctor当hashmap的值达到一定范围后,我们就开始扩容,加载因子默认0.75,你也用构造来指定加载因子,比如默认容量为16,0.75*16=12,当你的容量到达12的时候,就开始考虑扩容
4.threshold:扩容阀值:一般默认为上一次的2倍
hashMap和hashtable的区别:
1.两者在API的操作基本上一样,没有太大的区别
2.hashMap允许null键null值(null键只能出现一次),hashtable不允许出现null键null值
3.hashmap是线程不安全的,hashtable是线程安全的
4.hashmap效率高,hashtable效率低
treemap *****treemap如何排序
treeMap底层采用红黑树算法来进行数据的查找,红黑树的的每一个节点都是一个entry,里面包含了K V lift right parent color,root节点默认黑色、entry是根据key来进行排序的,entry节点包含的内容为value.
entry排序是按照Key来排的,Key是放在treeset,entry的key要比较大小根据的比较器comparator来进行判断的。
size:红黑树的节点个数