java List集合框架(collection,ArrayList,Vector,LinkedList)

本文详细介绍了Java集合框架中的ArrayList、Vector和LinkedList。内容包括集合框架的概念、Collection方法、ArrayList的数组结构和增长因子、Vector的线程同步特性以及LinkedList的链表结构。文章还探讨了如何使用LinkedList模拟堆栈和队列,以及如何优化List集合,避免内存浪费。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >




一、什么是集合框架

  在了解什么是集合框架之前我们想先来了解了解什么是集合。

1、集合:

  通常情况下,把具有相同性质的一类东西,汇聚成一个整体,就可以称为集合。

  比如:所有在上小学的人,小学生们。或者说所有的java程序员们。又或者说,老师们。像小学生,他们都具有相同的性质,如年纪小,在上学,打游戏坑队友。至于java程序员和老师的相同的性质,我就不在这里一一陈述了。

  那么有了集合的概念,什么是集合框架呢?




2、集合框架:

  集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。任何集合框架都包含三大块内容:对外的接口、接口的实现和对集合运算的算法。

  • 接口:是代表集合的抽象数据类型。接口允许集合独立操纵其代表的细节。在面向对象的语言,接口通常形成一个层次。

  • 实现(类):是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构。

  • 算法:是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现。

  而且他们在被设计时,就被定下以下几个目标:

  • 该框架必须是高性能的。基本集合(动态数组,链表,树,哈希表)的实现也必须是高效的。

  • 该框架允许不同类型的集合,以类似的方式工作,具有高度的互操作性。

  • 对一个集合的扩展和适应必须是简单的。

  所以,其实说简单些集合就是装数据的容器,数据多了用对象进行存储,对象多了用集合来进行存储。而存储数据的方式(数据结构)各有不同,所以存储的容器也就有多种,从而形成了集合框架这一体系。就比如箱子都是用来装东西的,但是由于有的箱子装的是金钱,所以叫保险柜。有的装衣服,所以叫做衣柜。





3、collection方法:

  万变不离其宗,那么所有人都有自己血脉的起点,我们中国人都自称我们是炎黄子孙。那么在代码里有没有老祖宗呢?答案当然是,有。而且在Java中老祖宗的东西(public方法)是会一代一代传承下来的,而所有集合的老祖宗就是collection。那么collection的方法,其他集合也都可以使用。

  接下来让我们看看collection有哪些方法吧!

  https://blog.youkuaiyun.com/headnull/article/details/90547700





4、迭代:

在我们把某些数据放入集合后,我们总有一天会想看看这些数据。那么我们该如何去看我们的数据呢?其实方法主要有三种。

4.1、使用for-each循环:
		Collection<Object> ajj=new ArrayList<>();//声明一个集合ajj
		
		//ajj添加数据
		ajj.add("aa");
		ajj.add("bb");
		ajj.add("cc");
		ajj.add("dd");
		ajj.add("ee");
		ajj.add("ff");
		
		for (Object o : ajj) {//fore遍历
			
			System.out.println(o);//打印结果为:aa bb cc dd ee ff
		}



4.2、使用迭代器:
		Collection<Object> ajj=new ArrayList<>();//声明一个集合ajj
		
		//ajj添加数据
		ajj.add("aa");
		ajj.add("bb");
		ajj.add("cc");
		ajj.add("dd");
		ajj.add("ee");
		ajj.add("ff");
		
		Iterator<Object> it = ajj.iterator();//声明一个迭代器进行遍历
		
		while (it.hasNext()) {//hasNext()方法:如果仍有元素可以迭代,则返回 true。
			
			System.out.println(it.next());//next()方法:返回迭代的下一个元素。
			//打印结果为:aa bb cc dd ee ff
			
		}



4.3、使用forEach()方法:
		Collection<Object> ajj=new ArrayList<>();//声明一个集合ajj
		
		//ajj添加数据
		ajj.add("aa");
		ajj.add("bb");
		ajj.add("cc");
		ajj.add("dd");
		ajj.add("ee");
		ajj.add("ff");
		
		ajj.forEach(System.out::println);//打印结果为:aa bb cc dd ee ff

  我们这里重点来讲一下迭代器,首先我们来讲讲什么是迭代,简单的,通俗的解释,老师在班上点人就是在迭代。而有一天老师终于发现天天数人好烦啊!所以,老师要求学生每天打卡上课。那么打卡机就是这个迭代器。


  那么迭代器(Iterator)有哪些方法呢?


