7.4 List集合


List集合代表一个元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引。

一、改进的List接口和ListIterator接口

1、List集合的增、删、替换、插入、截取等基本操作

List是Collection接口的子接口,继承了Collection接口中的所有方法,且List增加了一些根据索引来操作集合元素的方法:
★void add(int index,Object element):将元素element插入到List集合的index处
★boolean addAll(int index ,Collection c):将集合c所包含的所有元素全部插入到List集合的索引index处
★Object get(int index):返回集合index索引处的元素
★int indexOf(Object o):返回对象o在List集合对象中第一次出现的位置索引
★int lastIndexOf(Object o):返回对象o在List集合对象中最后一次出现的位置索引
★Object remove(int index):删除并返回index索引处的元素
★Object set(int index, Object element):将index索引处的元素替换为element对象,返回被替代的旧元素
★List subList(int fromIndex,int toIndex):返回索引fromIndex到toIndex(不包含)处所有集合元素组成的子集合
所有List实现类都可以调用这些方法来操作集合元素,与set集合相比,List增加了根据根据索引来插入、替换和删除集合元素的方法。
List的常规用法:

import java.util.ArrayList;
public class ListTest 
{
	public static void main(String[] args) 
	{
		var books=new ArrayList();
		//向books集合中添加三个元素
		books.add("轻量级Java EE企业级应用实战");
		books.add("疯狂Java讲义");
		books.add("疯狂Android讲义");
		//将新字符串插入到第二个位置
		books.add(1,"疯狂Ajax讲义");
		for(int i=0;i<books.size();i++)
		{
			System.out.println(books.get(i));
		}
		//删除第三个元素
		books.remove(2);
		System.out.println(books);

		//判断指定元素在List集合中得位置:输出1表示第二位
		System.out.println(books.indexOf(new String("疯狂Ajax讲义")));//①
		//将第二个元素替换成新的字符串
		books.set(1,"疯狂Java讲义");
		System.out.println(books);
		//第二个元素到第三个元素(不包括)截取成子集和
		System.out.println(books.subList(1,2));
	}
}

---------- 运行Java捕获输出窗 ----------
轻量级Java EE企业级应用实战
疯狂Ajax讲义
疯狂Java讲义
疯狂Android讲义
[轻量级Java EE企业级应用实战, 疯狂Ajax讲义, 疯狂Android讲义]
1
[轻量级Java EE企业级应用实战, 疯狂Java讲义, 疯狂Android讲义]
[疯狂Java讲义]

输出完成 (耗时 0 秒) - 正常终止

List判断两个对象是否相等只要通过equals()方法比较返回true即可。①代码处是两个不同的对象,具有不同的hashCode值,但是,Java仍然认为它们相等,因此可以返回其索引值。下面举例说明:

import java.util.ArrayList;
class  A 
{
	public boolean equals(Object obj)
	{
		return true;
	}
}
public class ListTest2
{
	public static void main(String[] args)
	{
		var books=new ArrayList();
		books.add("轻量级Java EE企业级应用实战");
		books.add("疯狂Java讲义");
		books.add("疯狂Android讲义");
		System.out.println(books);
		//删除集合中的A对象,将导致第一个元素被删除
		books.remove(new A());//1
		System.out.println(books);
		//删除集合中的A对象,再次导致第一个元素被删除
		books.remove(new A());//2
		System.out.println(books);

	}
}
---------- 运行Java捕获输出窗 ----------
[轻量级Java EE企业级应用实战, 疯狂Java讲义, 疯狂Android讲义]
[疯狂Java讲义, 疯狂Android讲义]
[疯狂Android讲义]

输出完成 (耗时 0 秒) - 正常终止

在代码1处执行Collection接口中的remove()方法试图删除一个A对象,List将会调用该对象A的equals()方法依次与与集合中的元素进行比较,如果该equals()方法以某个集合作为参数时返回true,List集合将会删除该元素——A类重写了equals()方法,总是返回true。所以在比较集合中的第一个元素时,就会删除第一个元素。

2、Java 8还为List接口增加了两个默认方法——replaceAll()和sort()方法

★void replaceAll(UnaryOperator operator):根据operator指定计算规则重新设置List集合的所有元素。
★void sort(Comparator c):根据Comparator 参数对List集合的元素排序
程序实例:

import java.util.ArrayList;
public class ListTest3 
{
	public static void main(String[] args) 
	{
		var books=new ArrayList();
		books.add("轻量级Java EE企业级应用实战");
		books.add("疯狂Java讲义");
		books.add("疯狂Android讲义");
		books.add("疯狂ios讲义");

		//使用目标类型为Comparator的Lambda表达式对List集合排序
		books.sort((o1,o2)->((String) o1).length()>((String) o2).length()?1:((String) o1).length()==((String) o2).length()?0:-1);//1
		System.out.println(books);

		//使用目标类型为UnaryOperator的Lambda表达式来替换集合中的所有元素
		books.replaceAll(ele->((String) ele).length());//2
		System.out.println(books);
	}
}
---------- 运行Java捕获输出窗 ----------
[疯狂ios讲义, 疯狂Java讲义, 疯狂Android讲义, 轻量级Java EE企业级应用实战]
[7, 8, 11, 17]

输出完成 (耗时 0 秒) - 正常终止

代码1处,传给sort()方法lambda表达式指定排序规则:字符串越长越大。因此执行完这段代码,List集合的字符串将会按照从短到长顺序排序。
代码2处,传给replaceAll()方法lambda表达式指定替换集合元素的规则:直接用集合元素的长度作为新元素。

3、Iterator接口和改进的ListIrerator接口

