JDK 不提供此接口(Collection)的任何直接实现:它提供更具体的子接口(如 Set 和 List)实现。Collection接口定义的是集合种类的共性内容,最基础的功能。
1 List接口
有序的 collection(也称为序列)。用户可以根据元素的整数索引(在列表中的位置)访问元素(有下标);
允许有重复的元素;
List接口普提供了增删改查动作:
(1)增加:add(element) add(index,element)
(2)移除:remove(element) remove(index,element)
(3)修改:set(index,element)
(4)查询:get(index)
public static void main(String[] args) {
// 创建List集合对象
List list = new ArrayList();
// 往集合中添加元素
list.add(new Student("周瑜",30));
list.add(new Student("鲁班",20));
list.add(new Student("张大仙",18));
list.add(new Student("小米",45));
// 在集合指定位置插入元素对象
list.add(2, new Student("剑姬",18));
// 在集合指定位置修改元素对象
list.set(3, new Student("小王",23));
// 删除指定位置的元素并且返回
Object remove = list.remove(3);
System.out.println(remove);
// 结果:Student [name=小王, age=23]
/* 遍历元素
* (1)基本方法:迭代器遍历出所有元素
* (2)由于List是有下标的,可以通过下标获取每一个
*/
// 方法一:迭代器
for (Iterator it = list.iterator(); it.hasNext();) {
System.out.println(it.next());
}
// 方法二:for循环遍历
for(int i=0;i<list.size();i++){
// get(int index)返回列表中指定位置的元素
System.out.println(list.get(i));
}
}
2 List常见的实现类
(1)Vector:数组,最早的集合,是同步的
(2)ArrayList:大小(长度)可变的数组(数据结构),查询速度块,增删速度慢。不同步的。
定义:ArrayList是长度可变的对象引用数组,称为动态数组;
(3)LinkedList:链接列表(数据结构),增删块,查询慢,不同步。
定义:LinkedList类用于创建链表数据结构;
常见面试题:ArrayList与LinkedList的区别
(1)ArrayList 采用的是数组形式来保存对象的,这种方式将对象放在连续的位置中,所以最大的缺点就是插入删除时非常麻烦;
(2)LinkedList 采用的将对象存放在独立的空间中,而且在每个空间中还保存下一个链接的索引。但是缺点就是查找非常麻烦,要从第一个索引开始。
2.1 数组结构(ArrayList类)
(1)数组里的元素是连续的。数组查询元素:可以通过下标迅速访问数组中任何元素。
(2)数组增加元素:如果需要给index为10的位置添加,则从index为11的位置开始右移。
(3)数组删除元素:如果需要删除index为10的位置,则从index为11的位置开始左移。
(4)数组的长度本不可以改变,ArrayList增加或删除一个元素时其底层原理创建一个新数组,并将内容从旧数组复制到新数组。
因此查询快,增删慢。
2.2 链表结构(LinkedList)
(1)链表的每个元素都存储了下一个元素的地址,从而使得一系列的随机的内存地址串在了一起,只要有足够的内存空间,就能为链表分配内存。链表查找:当同时读取所有元素时,链表的效率很高,读第一个,读第二个,以此类推。但当你需要跳跃,链表的效率就很低了,每次都必须从第一个开始查找。
(2)链表增加元素:只需要修改它前面的那个元素指向的地址就可以了。
(3)链表删除元素:只需要将前一个元素指向的地址更改即可。
因此,查询慢,增删快。
2.3 堆栈、队列(数据结构)
链接列表可用作堆栈、队列结构
(1)堆栈:线性,先进后出(弹夹)First In Last Out(FILO)
(2)队列:先进先出(排队)First In First Out(FITO)
public static void main(String[] args) {
// LinkedList中特有的方法都是围绕头尾来定义的
LinkedList list = new LinkedList();
// 往LinkedList头部添加元素
list.addFirst("a1");
list.addFirst("a2");
list.addFirst("a3");
// LinkedList允许将链接列表用作堆栈、队列
// while(!(list.isEmpty())){
// System.out.print(list.removeFirst());
// 结果:a3a2a1。a1先进后出---》堆栈结构
// }
while(!(list.isEmpty())){
System.out.print(list.removeLast());
// 结果:a1a2a3。a1先进先出---》队列结构
}
}
//需求:使用LinkedList仿写一个队列结构
public static void main(String[] args) {
// 创建Queue对象
Queue q = new Queue();
// 在Link的头部添加元素
q.myAdd("詹姆斯");
q.myAdd("库里");
q.myAdd("哈登");
// 判断集合是否为空
while(!q.isNull()){
System.out.println(q.getQueue());
} // 结果:詹姆斯,库里,哈登
}
}
class Queue{
// 仿写队列结构,不直接对外提供集合
private LinkedList link;
// 对象一创建,就引入LinkedList集合
public Queue(){
link = new LinkedList();
}
// 队列添加元素
public void myAdd(Object o){
link.addFirst(o);
}
// 队列的获取元素,先进先出
public Object getQueue(){
return link.removeLast();
}
// 判断集合是否为空
public boolean isNull(){
return link.isEmpty();
}
3 Set接口
List可以存储重复元素,如果需求中要求容器的元素必须保证唯一性。那么此时就要使用另外一种集合——》Set接口。
特点:
(1)collection接口的子接口
(2)元素不可重复
(3)方法和collection方法一致
(4)不存在下标,获取元素只能通过迭代器。
4 Set接口常见实现类
(1)HashSet:无序不可重复
哈希表结构(数据结构)。不保证Set的迭代顺序。
一般HashSet类要重写hashCode()和equals()方法。
(2)TreeSet:有序集合
二叉树结构(数据结构)。可以对集合中的元素的进行自然排序。
TreeSet中的元素要实现Comparable接口或者有一个自定义的比较器。
8 哈希表(数据结构)
哈希表结构(数据结构),保证了元素的唯一性,依赖于hashCode()和equals()方法来判断师傅存储元素。
存储对象的时候,分为两个步骤:
(1)首先判断hashCode()方法:每个对象都可通过哈希算法算出一个哈希值。哈希值不同,说明两个元素不相同。就不需要向下判断就可进行存储。哈希值相同时,在进行下一个阶段判断。
(2)判断equals()方法:当两个元素哈希值相同时(哈希值冲突),就需要再继续判断元素的内容(equals)是否相同。如果 equals()方法返回为true,说明连个元素相同,就不进行存储。如果equals()方法返回为false,意味着两个元素不相同,就会使用拉链法。
源码类:
/*
* Set集合不允许有相同的元素。创建两个对象即便是姓名和年龄相同,但由于对象地址不同,
* 此时也是被认为不同元素(对象)的,这明显不符合人们的逻辑。HashSet的数据结构
* 是哈希表。因此先比较对象地址(hashCode())是否相同,相同在比较equals()方法。
*
*/
// 那么就不能使用Object类的Hashcode方法,此时就要重写Hashcode方法。
public int hashCode(){
// 建立学生对象的哈希值算法内容,通过学生对象特有的数据来定义哈希算法。
return name.hashCode()+age;
}
// 重写equals()方法
public boolean equals(Object obj){
// 1.当前对象和传进的对象是同一个对象
if(this == obj){
return true;
}
// 判断用户传入的类型,用作提示用户
if(!(obj instanceof Student)){
throw new ClassCastException("类型转换异常");
}
// 向下转型为Student类型,本来是多态Object obj = new Student()
Student stu = (Student)obj;
// name是字符串,内容(equals)相同返回true。
// age时int类型,所以比较的是值。相等返回true。
return this.name.equals(stu.name) && this.age == stu.age;
}
9 二叉树结构(数据结构)
底层原理:利用对象比较的方法来判断元素是否相等。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。
9.1 根据元素的自然顺序比较
实现Comparable接口,重写compareTo()方法。比较方法结果为0,就视为同一元素,不进行存储。
(1)源码
public class Student implements Comparable /*自然排序*/ {
// 对集合添加元素add()方法底层实现了Comparable接口,因此元素可以进行排序。
/*
* 自定义对象时,为了让学生对象具有比较性,需要建立对象的自然排序。
* 1.需要自定义实现Comparable接口
* 2.重写compareTo()方法
*/
public int compareTo(Object obj){
if(!(obj instanceof Student)){
throw new ClassCastException("类转换异常");
}
// 向下转型为Student类型,传入的是多态Object obj = new Student();
Student stu = (Student)obj;
// if(this.age > stu.age){
// return 1;
// } else if(this.age < stu.age){
// return -1;
// }
// return 0;
// 对if判断语句进行优化
// return this.age - stu.age;
// 要想对多个对象的多个属性进行排序,要分清主次。
int temp = this.age - stu.age; // 先对年龄进行比较
// 在对姓名进行比较,age是基本数据变量可以直接相减,
// name是字符串,字符串也是有compareTo方法的
return temp == 0?this.getName().compareTo(stu.getName()):temp;
}
(2)调用空参构造函数,底层实现Comparable接口
public static void main(String[] args) {
// 创建Set集合对象
Set set = new TreeSet();
// 向集合中添加元素
set.add(new Student("小一1",22));
set.add(new Student("小一2",21));
set.add(new Student("小一3",24));
set.add(new Student("小一4",23));
set.add(new Student("小一5",23));
for (Iterator iterator = set.iterator(); iterator.hasNext();) {
System.out.println(iterator.next());
}
(3)存储过程:小的放左边,大的放右边
9.2 根据比较器比较
/*
* 定义一个比较器:根据姓名进行比较
*/
public class ComparatorByName implements Comparator {
// 重写compare方法
public int compare(Object o1, Object o2) {
// 因为要使用到Student类型的属性,要向下转型
Student stu1 = (Student)o1;
Student stu2 = (Student)o2;
// 比较
int temp = stu1.getName().compareTo(stu2.getName());
return temp == 0?(stu1.getAge() - stu2.getAge()):temp;
}
}
(2)创建集合对象,调用带有比较器构造函数
public static void main(String[] args) {
// 创建Set对象,并调用比较器
Set set = new TreeSet(new ComparatorByName());
// 向集合添加对象
set.add(new Student("小一1",22));
set.add(new Student("小一1",21));
// 迭代元素
for (Iterator iterator = set.iterator(); iterator.hasNext();) {
System.out.println(iterator.next());
}
}
关于TreeSet类的底层对象比较原理,关键要看创建集合对象是含参数还是不含参数,从而来判断出是调用自然排序还是比较器方法。
这是Comparator接口的方法摘要,我们发现第二个方法equals()方法不用重写。这是因为我们创建的比较类默认继承Object类,父类自身有equals()方法,所以不用重写这个方法。