boolean hasNext()方法
		Collection<Object> ajj=new ArrayList<>();//声明一个集合ajj
		
		//ajj添加数据
		ajj.add("aa");
		ajj.add("bb");
		ajj.add("cc");
		ajj.add("dd");
		ajj.add("ee");
		ajj.add("ff");
		
		Iterator<Object> it = ajj.iterator();//声明一个迭代器
		
		boolean f=it.hasNext();//判断ajj集合中是否有元素。
		
		System.out.println(f);//打印结果为:true



E next()方法
		Collection<Object> ajj=new ArrayList<>();//声明一个集合ajj
		
		//ajj添加数据
		ajj.add("aa");
		ajj.add("bb");
		ajj.add("cc");
		ajj.add("dd");
		ajj.add("ee");
		ajj.add("ff");
		
		Iterator<Object> it = ajj.iterator();//声明一个迭代器
		
		Object o= it.next();//返回迭代的下一个元素
		
		System.out.println(o);//打印结果为:aa



void remove()方法
		Collection<Object> ajj=new ArrayList<>();//声明一个集合ajj
		
		//ajj添加数据
		ajj.add("aa");
		ajj.add("bb");
		ajj.add("cc");
		ajj.add("dd");
		ajj.add("ee");
		ajj.add("ff");
		
		Iterator<Object> it = ajj.iterator();//声明一个迭代器
		
		System.out.println(ajj);//打印结果为:[aa, bb, cc, dd, ee, ff]
		
		Object o= it.next();//指针下移 
		
		System.out.println(o);//打印结果为:aa
		
		it.remove();//删除 aa,remove()方法:从迭代器指向的ajj集合中移除迭代器返回的最后一个元素(aa)
		
		System.out.println(ajj);//打印结果为:[bb, cc, dd, ee, ff]

remove()方法:
  remove()方法是一个比较特殊的方法,remove()方法删除 next()方法最后返回的元素。每次调用next()方法只能调用一次 remove()方法。如果对于每个 next()方法或在第一次调用next()之前被多次调用 remove()方法,它会抛出一个 IllegalStateException异常。所以他是要课next()方法配合起来使用的。

以上这些就是迭代器(Iterator)的方法。




二、ArrayList集合


在了解ArrayList集合集合之前我们要看看他的爸爸,List集合。


1、List集合:

  List集合是Collection的儿子,所以在一定程度上List集合发挥了代码家族的特点,一代更比一代强。相当于Collection来说List集合是有下标的,而且元素是可重复的。而且对于collection的方法来说,List不但继承了Collection的所以方法,还拥有自己的特有方法。如以下方法:

  https://blog.youkuaiyun.com/headnull/article/details/90547800


  以上就是List所特有的方法,相当于Collection而言。其实不光是方法,就连List的迭代器都和Collection不一样。Collection的迭代器是Iterator,而List的迭代器是listIterator。


在这里插入图片描述

看完这些方法你就可以方法,不同主要有以下几点:

  1. iterator()方法在set和list接口中都有定义,但是ListIterator()仅存在于list接口中(或实现类中);

  2. ListIterator有add()方法,可以向List中添加对象,而Iterator不能

  3. ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator就不可以。

  4. ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。

  5. 都可实现删除对象,但是ListIterator可以实现对象的修改,set()方法可以实现。Iierator仅能遍历,不能修改。



2、ArrayList集合:

  ArrayList是List的实现类(List的儿子),所以在一般情况下List的方法ArrayList都可以使用。ArrayList的方法,在这里我就不一一陈述了。这里我们主要讨论一下ArrayList数据结构特点。


2.1、数组结构:

  ArrayList的数据结构是数组结构,这也导致了ArrayList增删慢,查询快,有连续下标。
为什么会增删慢,查询快呢?我们来看看:

在这里插入图片描述
这是一个数组,如果我们将元素45删除,会发生什么呢?

在这里插入图片描述

  就会变成这样,元素45被删除之后在45后面的元素的下标都会减一。增加反之。现在我的集合只有3-4个元素,但是如果是一个有1000个元素的集合呢?随意删除一个元素,其他元素的下标都要更改。因此才导致了ArrayList增删慢,查询快。


