List接口
1.1 接口特点及主要子类
单列集合
可存放重复元素
元素有序
主要子类
ArrayList:底层数据结构是数组结构。线程不安全的。所以ArrayList的出现替代了Vector。增删慢,查找快。
LinkedList:底层是链表数据结构。线程不安全的,同时对元素的增删操作效率很高。
Vector:底层数据结构是数组结构。jdk1.0版本。线程安全的。无论增删还是查询都非常慢,已被ArrayList替代。
1.2 List接口常用方法
void add(int index, E element) //指定索引添加元素
E remove(int index) //移除指定索引处元素
E get(int index) //获取指定索引元素
E set(int index, E element) //修改指定索引元素
List<E> subList(int fromIndex, int toIndex)//截取指定索引子集
int indexOf(Object o) //返回指定元素索引位置
ListIterator<E> listIterator() 注意:用于应对并发修改异常的返回迭代器方法与迭代器
1.1 具体子类介绍
1.1.1 ArrayList
ArrayList底层数据结构是数组结构。线程不安全的,所以运行速度快,ArrayList的出现替代了Vector。增删慢,查找快,由于日常开发中使用最多的功能为查询数据,遍历数据,所以ArrayList是最常用的集合。目前市面上许多程序员开发时并不严谨,非常随意地使用ArrayList完成任何需求,这种用法是不提倡的。
1.1.2 LinkedList
LinkedList与ArrayList不同,LinkedList是方便添加删除的List。实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,索引该具体子类的特点在于提供了大量首尾操作。
public void addFirst(E e) 添加首个元素
public void addLast(E e) 添加最后元素
public E getFirst() 获取首个元素
public E getLast() 获取最后元素
以及其替代方法
1.1.3 Vector
Vector:我们可以将其理解为版本旧的、安全的、效率低的ArrayList,Vector中提供了一个独特的取出方式,就是枚举Enumeration。此接口Enumeration的功能与 Iterator 接口的功能是类似的。
有兴趣的可以自己了解:
public E elementAt(int index)
public E firstElement()
public E lastElement()
public void setElementAt(E obj, int index)
public void removeElementAt(int index)及其他删除
public Enumeration<E> elements()
Set接口
2.1 接口特点及主要子类
Set是不包含重复元素的集合接口,其子类均无法存放相同元素。
最常用的子类是无序不可重复的HashSet。其方法与Set接口方法相同。
HashSet下还有子类LinkedHashSet,可预测迭代顺序的Set集合。
2.2 判断元素唯一原理
2.2.1 ArrayList的contains方法判断元素是否重复原理
ArrayList的contains方法会使用调用方法时,传入的元素的equals方法依次与集合中的旧元素所比较,从而根据返回的布尔值判断是否有重复元素。此时,当ArrayList存放自定义类型时,由于自定义类型在未重写equals方法前,判断是否重复的依据是地址值,所以如果想根据内容判断是否重复,需要重写equals方法。
2.2.2 HashSet的add/contains等方法判断元素是否重复原理
Set集合不能存放重复元素,其添加方法在添加时会判断是否有重复元素,有重复不添加,没重复则添加。
HashSet集合由于是无序的,其判断唯一的依据是元素类型的hashCode与equals方法的返回结果。规则如下:
先判断新元素与集合内已经有的旧元素的HashCode值
如果不同,判断元素不同。
如果相同,再判断equals比较结果,返回true则相同,返回false则仍然不同。
所以,使用HashSet存储自定义类型,如果没有重写该类的hashCode与equals方法,则判断重复时,使用的地址值,如果想通过内容比较元素是否相同,需要重写该类的hashcode与equals方法。
hashCode方法重写规则:将该对象的各个属性值hashCode相加即是整个对象的HashCode值。如果是基本类型,类似int,则直接返回int值就是该属性的hash值,如果是引用类型,类似String,就调用该成员变量的hashCode方法返回该成员变量hash值。这样可以根据对象的内容返回hashCode值,从而可以根据hashCode判断元素是否唯一。
但是由于在一些”碰巧的”情况下,可能出现内容不同但hashCode相同的情况,为了避免这些情况,我们加入一些干扰系数。
可是加入干扰系数后,仍会出现一些”碰巧”的情况,所以我们还要进行equals的二次判断。
泛型
3.1 泛型概述
我们在集合中大量使用到了泛型,这里来完整地介绍泛型知识。
泛型用来灵活地将数据类型应用到不同的类、方法、接口当中。将数据类型作为参数传递。
泛型是数据类型的一部分,我们将类名与泛型合并一起看做数据类型。
泛型的定义:定义泛型类可以预支地使用未知的类型。
泛型的使用:一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型。
3.2 泛型代码实现
泛型类:
定义:类名后<变量> 如:class A<E> {使用E完全类的定义}
使用:创建对象时确定类型
泛型方法:
定义:方法返回值前<变量> 如:public <T> void method(){使用T}
使用:调用方法时确定类型
泛型接口:
定义:接口后<变量> 如: interface B<T>{使用T完成接口定义}
使用:
1、定义类时确定类型
2、始终不确定类型,直到创建对象时确定类型
3.3 泛型优点及其他
3.3.1 泛型其他
在JDK1.5出现前,使用Object代表任意类型,但在使用时,涉及到了强转的麻烦。泛型替代了Object来代表任意类型。
泛型在编译时会擦除:泛型仅用来在编译期限制、方便程序员的操作,实际上真正编译后的.class中是没有泛型的,其中仍然使用的为Obejct类,通过类似多态的方式完成任意某个类型的指定。
泛型通配符?
定义:(查看ArrayList的构造方法)无法在类中使用
使用:调用方法时可以给予任意类型。参照Arraylist的构造方法
? extends E代表只要是E类型的子类即可
? super E代表只要是E类型的父类即可
3.3.2 泛型优点
提高程序的安全性
将运行期问题转移到了编译期
省去了类型强转的麻烦
优化了程序设计