容器类(一)——List

容器类概述

在使用数组存储数据时,数组尺寸固定这一限制显得过于局限。因此,Java 实用类库提供了一套完整的容器类来解决这个问题,其中基本的类型是 List、Set、Queue 和 Map。这些对象类型也称为集合类,但由于 Java 的类库使用了 Collection 这个名词来指代该类库的一个特殊子集,所以通常使用范围更广的“容器类”来称呼它们。

1、分类

Java 容器类的作用是“保存对象”,并将其划分为两个不同的概念:

(1)Collection——一个独立元素的序列,这些元素都服从一条或多条规则,包括 List、Set 和 Queue。

(2)Map——一组成对的“键值对”对象,可以使用键来查找值。

2、容器类的打印

数组是不支持直接打印的,可以使用 Arrays.toString() 将数组以字符串的形式打印。而容器类是支持直接打印的,比如,有一个 List 对象,

List<Integer> list = new ArrayList<>();

可以直接使用

System.out.println(list);

打印出 list 的内容。

3、容器类的创建

可以不指定类型就直接创建容器,如下:

ArrayList list = new ArrayList();

此时编译器会报告警告信息但不会报错,因为这个示例没有使用泛型。并且 list 没有指定参数类型,因此可以添加任何类型的对象,比如:

list.add(3);// int型
list.add(“string”);// 字符串

但是,当你使用 get() 从容器中取对象的时候,得到的只是一个 Object 引用,而无法确定得到的是 int 型的还是 String 型的。因此通常使用泛型来创建容器类,声明如下:

List<Integer> list = new ArrayList<Integer>();
List<Integer> list = new ArrayList<>();

两种方式是一样的,后一个尖括号中的类型可以不写。

尖括号括起来的就是类型参数,它指定了这个容器可以保存的类型,这样,就可以在编译期防止将错误类型的对象放入容器中,使得编程更加方便清晰。

一、List 接口

List 接口在 Collection 的基础上添加了大量的方法,使得可以在 List 中插入和删除元素。List 是线性的存储容器,可通过索引访问元素。有三种类型的 List,分别为 LinkedList、ArrayList 和 Vector。

二、LinkedList、ArrayList 和 Vector 的区别及用途

1、区别

ArrayList 和 Vector 是基于数组实现的(ArrayList 和 Vector 的区别在于,Vector 使用了 synchronized 方法,是线程安全的,所以性能上比 ArrayList 要差)。由于是基于数组实现的,所以允许直接序号索引元素,可以快速随机访问元素;但是插入和删除数据要移动部分数组元素,所以插入和删除的速度慢。

LinkedList 是基于链表实现的,所以索引数据时要进行遍历,速度慢;而插入和删除时,只需记录删除元素的前后项,所以插入和删除速度快。

总的来说:

  • 快速随机访问元素,使用 ArrayList;
  • 快速插入和删除元素,使用 LinkedList;
  • 多线程时,才使用 Vector。

2、用途

ArrayList 提供了一些集合类的基本方法,如增加和删除元素,以及动态调整大小(这是动态数组的特性)等。相比于 ArrayList,LinkedList 提供了更大的特性集,因为它添加了可以用作栈、队列和双端队列的方法。这些方法只是存在些许差异,例如:

  • getFirst() 和 element() 完全一样,它们都返回列表的第一个元素,而不移除它,如果 列表为空,就抛出 NoSuchElementException;peek() 与这两个方法只是稍有差异,它在列表为空时返回 null。
  • removeFirst() 和 remove() 也完全一样,它们移除并返回列表的头,在列表为空时抛出 NoSuchElementException;poll() 稍有差异,它在列表为空时返回 null。
  • addFirst()、add() 和 addLast() 、offer() 相同,它们都将某个元素添加到列表的头(尾)部。
  • removeLast() 移除并返回列表的最后一个元素。

Tips:

  • 利用LinkedList 的 offer()、peek()、poll() 和 remove() 可以使其成为一个 Queue 的实现;
  • 利用LinkedList 的 pop() 、push() 和 peek() 可以使其成为一个 Stack 的实现。

三、为什么要声明为 List< Integer> list = new ArrayList<>() ?

首先,List 是一个接口,而接口是不能创建对象的,但是可以为接口创建一个引用。

List list;// 正确:为接口创建一个引用
List list = new List();// 错误:接口不能创建对象

ArrayList 是 List 的一个实现类,可以创建一个 ArrayList 对象赋值给 List 引用,如:

List<Integer> list = new ArrayList<>() ;

这句代码创建了一个 ArrayList 对象然后将其上溯到了 List,这时候它就不再是 ArrayList 对象了,有些 ArrayList 有但是 List 没有的属性和方法,它也就不能再用了。

为什么要声明为 List< Integer> list = new ArrayList<>() 而不是 ArrayList< Integer> list = new ArrayList<>() ?

因为 List 有多个实现类。假如刚开始声明为 List< Integer> list = new ArrayList<>(),如果需要将 ArrayList 换成其它的实现类,如 LinkedList,只需将 List< Integer> list = new ArrayList<>() 改为 List< Integer> list = new LinkedList<>(),而其它使用 list 的地方不需要改动。

倘若一开始就声明为 ArrayList< Integer> list = new ArrayList<>() ,将 ArrayList 换成 LinkedList 时,所有使用到 ArrayList 特有属性和方法的地方都要改动。

四、List 的遍历

假设已有一个 ArrayList 的对象 list。

方式一:for 循环

for(int i = 0; i < list.size(); i++){
   System.out.print(list.get(i) + " ");
}

方式二:foreach 循环

for(Integer i : list){
   System.out.print(i + " ");
}

方式三:迭代器

迭代器(Iterator)是一个对象,它的工作就是遍历并选择序列中的对象。使用方法如下:

  • 使用 iterator() 方法返回一个 Iterator,并且这个 Iterator 将准备好返回序列的第一个元素;
  • 使用 next() 获得序列中的下一个元素;
  • 使用 hasNext() 检查序列中是否还有元素;
  • 使用 remove() 将迭代器新近返回的元素删除。

示例如下:

Iterator<Integer> it = list.iterator();
while(it.hasNext()){
	Integer i = it.next();
	System.out.print(i + " ");
}
发布了56 篇原创文章 · 获赞 0 · 访问量 935

猜你喜欢

转载自blog.csdn.net/weixin_45594025/article/details/104936704