List 子类

List的特点:有序,有索引,可重复

List子类有:ArrayList,LinkedList,Vector

ArrayList:异步,非线程安全(随机访问效率高)

LinkedList:异步, 非线程安全(随机插入、删除效率高)

Vector    :同步,线程安全(因为同步的要求会影响执行的效率,所以如果不需要线程安全的集合那么使用ArrayList是一个很好的选择,这样可以避免由于同步而带来的不必要的性能开销)

1.ArrayList

ArrayList是List子类,可以直接通过对象的多态性为List接口实例化。此类的定义如下:

public class ArrayList<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable

从定义中可以发现ArrayList类继承了AbstractList类。AbstractList类的定义如下:

public abstract class AbstractList<E>
extends AbstractCollection<E>
implements List<E>

此类实现了List接口,所以可以直接使用ArrayList为List接口实例化。下面通过一些实例操作为读者讲解List接口中主要方法的使用。

(1)实例操作一:向集合中增加元素

要想完成此类操作,可以直接使用Collection接口中定义的两个方法。

增加一个元素:public boolean add(E o)。

增加一组元素:public boolean addAll(Collection<? extends E> c)。

也可以使用List扩充的add()方法在指定位置处增加元素。

在指定位置处添加元素:public void add(int index,E element)。

范例:验证增加数据的操作

package com.list.demo;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class Demo_Test1 {
	public static void main(String[] args) {
		List<String> allList = null; // 定义List对象
		Collection<String> allCollection = null; // 定义Collection对象
		allList = new ArrayList<String>(); // 实例化List对象,只能是String类型
		allCollection = new ArrayList<String>(); // 实例化Collection,只能是String类型
		allList.add("Hello"); // 从Collection继承的方法
		allList.add(0, "World"); // 此方法为List扩充的方法
		System.out.println(allList); // 输出集合中的内容
		allCollection.add("GGG"); // 增加数据
		allCollection.add("www.mldn.cn"); // 增加数据
		allList.addAll(allCollection); // 从Collection继承的方法,增加一组对象
		allList.addAll(0, allCollection); // 此方法是List自定义的,增加一组对象
		System.out.println(allList); // 输出对象,调用toString()方法
	}
}

程序运行结果:



  1. [World, Hello]  
  2. [GGG, www.mldn.cn, World, Hello, GGG, www.mldn.cn] 

从程序的运行结果中可以发现,使用List中的add(int index,E element)方法可以在集合中的指定位置增加元素,而其他的两个add()方法只是在集合的最后进行内容的追加。

 (2)实例操作二:删除元素

在类集中提供了专门的删除元素的方法,Collection和List接口都分别定义了删除数据的方法。

Collection定义的方法

每次删除一个对象:public boolean remove(Object o)。

每次删除一组对象:public boolean removeAll(Collection<?> c)。

List扩展的方法

删除指定位置的元素:public E remove(int index)。

范例:删除对象

 

package com.list.demo;

import java.util.ArrayList;
import java.util.List;

public class Demo_Test1 {
	public static void main(String[] args) {
		List<String> allList = null; // 声明List对象
		allList = new ArrayList<String>(); // 实例化List对象,只能是String类型
		allList.add("Hello"); // 增加元素
		allList.add(0, "World"); // 此方法为List扩展的增加方法
		allList.add("MLDN"); // 增加元素
		allList.add("www.mldn.cn"); // 增加元素
		allList.remove(0); // 删除指定位置的元素
		allList.remove("Hello"); // 删除指定内容的元素
		System.out.println(allList); // 输出对象,调用toString()方法
	}
}

程序运行结果:


  1. [MLDN, www.mldn.cn] 

在集合中增加完数据后,可以通过下标或对象的方式直接对集合中的元素进行删除。

U提示:关于使用remove(Object o)方法删除对象的说明。

在集合中可以插入任意类型的对象,在本程序中是以String类的对象为例,所以在使用remove(Object o)方法删除时可以直接删除;而对于自定义的类如果要通过此种方式删除,则必须在类中覆写Object类的equals()及hashCode()方法。这两个方法的使用在随后的章节中将为读者介绍。

(3)实例操作三:输出List中的内容

在Collection接口中定义了取得全部数据长度的方法size(),而在List接口中存在取得集合中指定位置元素的操作get(int index),使用这两个方法即可输出集合中的全部内容。

范例:输出全部元素

package com.list.demo;

import java.util.ArrayList;
import java.util.List;