2.2、增长因子为1.5

  什么是增长因子呢?其实集合是一个可以改变长度的数组,在ArrayList集合被实例化时,是有初始长度的(10),每当增加的元素,导致集合总长度超过10的时候,集合的长度就会乘以1.5。使元素始终可以存放到集合中。我们来看看以下代码:

package com.zking.collection;

import java.lang.reflect.Field;
import java.util.ArrayList;

/**
 * 探究list集合的增长因子
 * @author ajj
 *
 */
public class ListGrowthFactorDemo {
	public static void main(String[] args) throws Exception {
		
		ArrayList li=new ArrayList<>();//定义一个初始容器量为60的ArrayList集合
		
		for (int i = 1; i < 60; i++) {//循环
			
			li.add(i);//循环添加60次
			
			System.out.print(i+",");//打印出当前添加的元素
			
			getln(li);//打印出当前容量
		}
	}
	
	public static void getln(ArrayList li)throws Exception {
		Field f = li.getClass().getDeclaredField("elementData");
		f.setAccessible(true);
		Object obj=f.get(li);
		Object[] elementData=(Object[]) obj;
		System.out.println("当前容器的容量是:"+elementData.length);
	}
}

  他的运行结果为:

在这里插入图片描述
在这里插入图片描述
从上面两张图我们可以发现,每次当集合的容量要不足时。他就马上将集合长度乘以1.5。以此来实现集合长度可变(变长不变短)。




三、Vector集合

  Vector也是List的实现类(List的儿子),但是这个儿子不同,他有一个很奇特的特点是其他集合所没有的,也就是它和他和枚举有关系,它可以用枚举遍历数据,当然它也可以使用迭代器进行遍历。


1、数组结构:

  这是Vector和ArrayList的相同点,他们都是数组结构。


2、增长因子为2:

  相比ArrayList,Vector的增长因子更大,每次当集合的容量要不足时。他就马上将集合长度乘以2。以此来实现Vector集合长度可变(变长不变短)。


3、线程同步:

  这也是Vector集合的一大特点之一。他的线程是同步,这怎么解释呢?

  我们先来看看它为什么线程同步,以下是Vector的源代码。

在这里插入图片描述
  我们可以明显的看到他的方法前面加了 synchronized(同步的),Vector的大多数方法都加了 synchronized(同步的),所以它可以实现线程同步。


4、枚举遍历:


		Vector<Object> ve=new Vector<>();//定义一个Vector集合
		
		
		//添加数据
		ve.add("aa");
		ve.add("bb");
		ve.add("cc");
		ve.add("dd");
		ve.add("ee");
		
		Iterator<Object> it = ve.iterator();//定义迭代器
		
		while (it.hasNext()) {//进行集合判断
			
			System.out.println(it.next());//打印结果:aa bb cc dd ee
		}
		
		
		
		Enumeration<Object> el = ve.elements();//定义一个Enumeration(枚举)
		
		while (el.hasMoreElements()) {//hasMoreElements()方法:测试此枚举是否包含更多的元素。
			
			//nextElement()方法:如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。
			
			System.out.println(el.nextElement());//打印结果:aa bb cc dd ee
			
		}

  由以上代码我们可知,Vector既可以用迭代器(iterator)进行遍历,又可以用枚举(Enumeration)进行遍历。在这里我们来浅淡了解一下枚举(Enumeration),其实在某种程度上而已,枚举和迭代器的功能上是相同的,都是为了打印出数据。而不同的是,迭代器在这方面上功能更强大(个人看法)。为什么这么说呢?因为枚举(Enumeration)接口只有两个方法,就是 nextElement()hasMoreElements(),有且仅有这两个方法。



四、LinkedList集合

  LinkedList集合和其他集合有着本质上的区别,因为LinkedList集合的数据结构是链表结构,准确的来说,是链表结构(单向链表、循环链表、双向链表)中的双向链表结构。


1、链表结构(双向链表结构)

  那么双向链表结构是一种什么样的数据结构呢?我们来看看下面这两张图:

在这里插入图片描述
在这里插入图片描述

  我们从上图的第二张图可以看出,这是一个双向链表结构,它有且仅有三个节点。每个节点中有三个属性,previous上一个节点,data当前节点,next下一个节点。如果我们在LinkedList集合插入三个元素,aa,bb,cc。那么,bb的previous为aa,bb的next为cc,而bb的data为bb。其中previous为null的为这个链表的头(第一个元素first),而next为null的这个链表的尾(最后一个元素last)。


  那么双向链表结构的删除和增加都是怎样操作的呢?在这里插入图片描述

