目录
集合概念
什么是集合?
集合:集合是一组用来存放数据对象的容器。
特点:存放的数据是对象。
对比数组:
1、数组初始化后,其长度不可以再被改变,也就是说数组存放数据的个数是事先确定的。
2、数组中对于插入元素和删除元素的操作是很繁琐的,因为涉及到了数组元素的移位操作。
3、数组存放数据的方式是有序的,可重复的,而对于那些需要去掉重复数据的需求就无法满足了。
但是对于数组以上的三点缺陷,在集合中就得到了很好的解决。
集合的分类
集合可分为两大类:单例集合和双例集合。
什么是单列集合?
单例集合:每次以单个对象的形式将数据存放到集合中。
什么是双列集合?
双列集合:每次以两个对象为一组的形式将数据存放到集合中,用来存储键值对对象,其中键具有唯一性,而值是可以重复的。
集合分类示意图
如上图,左边的Collection接口为单列集合的接口,右边的Map接口为双列集合的接口,下面分别对应他们的子接口以及对应的实现类。
集合详解
一、Collection接口的设计与实现
1、简介
Collection: 层次结构 中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。JDK 不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set 和 List)实现。此接口通常用来传递 collection,并在需要最大普遍性的地方操作这些 collection。
对于上面用黄色圈出来的字,解释是这样的:
1、对于Collection这个接口,我们如果需要直接调用它的一些方法的话,我们是需要去通过间接的使用Collection它的子接口的实现类来调用Collection中的方法的,如果说,我们手动的定义一个Collection接口的实现类的话我们是需要去重写它的所有方法的,这样就得不偿失了。例如下面:
package Collection;
import java.util.*;
public class Test implements Collection{
//当我们定义Test类去实现Collection接口时,我们发现此时就需要去重写它的方法,
//这样并不太方便
@Override
public Object[] toArray() {
return new Object[0];
}
@Override
public boolean add(Object o) {
return false;
}
@Override
public boolean remove(Object o) {
return false;
}
@Override
public boolean addAll(Collection c) {
return false;
}
}
2、如果我们使用它的子接口的实现类去创建对象,调用方法时
public class Test {
Collection col = new ArrayList();
Object O = col.add(5);//
}
此时我们就可以直接调用其方法而不需要去完成重写操作。
2、常见方法
添加元素操作:
public class Test {
public static void main(String[] args) {
Collection col = new ArrayList();
col.add(18);
col.add(20);
col.add(21);
System.out.println(col);
}
}
遍历操作
3、子接口的设计与实现
List接口
LIst:有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
用法
实现类:ArryList
ArrayList的底层结构:是一个数组,根据索引定位元素快,增加、删除操作需要数组的移位,第一次创建集合并添加一个元素时,底层就会创建一个大小为10的数组。
这里有一个小知识点:
在JDK1.7中:创建集合时,底层就会直接开辟一个大小为10的数组。
在JDK1,7后:创建集合时,底层只是调用了构造方法创建了一个空数组,当往集合中添加一个元素后,调用add方法,此时的集合就指向一个新开辟的大小为10的新数组,这样做就节省了内存。
上图为实现类在内存中的示意图。
注意:当我们存放的数据大小大于10的时候,这时就会新开辟一个数组,大小为原数组的1.5倍。
实现类:vector
面试题;vector与ArrayList的区别?
1、底层的扩容机制不同:vector扩容为原数组的两倍,而ArrayList扩容为原数组的1.5倍。
2、内部实现机制不同:vector类的操作方法采用的都是synchronize同步处理,线程安全但效率低。ArrayList类的操作方法却并没有采用synchronize同步处理,线程不安全但效率高。
实现类:LinkedList
LinkedList的底层结构:是一个双向链表,可以充分利用碎片化空间进行存储,查找,修改数据的时间复杂度都是O(n),因为都要遍历整个链表。
面试题:请问ArrayList与LinkedList有什么区别?
1、ArrayList是数组实现的集合结构,LinkedList是双向链表实现的集合结构。
2、LinkedList不需要频繁的开辟新数组空间,而ArrayListx需要,但是ArrayList根据索引查找元素的时间复杂度为O(1),而LinkedList根据索引查找元素的时间复杂度为O(N),很显然在查询操作上前者更加快速。
set接口
用法
实现类:HashSet
HashSet的底层结构:数组+链表=哈希表。
什么是哈希表?要理解这个东西,我们首先要搞清楚什么是哈希值
哈希值:是JDK根据对象的地址,按照某种规则计算出来的一个int型的数值。
如何获取哈希值:
通过Object类提供的API
public int hashCodde(); 返回对象的哈希值
1、同一个对象多次调用hashCode方法返回的哈希值是相同的。
2、不同对象调用的hashCode方法返回的哈希值默认是不同的。
了解了哈希值之后,我们来看看数据是怎么样存入到HashSet集合中的:
1、当我们创建一个HashSet集合的时候,在内存中就会开辟一个大小为16的Entry型的数组,数组名叫table。
2、当我们往集合中添加一个元素时,首先就会调用HashCode()求出该元素的哈希值,求出哈希值之后,除以数组长度然后取余,对应的取余值就是该元素在哈希表中的位置。
3、在存入之前,会判断数组上该位置是否为空,如果为空就直接存入,如果不为空,则进行第4步。
4、调用equals()方法,对两者进行比较,如果值相同,则不存入该元素,如果值不相同,则进行第5步。
5、JDK 7 新元素占用老元素位置,并指向老元素,形成链表结构;
JDK 8 新元素存在老元素下面,即老元素指向新元素。
注意:如果要放入HashSet元素,一定要重写两个方法:hashCode()、equals()。
实现类:TreeSet
TreeSet的底层结构:红黑树,也就说是一棵自平衡的排序二叉树。
有同学不清楚红黑树的排序原理,下面我们讲一下(以自平衡排序二叉树为例):
1、首先我们需要明白的是二叉树的度是2。
2、其次,红黑树会遵循向上分裂的原则。
可以根据上图,我们就能简单的理解什么是红黑树了。
讲完了红黑树,我们来讲一下另一个重要的知识点—比较器:
1、内部比较器:自定义定义一个类实现Comparable接口,在类内部重写compareTo方法
2、外部比较器:专门定义一些实现Comparable接口的比较类,然后在主方法中通过创建实现类对象(以多态的形式),通过对象调用比较方法对两个自定义的待比较对象进行比较,结果返回一个整数。
为什么要讲这个知识点?
对于TreeSet集合,他所存入的数据是有序的,既然是有序的,那么它的排序规则是怎么样的?
1、对于数值型:Integer、Double等,系统默认按大小进行升序排序
2、对于字符串:对首字母的编号进行升序排序(abcdefg…)。
3、对于自定义类型的数据:你需要自己自定排序规则。
这时候比较器的作用不就表现出来了吗?
我们来看看如何使用比较器对自定义数据进行排序:
package Collection;
/*
1、用内部构造器
*/
public class Student implements Comparable<Student>{
private String name;
private String id;
private int age;
private String sex;
public Student(String name, String id, int age, String sex) {
this.name = name;
this.id = id;
this.age = age;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", id='" + id + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
@Override
public int compareTo(Student o) {
//按照年龄进行比较
return this.age- o.age;
}
}
package Collection;
/*
使用外部比较器
*/
import java.util.Comparator;
public class Test {
public static void main(String[] args) {
Student std1 = new Student("wang","2009",18,"男");
Student std2 = new Student("li","2010",19,"男");
Student std3 = new Student("sun","2011",20,"男");
Student std4 = new Student("liu","2012",21,"女");
TreeSet set = new TreeSet();
set.add(std1);
set.add(std2);
set.add(std3);
set.add(std4);
//外部比较器
TreeSet<Student> set1 = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge()- o2.getAge();
}
});
System.out.println(set);
}
}
4、小结
对于Collection接口,它有两个子接口List、set。他们对应的实现类分别为ArrayList、LinkedList、HashSet、TreeSet。他们的区别如下:
|--List 有序,可重复
|--ArrayList
底层数据结构是数组,查询快,增删慢。
线程不安全,效率高
|--Vector
底层数据结构是数组,查询快,增删慢。
线程安全,效率低
|--LinkedList
底层数据结构是链表,查询慢,增删快。
线程不安全,效率高
|--Set 无序,唯一
|--HashSet
底层数据结构是哈希表。
如何保证元素唯一性的呢?
依赖两个方法:hashCode()和equals()
|--TreeSet
底层数据结构是红黑树。
如何保证元素排序的呢?
自然排序
比较器排序
如何保证元素唯一性的呢?
根据比较的返回值是否是0来决定
二、Map接口的设计与实现
1、简介
1、Map集合是一种双列集合,每个元素包含两个数据;
2、Map集合的每个元素格式:key=value(键值对元素);
3、Map集合也称“键值对”集合。
2、常见方法
3、实现类的设计与实现
HashMap的设计与实现
用法
底层结构
HashMap底层结构是哈希表。
扩容:
初始化容量扩充为16
当保存内容的容量扩充超过了与阈值*0.75=12时,就会进行容量的扩充。
在进行扩充的时候HashMap采用的成倍的扩充模式即:每一次扩充两倍(源码用的左移运算符1<<)。
注意:HashMap的存储方式和HashSet一样,唯一的不同点是,HashMap是把键值作为关键点
TreeMap的设计与实现
用法
底层结构
TreeMap的底层结构是二叉红黑树,这个于TreeSet一样,唯一不同的是TreeMap把键值作为关键点。
总结
本章主要介绍了Java集合的基本内容,包括集合的概念、种类、特点、以及相应的实现等,对于几种常见的集合,如何选择呢?
针对Collection集合我们到底使用谁呢?(掌握)
唯一吗?
是:Set
排序吗?
是:TreeSet
否:HashSet
如果你知道是Set,但是不知道是哪个Set,就用HashSet。
否:List
要安全吗?
是:Vector
否:ArrayList或者LinkedList
查询多:ArrayList
增删多:LinkedList
如果你知道是List,但是不知道是哪个List,就用ArrayList。
如果你知道是Collection集合,但是不知道使用谁,就用ArrayList。
如果你知道用集合,就用ArrayList。
在集合中常见的数据结构(掌握)
ArrayXxx:底层数据结构是数组,查询快,增删慢
LinkedXxx:底层数据结构是链表,查询慢,增删快
HashXxx:底层数据结构是哈希表。依赖两个方法:hashCode()和equals()
TreeXxx:底层数据结构是二叉树。两种方式排序:自然排序和比较器排序