public class Demo_Test1 {
	public static void main(String[] args) {
		List<String> allList = null; // 定义List接口对象
		allList = new ArrayList<String>(); // 实例化List对象,只能使String类型
		allList.add("Hello"); // 增加元素
		allList.add("Hello"); // 增加元素
		allList.add(0, "World"); // 增加元素
		allList.add("MLDN"); // 增加元素
		allList.add("www.mldn.cn"); // 增加元素
		System.out.print("由前向后输出:"); // 信息输出
		for (int i = 0; i < allList.size(); i++) // 循环输出集合内容
		{
			System.out.print(allList.get(i) + "、"); // 通过下标取得集合中的元素
		}
		System.out.print("\n由后向前输出:");
		for (int i = allList.size() - 1; i >= 0; i--) // 循环输出集合内容
		{
			System.out.print(allList.get(i) + "、"); // 通过下标取得集合中的元素
		}
	}
}

程序运行结果:


  1. 由前向后输出:World、Hello、Hello、MLDN、www.mldn.cn、  
  2. 由后向前输出:www.mldn.cn、MLDN、Hello、Hello、World、 

从程序的运行结果中可以看出,在List集合中数据增加的顺序就是输出后的顺序,本身顺序不会发生改变。

(4)实例操作四:将集合变为对象数组

在Collection中定义了toArray()方法,此方法可以将集合变为对象数组,但是由于在类集声明时已经通过泛型指定了集合中的元素类型,所以在接收时要使用泛型指定的类型。

范例:将集合变为对象数组

package com.list.demo;

import java.util.ArrayList;
import java.util.List;

public class Demo_Test1 {
	public static void main(String[] args) {
		List<String> allList = null; // 声明List对象
		allList = new ArrayList<String>(); // 实例化List对象,只能是String类型
		allList.add("Hello"); // 增加元素
		allList.add(0, "World"); // 增加元素
		allList.add("MLDN"); // 增加元素
		allList.add("www.mldn.cn"); // 增加元素
		String str[] = allList.toArray(new String[] {}); // 指定的泛型类型
		System.out.print("指定数组类型:"); // 信息输出
		for (int i = 0; i < str.length; i++) {
			// 输出字符串数组中的内容
			System.out.print(str[i] + "、");
			// 输出每一个元素
		}
		System.out.print("\n返回对象数组:");
		// 信息输出
		Object obj[] = allList.toArray();
		// 直接返回对象数组
		for (int i = 0; i < obj.length; i++) {
			// 循环输出对象数组内容
			String temp = (String) obj[i];
			// 每一个对象都是String类 型实例
			System.out.print(temp + "、");
			// 输出每一个元素
		}
	}
}

程序运行结果:


  1. 指定数组类型:World、Hello、MLDN、www.mldn.cn、  
  2. 返回对象数组:World、Hello、MLDN、www.mldn.cn、 

(5)实例操作五:集合的其他相关操作

在List中还存在截取集合、查找元素位置、判断元素是否存在、集合是否为空等操作。下面直接测试以上的操作。

范例:测试其他操作

package com.hehe.coco;

import java.util.ArrayList;
import java.util.List;

public class Test_Demo_1
{
	public static void main(String[] args)
	{
		List<String> allList = null;// 声明List对象
		allList = new ArrayList<String>();	// 实例化List对象,只能是 String类型
		System.out.println("集合操作前是否为空?" + allList.isEmpty());
		allList.add("Hello");// 增加元素
		allList.add(0, "World");// 增加元素
		allList.add("MLDN");// 增加元素
		allList.add("www.mldn.cn");// 增加元素
		System.out.println(allList.contains("Hello") ? "\"Hello\"字符串存在!" : "\"Hello\"字符串不存在!");
		List<String> allSub = allList.subList(2, 3);// 取出里面的部分集合
		System.out.println(allSub);
		System.out.print("集合截取:");
		for (int i = 0; i < allSub.size(); i++)// 截取部分集合
		{
			System.out.print(allSub.get(i) + "、");
		}
		System.out.println("");
		System.out.println("MLDN字符串的位置:" + allList.indexOf("MLDN"));
		System.out.println("集合操作后是否为空?" + allList.isEmpty());
	}
}

程序运行结果:


  1. 集合操作前是否为空?true 
  2. "Hello"字符串存在! 
  3. [MLDN] 
  4. 集合截取:MLDN、  
  5. MLDN字符串的位置:2 
  6. 集合操作后是否为空?false 