在这里插入图片描述

  由上图可知,在链表中插入15这个元素,元素10的next就会改为15,而20的previous也会改为15,从而组成一个新的链表,删除类似。


五、应用及补充


1、list集合的调优

  结合以上的讲述我们都知道了,ArrayList的增长因子是1.5。并且当集合容量不足时会自动增长,但是我们看看最后的结果:

在这里插入图片描述

  集合中的元素实际长度为59,而容器的长度已经达到了73。很明显有的长度浪费掉了。那么我们知道来个集合进行调优呢?我们来看看以下代码:

package com.test;

import java.lang.reflect.Field;
import java.util.ArrayList;

public class Test0526 {
	public static void main(String[] args) throws Exception {

		ArrayList<Object> li = new ArrayList<>(60);// 定义一个初始容器量为60的ArrayList集合

		for (int i = 1; i < 60; i++) {// 循环60

			li.add(i);// 循环添加60次

			System.out.print(i + ",");// 打印出当前添加的元素

			getln(li);// 打印出当前容量
		}
	}

	public static void getln(ArrayList<Object> li) throws Exception {
		Field f = li.getClass().getDeclaredField("elementData");
		f.setAccessible(true);
		Object obj = f.get(li);
		Object[] elementData = (Object[]) obj;
		System.out.println("当前容器的容量是:" + elementData.length);
	}
}


  在上面我们就可以看出来,我在一开始定义集合的时候,就同时也定义了集合长度。这样一来,结果如下:
在这里插入图片描述

  结果就是,元素实际长度为60,而集合长度也为60。当然这需要你一开始就知道要添加多元素,但是如果你不知道要添加多少元素时该怎么办呢?输入一个接近实际值的数就ok了。一般误差都不会很大的。


2、使用LinkedList模拟出堆栈和队列的效果

  首先我们要了解一下,什么是堆栈?什么是队列?


2.1、堆栈

  其实堆栈和队列仅仅是两种数据结构而已,堆栈这种数据结构很像弹夹。如果我们把子弹标上序号,我们往弹夹里压十颗子弹,最先压进去的1号子弹。最后一颗子弹是10号子弹。那么这个过程我们可以看成是我们往一个堆栈数据结构的集合,添加数据。从1到10,那么当我们装好子弹准备开枪时,最开始打出来的子弹是那一颗呢?当然是10号子弹,接下来依次9号,8号,7号,6号…1号。我们可以把这个过程看作我们从集合里遍历元素。

  接下来我们来看看代码吧:

package com.zking.collection;

import java.util.Iterator;
import java.util.LinkedList;

public class LinkedListDemo {
	public static void main(String[] args) {
		Duizhan duizhan=new Duizhan();//定义一个具有堆栈存储结构特点的一个容器
		
		//增加数据
		duizhan.push("a1");
		duizhan.push("a2");
		duizhan.push("a3");
		duizhan.push("a4");
		duizhan.push("a5");
		
		//进行遍历
		duizhan.bianli();//遍历结果:a5 a4 a3 a2 a1与增加顺序相反


	}
}

/**
 * 模拟LinkedList完成具有堆栈存储结构特点的一个容器
 *
 */
class Duizhan{
	private LinkedList<Object> ll=new LinkedList<>();//定义一个LinkedList集合
	
	public void push(Object obj){//定义增加方法
		
		ll.addFirst(obj);//永远将新增的元素放在集合的首位。
	}

	
	public void bianli() {//定义遍历方法
		
		Iterator<Object> it=ll.iterator();//定义迭代器
		
		while(it.hasNext()) {//进行元素是否为空判断
			
			System.out.println(it.next());//遍历元素
			
		}
	}
}

  由以上我们可以得出,堆栈就是一种头入尾出的数据结构。


2.2、队列

  队列这种数据结构是一种非常常见的数据结构,它比较像水管。比如说元素从A端进去从B出来,所以它的顺序是保持一致的。比如以A,B,C,D,E的顺序增加至集合就会以A,B,C,D,E的顺序遍历出来。

  多说无益,我们来看代码:


package com.zking.collection;

import java.util.Iterator;
import java.util.LinkedList;

