1 List集合的实现类
1.1 ArrayList
ArrayList特点
- 底层为数组结构,查询快,增删慢,本质上是一个长度可变(ArrayList对象的引用可变)的数组
- 实现不同步
- 进行扩容时会将容量变为原来的1.5倍
在1.8前后 ArrayList的实现区别
JDK1.7:创建时直接创建了一个长度为10的数组,类似于饿汉式
JDK1.8以后:创建时创建一个长度为0的数组,当添加元素时,再创建一个长度为10的数组,类似于懒汉式
1.2 LinkedList
底层为双向链表,增删快,查询慢
有头插法和尾插法两种方法添加元素
void | addFirst(E e) 在该列表开头插入指定的元素。 |
---|---|
void | addLast(E e) 将指定的元素追加到此列表的末尾。 |
E | element() 检索但不删除此列表的头(第一个元素)。 |
E | getFirst() 返回此列表中的第一个元素。 |
E | getLast() 返回此列表中的最后一个元素 |
1.3 Vector
是一个古老的集合(在JDK1中就有),底层是可变数组
Vector是同步的,如何不需要线程安全的实现,建议使用ArrayList代替Vector
1.4 List的实现类的选择
- ArrayList 数据查找频率高时使用
- LinkedList 数据元素操作频率高时使用
- Vector 效率低于其他实现类,如果不考虑线程安全则不使用
面试题
ArrayList / LinkedList / Vector 的异同?ArrayList 的底层实现是什么?扩容机制?Vector 和ArrayList的区别?
ArrayList和LinkedList的异同
二者的线程都是不安全的,但相对于线程安全的 Vector 的效率高
ArrayList 是基于动态数组的数据结构,LinkedList 是基于链表的数据结构;对于元素的 get 和 set ,ArrayList(索引) 优于LinkedList(移动指针);对于新增、删除等操作 LinkedList(改变指针指向) 效率高于 ArrayList(移动数据)。
ArrayList 和 Vector 的区别
Vector 是线程安全且同步的而 ArrayList 不是
扩容机制: ArrayList 扩容是原来的1.5倍,Vector 扩容是原来的2倍
2 Set集合
- 不包含重复元素
- 最多只有一个 null 值
- 没有索引,只能通过增强 for 循环和迭代器遍历
- Set 集合无序(集合中的元素添加顺序和迭代顺序不一致)
2.1 Set 的实现类
2.1.1 HashSet
HashSet 特点:
- 底层数据结构是哈希表
- 对于迭代的顺序不做保证,即保存顺序和获取顺序不一致
- 没有索引,只能通过增强 for 循环和迭代器遍历
- 不包含重复元素
- 最多只有一个 null 值
- 此实现不同步
- 初始容量为16 扩容时变为原来的1.75倍
2.1.2 HashSet 集合元素唯一性
原理:
-
获取对象哈希码,根据哈希码计算存储位置
- 当前位置没有元素——>直接存储
- 当前位置有元素——>进行下一步
-
将对象的哈希值和当前位置所有元素的哈希值比较
- 哈希值不同——>直接存储
- 哈希值相同——>进行下一步
-
通过 equals 方法比较两个元素内容
- 内容不同——>直接存储
- 内容相同——>判断元素重复,不存储
对于存入hashSet集合中对象,都必须重写hashCode和equals方法 保证元素的唯一性
2.2 哈希表
数据结构中的一种
JDK8之前,哈希表底层采用数组+链表来实现,可以认为是一个元素为链表的数组
JDK8之后,哈希表底层采用数组+链表+红黑树来实现,当链表长度超过一定值时就会将其转换成红黑树
2.3 LinkedHashSet
特点:
- 是 Set 接口的哈希表和双向链表的实现,是有序的
- 唯一性
格式:
Set 对象名 = new LinkedHashSet()
2.4 TreeSet
- 基于 TreeMap 实现
- 可对其中元素进行排序,排序方式:自然排序、定制排序
- 此实现不同步
格式:
TreeSet 对象名 = new TreeSet()
2.4.1 自然排序
对于存入 TreeSet 的数据都必须实现 Comparable 接口
Comparable 接口:
该接口会对实现他的每个类进行一个整体排序,称之为类的自然排序,其方法 compareTo 称之为自然比较方法
Comparable 方法
int compareTo(T o)(o 为要比较的对象)
将此对象与指定对象进行比较,返回值为负整数、零、正整数,代表该对象小于、等于、大于指定对象
通过实现Comparable 接口并且重写 compareTo()方法可以将想要的数据类型保存到 TreeSet 集合中,重写方法时要注意排序规则,并且最好主要条件和次要条件都有定义
2.4.2 定制排序(比较器排序)
Comparator 接口
Comparator方法
int compare(T o1,T o2)比较两个参数的顺序
在定制排序中需要用到 TreeSet 的构造方法 TreeSet(Comparator<? super E> comparator) 构造一个新的集合,根据指定的比较器进行排序
推荐使用匿名内部类,也可在新建一个类实现比较器的接口
格式:
TreeSet 对象名 = new TressSet(new Comparator(){匿名内部类主体});
3 泛型
是JDK1.5引入的特性,提供了编译时类型的安全检测机制,允许在编译时检测非定义类型的数据。本质是参数化类型,即由会操作的数据类型进行
参数类型理解:将类型原来的具体类型参数化,然后在使用/调用时传入具体的类型
(输入数据时类似形参,输出结果类似实参)
该参数化类型可用于类、方法和接口,分别称为泛型类、泛型方法、泛型接口
格式:
<类型> 指定一种类型
<类型1,类型2,……> 指定多种类型
泛型的好处:
- 在编译时期就能检测数据类型
- 避免强制类型转换
3.1 泛型的应用
public static void main(String[] args) {
List<String> list1 = new ArrayList<String>();//在jdk7以前
List<String> list2 = new ArrayList<>();//在jdk7开始 菱形语法
list2.add("abc");
list2.add("12");
list2.add(123);//报错
for(String str : list2){
System.out.println(str);
}
3.2 自定义泛型
3.2.1 定义泛型类
格式:
修饰符 class 类名 T可为任意标识符 其余还有E K V 等
自定义泛型类中的 get 和 set 不是泛型方法
3.2.2 泛型方法
格式:
修饰符 返回类型 方法名称(参数列表)
3.2.3 泛型接口
格式:
修饰符 interface 接口名
3.2.4 泛型通配符
用于表示泛型 List 的父类,形式为<?>
List<?> 表示一个元素类型未知的 List,他的元素可以匹配任意类型,但不能往其中添加元素
通配符的基本用法:
类型通配符的上限:<?extends 类型> 表示的类型为该类型或该类型的子类
类型通配符的下限:<?super 类型> 表示的类型为该类型或该类型的父类
通配符用法:在要用集合做参数时,可用其接收一个知道 List 却不知道其泛型类型的集合作为实参
通配符好处:
- 提高了代码的通用性和扩展性
- 可以接受任意类型的集合作为实参
泛型擦除:泛型只在编译期有效,在运行时(如输出)泛型会丢失,该现象称为泛型擦除
4 可变参数
可用作方法的形参,作为形参时方法的参数可变
用法于数组相同
格式:
修饰符 返回值类型 方法名 (数据类型。。。变量名) 表面该方法可以接受指定数据类型的多个参数
注意事项:在一个方法中可有多个参数,但只能有一个可变参数,且可变参数必须在参数列表末尾