List集合在刚刚实例化之后因为还有为其增加内容,所以在使用isEmpty()方法时返回的结果是true,表示集合是空的,之后向集合中增加了4个元素,所以程序的最后再使用此方法判断时就返回false,表示集合中已经存在内容。在程序中使用contains()方法判断集合中是否存在指定的元素,如果存在,则输出存在的信息;反之,输出不存在的信息。在集合中也可以使用subList()方法取出指定的子集合。

2.LinkekList

LinkedList也和ArrayList一样实现了List接口,但是它执行插入和删除操作时比ArrayList更加高效,因为它是基于链表的。基于链表也决定了它在随机访问方面要比ArrayList逊色一点,所以使用LinkedList时不要用get方法,即使LinkedList的元素个数只有很少的几个。也要避免使用,养成好习惯,免得犯错。

LinkedList底层的数据结构是基于双向循环链表的,且头结点中不存放数据,如下

既然是双向链表,那么必定存在一种数据结构——我们可以称之为节点,节点实例保存业务数据,前一个节点的位置信息和后一个节点位置信息,如下图所示:

(1)LinkedList和ArrayList性能测试

这里针对于LinkedList和ArrayList 的插入和随机访问性能做了如下测试,测试代码如下:

1.LinkedList插入和随机访问性能测试

package com.LinkedList.demo;

import java.util.LinkedList;
import java.util.List;

public class LinkedList_Demo_1 {
	public static void main(String[] args) {
		int size = 2000000;// 200万次循环
		List<String> list = new LinkedList<String>();// 实例化LinkedList对象
		long addStartTime = System.currentTimeMillis();// add开始时间
		for (int i = 0; i < size; i++)// 200万次循环
		{
			list.add("Just some test data");// 增加元素
		}
		long addEndTime = System.currentTimeMillis();// add结束时间
		System.out.println("LinkedList添加200万条数据,耗时:" + (addEndTime - addStartTime)
				+ "\t毫秒");// 打印添加200万条数据所消耗的时间
		long queryStartTime = System.currentTimeMillis();// query开始时间
		for (int i = 0; i < size; i++)// 循环200万次
		{
			list.get(i);// 取出当前循环的元素
			if (i % 100000 == 0)// 当元素每循环到10万条时,记录消耗毫秒数
			{
				long queryEndTime = System.currentTimeMillis();// query结束时间
				System.out.println("LinkedList查询到第: " + i + "条数据,本次耗时"
						+ (queryEndTime - queryStartTime) + "  毫秒");// 打印查询200万条数据所消耗的时间
				queryStartTime = System.currentTimeMillis();// 初始化下一次query开始时间
			}
		}
	}
}

程序运行结果:

LinkedList添加200万条数据,耗时:2218 毫秒
LinkedList查询到第: 0条数据,本次耗时0  毫秒
LinkedList查询到第: 100000条数据,本次耗时13121  毫秒
LinkedList查询到第: 200000条数据,本次耗时61236  毫秒
LinkedList查询到第: 300000条数据,本次耗时102181  毫秒
LinkedList查询到第: 400000条数据,本次耗时148209  毫秒
LinkedList查询到第: 500000条数据,本次耗时183442  毫秒
......

后面几条记录用省略号代替来了,由于后面的结果实在太慢的,等下去也没有意义,从这里可以看出LinkedList查询的结果是非常的慢,所以要避免使用LinkedList的get方法。

2.ArrayLis插入和随机访问性能测试

package com.ArrayList.demo;

import java.util.ArrayList;
import java.util.List;

public class ArrayList_Demo_1 {
	public static void main(String[] args) {
		int size = 2000000;// 200万次循环
		List<String> list = new ArrayList<String>();//实例化ArrayList对象
		long addStartTime = System.currentTimeMillis();// add开始时间
		for (int i = 0; i < size; i++)// 200万次循环
		{
			list.add("Just some test data");// 增加元素
		}
		long addEndTime = System.currentTimeMillis();// add结束时间
		System.out.println("ArrayList添加200万条数据,耗时:"
				+ (addEndTime - addStartTime) + "  毫秒");// 打印添加200万条数据所消耗的时间
		long queryStartTime = System.currentTimeMillis();// query开始时间
		for (int i = 0; i < size; i++)// 循环200万次
		{
			list.get(i);// 取出当前循环的元素
			if (i % 100000 == 0)// 当元素每循环到10万条时,记录消耗毫秒数
			{
				long queryEndTime = System.currentTimeMillis();// query结束时间
				System.out.println("ArrayList查询到第: " + i + "条数据,本次耗时"
						+ (queryEndTime - queryStartTime) + "  毫秒");// 打印查询200万条数据所消耗的时间
				queryStartTime = System.currentTimeMillis();// 初始化下一次query开始时间
			}
		}
	}
}