public class LinkedListDemo {
	public static void main(String[] args) {
		Duizhan duizhan=new Duizhan();//定义一个具有堆栈存储结构特点的一个容器
		
		//增加数据
		duizhan.push("a1");
		duizhan.push("a2");
		duizhan.push("a3");
		duizhan.push("a4");
		duizhan.push("a5");
		
		//进行遍历
		duizhan.bianli();//遍历结果:a1 a2 a3 a4 a5与增加顺序相同


	}
}

/**
 * 模拟LinkedList完成具有堆栈存储结构特点的一个容器
 *
 */
class Duizhan{
	private LinkedList<Object> ll=new LinkedList<>();//定义一个LinkedList集合
	
	public void push(Object obj){//定义增加方法
		
		ll.addLast(obj);//永远将新增的元素放在集合的末位。 
	}

	
	public void bianli() {//定义遍历方法
		
		Iterator<Object> it=ll.iterator();//定义迭代器
		
		while(it.hasNext()) {//进行元素是否为空判断
			
			System.out.println(it.next());//遍历元素
			
		}
	}
}

  由以上代码我们可以得出,队列数据结构,增加元素的顺序和遍历元素的顺序是一致的。


3、去除集合框架ArrayList中的重复元素

  这个我们就话不多说上代码:

package com.zking.collection;

import java.util.ArrayList;
import java.util.Iterator;

public class ListEliminateRepeatDemo {
	public static void main(String[] args) {
		
		ArrayList<Object> list=new ArrayList<>();//定义一个ArrayList数据结构
		
		
		//添加数据
		list.add(new Person("aa", 15));
		list.add(new Person("bb", 16));
		list.add(new Person("cc", 17));
		list.add(new Person("dd", 18));
		list.add(new Person("vv", 18));
		list.add(new Person("aa", 15));

		
		
		ArrayList<Object> list2 = singleList(list);//进行去重复处理
		
		
		Iterator<Object> it = list2.iterator();//定义迭代器
		
		while (it.hasNext()) {//进行判空
			
			Person next = (Person)it.next();//进行数据转换
			
			System.out.println(next.getName());//打印结果为:aa bb cc dd vv。最后一个aa被去除。
		}
		
		
		
		
		
		
	}
	
	
	/**
	 * 思路:
	 * 
	 * 	将原有的集合遍历
	 * 
	 *  遍历后的元素存放到新的集合中
	 *  
	 *  存放之前做一个字符串判断
	 *  
	 * @param list
	 * @return
	 */
	public static ArrayList<Object> singleList(ArrayList<Object> list) {//定义一个去除重复方法
		
		ArrayList<Object> newAL=new ArrayList<>();//定义一个ArrayList数据结构
		
		Iterator<Object> it=list.iterator();//定义迭代器
		
		while(it.hasNext()) {//进行判空
			
			Object next = it.next();//将元素取出
			
			if(!newAL.contains(next)){//进行元素重复判断,contains方法会自动调用底层源代码从而调用equals方法,同样的Remove也会调用。
				
				//判断next元素是否在newAL集合中已经出现过
				
				newAL.add(next);//如果没有出现过就将next元素添加至newAL集合中
				
			}
		}
		return newAL;//反回newAL集合
		
	}
}

class Person{//定义一个内部类Person
	
	private String name;//定义属性String name
	
	private int age;//定义属性int age
	
	public String getName() {//公开设/取方法
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public int getAge() {
		return age;
	}
	
	public void setAge(int age) {
		this.age = age;
	}
	
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	
	public boolean equals(Object obj){//重写equals方法
		
		if(obj instanceof Person) {//如果调用方法的类型是Person类型
			
			Person p=(Person) obj;//数据转换
			
			return this.getName().equals(p.getName())&&this.getAge()==p.getAge();//进行姓名和年龄的判断,如果一致则返回true。
			
			//在这里解释一下,比如"ajj".equals("akk")。那么这个ajj就是上面的this,而akk就是obj。简单地说谁点equals()方法,谁就是this。
		}
		return false;//
	} 
	
	
	
}

  由以上代码,我们可以看出。contains()方法会自动调用equals()方法,所以我们要重写equals()方法进行判断,从而达到去重复的效果。



java List集合框架(collection,ArrayList,Vector,LinkedList)
讲解完毕,感谢你的耐心观看!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值