集合--Set接口和List接口

本文介绍了Java集合框架中的List接口及其常见实现类ArrayList和LinkedList,包括它们的特点和操作效率。同时,文章讲解了Set接口,强调了其元素唯一性的特点,并讨论了HashSet和TreeSet的实现原理。内容涵盖了数据结构如哈希表和二叉树在集合实现中的应用。
摘要由CSDN通过智能技术生成

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()方法,所以不用重写这个方法。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值