程序运行结果:

ArrayList添加200万条数据,耗时:59  毫秒
ArrayList查询到第: 0条数据,本次耗时0  毫秒
ArrayList查询到第: 100000条数据,本次耗时10  毫秒
ArrayList查询到第: 200000条数据,本次耗时0  毫秒
ArrayList查询到第: 300000条数据,本次耗时1  毫秒
ArrayList查询到第: 400000条数据,本次耗时0  毫秒
ArrayList查询到第: 500000条数据,本次耗时0  毫秒
ArrayList查询到第: 600000条数据,本次耗时1  毫秒
ArrayList查询到第: 700000条数据,本次耗时0  毫秒
ArrayList查询到第: 800000条数据,本次耗时0  毫秒
ArrayList查询到第: 900000条数据,本次耗时1  毫秒
ArrayList查询到第: 1000000条数据,本次耗时0  毫秒
ArrayList查询到第: 1100000条数据,本次耗时0  毫秒
ArrayList查询到第: 1200000条数据,本次耗时0  毫秒
ArrayList查询到第: 1300000条数据,本次耗时1  毫秒
ArrayList查询到第: 1400000条数据,本次耗时0  毫秒
ArrayList查询到第: 1500000条数据,本次耗时0  毫秒
ArrayList查询到第: 1600000条数据,本次耗时0  毫秒
ArrayList查询到第: 1700000条数据,本次耗时1  毫秒
ArrayList查询到第: 1800000条数据,本次耗时0  毫秒
ArrayList查询到第: 1900000条数据,本次耗时0  毫秒

结果很清晰,使用ArrayList的get方法随机访问对象中的元素所消耗的时间是很短的。在很多地方会看到LinkedList比ArrayList插入的效率较快,这样说是不严谨的,比如上面的测试:

ArrayList添加200万条数据,耗时:59 毫秒
LinkedList添加200万条数据,耗时:2218 毫秒

这样看起来ArrayList插入效率比LinkedList高,这样说也不严谨的。我理解是有序的插入是ArrayList效率高,无序的插入LinkedList效率高。

3.Vector

Vector非常类似ArrayList,但是Vector是同步的。ArrayList会比Vector快,因为ArrayList是非同步的,如果设计涉及到多线程,还是用Vector比较好一些
。由Vector创建的Iterator,虽然和ArrayList创建的 Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。

package com.Vector.demo;

import java.util.Iterator;
import java.util.Vector;

public class VectorDemo {
	/**
	 * Vector的使用。包括Vector的创建、向Vector中添加元素、从Vector中删除元素、
	 * 统计Vector中元素的个数和遍历Vector中的元素。
	 */
	public static void main(String[] args) {

		Vector<String> v = new Vector<String>(4);// 实例化Vector对象
		v.add("Test1");// 添加元素
		v.add("Test0");// 添加元素
		v.add("Test2");// 添加元素
		v.add("Test0");// 添加元素
		v.add("Test2");// 添加元素
		System.err.println(v);

		/**
		 * 删除元素
		 */
		v.remove("Test0"); // 删除指定内容的元素(这里是删除第一个出现的该元素)
		v.remove(0); // 按照索引下标删除元素

		/**
		 * 获得已有元素的个数
		 */
		int size = v.size();
		System.out.println("size:" + size);

		/**
		 * 遍历元素
		 */
		for (int i = 0; i < v.size(); i++)
		{
			System.out.println(v.get(i));
		}

		/**
		 * 迭代元素
		 */
		Iterator<String> iterator = v.iterator();
		while (iterator.hasNext())
		{
			System.err.println(iterator.next());
		}
	}
}

程序运行结果:

[Test1, Test0, Test2, Test0, Test2]
size:3
Test2
Test0
Test2
Test2
Test0
Test2
Vector 类提供了实现可增长数组的功能,随着更多元素加入其中,数组变的更大。在删除一些元素之后,数组变小。 

时间: 2024-10-01 11:35:30

List 子类的相关文章

android java 继承父类里的子类问题!!!!!!!