List除了继承了Iterator接口中的iterator()方法,返回一个迭代对象。List还提供了一个额外的listIterator(0方法,该方法返回一个ListIterator对象,ListIterator接口继承Iterator接口,提供了专门操作List的方法。ListIterator在Iterator接口基础上增加了以下方法:
★boolean hasPrevious():返回该迭代器关联的集合是否还有上一个元素。
★Object previous():返回该迭代器的上一个元素
★void add(Object o):在指定位置插入一个元素。
ListIterator与普通的Iterator相比,ListIterator增加了向前迭代的功能(Iterator只能向后迭代),而且ListIterator还可以通过add()方法向List集合中添加元素(Iterator只能删除元素)

import java.util.ArrayList;
public class ListIteratorTest  
{
	public static void main(String[] args) 
	{
		ArrayList books=new ArrayList();
		books.add("西游记");
		books.add("三国演义");
		books.add("水浒传");
		books.add("红楼梦");
		var it=books.iterator();
		//Iterator只能向后迭代和删除功能
		while(it.hasNext())
		{	
			var ele=it.next();
			if(ele=="红楼梦")
			{
				it.remove();
			}
			else
				System.out.println(ele);
		}
		System.out.println(books);//[西游记, 三国演义, 水浒传]
	
		//ListIterator接口向后访问和增加功能
		var lit=books.listIterator();
		while(lit.hasNext())
		{
			var ele=lit.next();
			if(ele=="水浒传")
			{
				lit.add("红楼梦");	
			}
			System.out.println(ele);
		}
		System.out.println(books);//[西游记, 三国演义, 水浒传, 红楼梦]
		//ListIterator接口的向前访问
		while(lit.hasPrevious())
		{
			System.out.println(lit.previous());
		}
	}
}
西游记
三国演义
水浒传
[西游记, 三国演义, 水浒传]
西游记
三国演义
水浒传
[西游记, 三国演义, 水浒传, 红楼梦]
红楼梦
水浒传
三国演义
西游记
请按任意键继续. . .

二、 ArrayList和Vector实现类

ArrayList和Vector作为List类都是基于数组实现的List类,所以ArrayList和Vector类封装了一个动态的、允许再分配的Object[]数组。ArrayList和Vector对象使用initialCapacity参数来设置该数组的长度,当向ArrayList和Vector集合中添加元素超过了数组的长度时,它们initialCapacity会自动增加。
在多数编程情况下,程序员可以不用去管initialCapacity。但如果向ArrayList和Vector集合中添加大量的元素时,可使用ensureCapacity(int minCapacity)方法一次性地增加initalCapacity。可以减少重分配的次数,从而提高性能。
如果没有指定initialCapacity参数,则Object[]数组的长度默认为10.
ArrayList和Vector提供了两个方法来重现分配Object[]数组:
★void ensureCapacity(int minCapacity):将ArrayList或Vector集合的Object[] 数组长度增加大于或等于minCapacity.
★void trimToSize():调整ArrayList或Vector集合的Object[] 数组长度为当前元素的个数。调用该方法可减少ArrayList和Vector集合对象所占的存储空间。

import java.util.ArrayList;
public class ArrayListTest1 
{
	public static void main(String[] args) 
	{
		//创建空的ArrayList集合时不指定initialCapacity参数,Object[]数组默认长度为10
		var al=new ArrayList();
		al.add(0,"good");
		al.add(1,"morning");
		System.out.println(al.size());//2
		//将集合转换为数组
		Object[] array=al.toArray();
		System.out.println(array.length);//2

		//指定数组的长度
		var al1=new ArrayList();
		al1.ensureCapacity(4);
		System.out.println(al1.size());//0
		al1.add('a');
		al1.add('b');
		al1.add('c');
		al1.add('d');
		al1.add('e');
		System.out.println(al1.size());//5
	}
}

两者的区别:
1、Vector类是古老的集合,那时设计不完美,有一些方法名很长的方法,而Vector系列中方法名更短的方法属于后来新增的方法。ArrayList开始作为List的主要实现类,没有那些方法名很长的方法。实际上Vector有很多缺点,应该减少使用Vector。
2、ArrayList的线程是不安全的,当多个线程同时访问同一个ArrayList集合时,如果由超过一个以上的线程修改了ArrayList,则程序必须手动保证该集合的同步性;但Vector集合的线程是安全的,无需保证该集合的同步性。但是因此Vector的性能要低于ArrayList。我们仍然不推荐使用Vector。我们通常使用一个Collection的工具类,它可以将一个ArrayList线程变得安全。
3、Vector还提供一个子类Stack,用于模拟“栈”这种数据结构,“栈”通常是指后进先出(LIFO)的容器。最后push进栈的元素,最先被pop出栈。注意进栈和出战都是Object,因此从栈取出元素必须进行类型转换,除非只是使用Object具有的操作。
Stack类提供的方法:
★Object peek():返回栈的第一个元素,但并不把该元素"pop"出栈
★Object pop():返回栈的第一个元素,并将该元素pop出栈
★void push(Object item):将一个元素push进栈,最后一个进栈的元素总是位于栈顶。
注:由于Stack是继承Vector的,因此它的线程是安全的、性能较差的。需要使用栈这种数据结构,建议使用ArrayDeque代替它。

import java.util.Stack;
public class StackTest 
{
	public static void main(String[] args) 
	{
		var st=new Stack();
		st.add("a");
		st.add("b");
		st.push("c");
		st.add("d");
		//返回栈的第一个元素,不pop出栈
		System.out.println(st.peek());//d
		System.out.println(st);//[a, b, c, d]
		////返回栈的第一个元素,pop出栈
		System.out.println(st.pop());//d
		System.out.println(st.pop());//c
		System.out.println(st);//[a, b]
	}
}

三、固定长度的List

操作数组的工具类Arrays,该工具类提供了一个asList(Object... a)方法,该方法可以把一个数组或指定个数的对象转换为一个List集合,这个List集合既不是ArrayList的实例,也不是Vector实现类的实例,而是Arrays内部类的ArrayList的实例。
Arrays.ArrayList是一个固定长度的List集合,程序只能遍历该集合的元素,不可增加、删除该集合中的元素。

import java.util.Arrays;
public class FixedSizeList 
{
	public static void main(String[] args) 
	{
		String[] str={"疯狂Java讲义","轻量级Java EE企业应用实战"};
		var fixedList=Arrays.asList(str);
		//获取fixedList的实现类
		System.out.println(fixedList.getClass());//class java.util.Arrays$ArrayList
		//遍历集合中的元素
		fixedList.forEach(obj->System.out.println("集合元素:"+obj));
		//遍历集合元素方法2
		var it=fixedList.iterator();
		while(it.hasNext())
		{
			System.out.println(it.next());
		}
		//试图增加、删除元素将会引发异常Exception in thread "main" java.lang.UnsupportedOperationException
		//fixedList.add(2,"疯狂Android讲义");
		//fixedList.remove("疯狂Java讲义");

		//执行替换
		fixedList.set(1,"疯狂Android讲义");
		System.out.println(fixedList);//[疯狂Java讲义, 疯狂Android讲义]

	}
}
原文地址:https://www.cnblogs.com/weststar/p/12571346.html