问题描述 android java 继承父类里的子类问题!!!!!!! 请问各位,java可以继承父类的方法和全部属性,那父类里的子类可以继承使用么? android 可以继承父类的Handler.广播么??

java中父类与子类, 不同的两个类中的因为构造函数由于递归调用导致栈溢出问题

/* 对于类中对成员变量的初始化和代码块中的代码全部都挪到了构造函数中, 并且是按照java源文件的初始化顺序依次对成员变量进行初始化的,而原构造函数中的代码则移到了构造函数的最后执行 */ import static java.lang.System.out; public class PersonDemo { public static void main(String[] args) { //*********测试父类与子类之间的循环调用的问题 out.println("main1&quo

jvm执行流程(static代码块和初始化快和父类子类执行过程)

大家猜想一下,结果会是什么? 为什么会是这样呢? 1.jvm加载StaticBlock的main方法前,要看SubClass中是否有静态的变量和语句,如果有,先给这些静态的变量分配存储空间和执行静态语句(不是静态方法),且由于SubClass的父类中也有静态的变量,根据继承的特性,则先执行父类Parent的静态数据的初始化,然会执行子类的静态数据的初始化. 2.执行main方法中的new StaticBlock(); 语句,进行Parent的类的实例化因为Parent的静态数据已经实例化,并且在

主类main方法里面可以有不带public的子类 但是不能有接口

1.//还可以写在main外面 2. interface product{ 3. public void getName(); 4. } 5. class productA implements product { 6. @Override 7. public void getName() { 8. // TODO Auto-generated method stub 9. System.out.println("++++A++++"); 10. } 11. 12. } 13. cla

子类继承HibernateDaoSupport后如何注入SessionFactory

子类继承HibernateDaoSupport后,由于HibernateDaoSupport,setSessionFactory是使用final修飾的,無法重寫,沒有辦法使用註解的方式注入sessionFactroy public final void setSessionFactory(SessionFactory sessionFactory) {         if (this.hibernateTemplate == null || sessionFactory != this.hib

java-Java抽象类中的非抽象方法的方法体为空,子类也没有头给予实现,但却被调用了是什么意思

问题描述 Java抽象类中的非抽象方法的方法体为空,子类也没有头给予实现,但却被调用了是什么意思 读源码的时候关键地方卡住了,在一个方法的结尾把数据作为参数调用了一个抽象类的方法,这个方法不是抽象方法,方法体也是空的,这个抽象类的子类只有一个,而且也没有实现这个方法,那这些数据到底了什么地方呢? 解决方案 实现方法在子类中,多态的概念,父类有这个方法(不是abstract的),所以要写一个空方法,具体实现由子类处理 解决方案二: 子类继承了父类, 不管是否重写父类的方法,都可以调用父类的方法吧

java子类的super关键词问题

问题描述 java子类的super关键词问题 看以下为代码 public class 子类名 extends 父类名{ super( ); //这里的super是不是省略了父类的名字,也就是 super 父类名( ) 解决方案 不是省略了父类名,而是这里super本身就是父类名,根本就不需要,也不能再加上类名. 好比this就是本对象一样 解决方案二: no no 构造函数是不能被重写的 解决方案三: super就是调用父类的方法,这个父类可能是他的直接父类,也可能是他的父类的父类 解决方案四:

java-Java动态绑定,对象已经向上转型了,为何优先使用的方法依然是子类的方法。

问题描述 Java动态绑定,对象已经向上转型了,为何优先使用的方法依然是子类的方法. 情况如下,我写了个测试类. son类 public class son extends father { public son() { fs=""new son""; } public void say() { System.out.println(""son say()""); } father类 public class father {

imestamp-Date或子类的输出格式(要求类型不能变)

问题描述 Date或子类的输出格式(要求类型不能变) 比如Person的成员变量 java.util.Date createTime; 现在想要System.out.println(person.getCreateTime());输出的结果是 2015-12-12 12:12:12 . 但现在输出的是Sun Apr 12 23:56:56 CST 2015 CST格式. 这里明显不能使用SimpleDateFormat.因为需要的是Date的数据(实际情况中使用了反射). 尝试过子类Timest

java中有类的子类一说,那有对象的子对象一说吗?

问题描述 java中有类的子类一说,那有对象的子对象一说吗? java中有类的子类一说,那有对象的子对象一说吗?新手提问莫见笑. 解决方案 类的子类专业点说叫继承,对象的子对象你可以理解成方法的重写或重载等. 解决方案二: 那是不是类中有内部类就可以呢?