一、集合
(一)概述
集合是一种容器,用来装数据的,类似于数组,但集合的大小可变,开发中也非常常用。
集合的体系结构可以分为:
- 单列集合Collection每个元素(数据)只包含一个值
- 双列集合Map 每个元素包含两个值(键值对)
Collection集合体系:
Collection集合特点:
List系列集合:有序、可重复
- ArrayList、LinekdList
Set系列集合:无序、不重复
- HashSet
- LinkedHashSet: 存取有序
- TreeSet:可排序
(二)Collection的常用方法
Collection是单列集合的祖宗,它规定的方法(功能)是全部单列集合都会继承的。
常见方法:
方法名 | 说明 |
public boolean add(E e) | 把给定的对象添加到当前集合中 |
public void clear() | 清空集合中所有的元素 |
public boolean remove(E e) | 把给定的对象在当前集合中删除 |
public boolean contains(Object obj) | 判断当前集合中是否包含给定的对象 |
public boolean isEmpty() | 判断当前集合是否为空 |
public int size() | 返回集合中元素的个数 |
public Object[] toArray() | 把集合中的元素,存储到数组中 |
(三)Collection的遍历方法
1、迭代器方法
迭代器是用来遍历集合的专用方式(数组没有迭代器),在Java中迭代器的代表是Iterator。
Collection集合获取迭代器的方法:
Iterator<E> iterator()
返回集合中的迭代器对象,该迭代器对象默认指向当前集合的第一个元素
Iterator迭代器中的常用方法:
方法名 | 说明 |
boolean hasNext() | 询问当前位置是否有元素存在, 存在返回true ,不存在返回false |
E next() | 获取当前位置的元素, 并同时将迭代器对象指向下一个元素处。 |
举例子:
iterator.hasNext() 作为while循环的条件语句,为了判断遍历过程中指向的位置是否有元素,有则返回true,循环继续。
Iterator<String> iterator = lists.iterator();
while(iterator.hasNext()){
String str = iterator.next();
System.out.println(str);
}
2、增强for循环方法
for (元素的数据类型 变量名 : 数组或者集合) {
}
- 增强for可以用来遍历集合或者数组
- 增强for遍历集合,本质就是迭代器遍历集合的简化写法
3、Lambda表达式遍历集合
Lambda表达式,提供了一种更简单、更直接的方式来遍历集合,需要使用Collection的如下方法来完成:
default void forEach(Consumer<? super T> action)
举例子:
Collection<String> lists = new ArrayList<>();
...
lists.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
//转化为Lambda形式
lists.forEach(s -> {
System.out.println(s);
});
//再简化一点
lists.forEach(s -> System.out.println(s));
(四)Collection的并发修改异常
使用迭代器遍历集合时,又同时在删除集合中的数据,程序就会出现并发修改异常的错误。
由于增强for循环遍历集合就是迭代器遍历集合的简化写法,因此,使用增强for循环遍历集合,又在同时删除集合中的数据时,程序也会出现并发修改异常的错误。
解决方法:
- 使用迭代器遍历集合:但用迭代器自己的删除方法删除数据即可。
- 使用普通for循环遍历集合:可以倒着遍历并删除;或者从前往后遍历,但删除元素后做i --操作。
注意:使用增强for循环遍历集合无法解决这个问题
二、List集合
(一)List集合的特有方法
List集合因为支持索引,所以多了很多与索引相关的方法,当然,Collection的功能List也都继承了。
方法名 | 说明 |
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
List集合支持的遍历方式:
- 迭代器
- 增强for循环
- Lambda表达式
- for循环(因为List集合有索引)
(二)ArrayList
ArrayList底层是基于数组实现的。
特点是:
1、查询速度快(注意:是根据索引查询数据快)。
2、查询数据通过地址值和索引定位,查询任意数据耗时相同,适合数据量不是很大时。
3、删除效率低,实际删除中可能需要把后面很多的数据进行前移。
4、添加效率极低,可能需要把后面很多的数据后移,再添加元素;或者也可能需要进行数组的扩容。
ArrayList的底层原理
- 利用无参构造器创建的集合,会在底层创建一个默认长度为0的数组
- 添加第一个元素时,底层会创建一个新的长度为10的数组
- 存满时,会扩容1.5倍
- 如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准
(三)LinkedList
LinkedList是基于双链表实现的,链表中的结点是独立的对象,在内存中是不连续的,每个结点包含数据值和下一个结点的地址。
特点:
- 查询慢,无论查询哪个数据都要从头开始找
- 链表增删相对快
- 对首尾元素进行增删改查的速度是极快的
LinkedList新增了许多首尾操作的特有方法:
三、Set集合
(一)概述
Set系列集合特点:无序,不可重复
- HashSet
- LinkedHashSet:存取有序
- TreeSet:可排序
Set要用到的常用方法,基本上就是Collection提供的。 自己几乎没有额外新增一些常用功能!
(二)HashSet
1、哈希值
哈希值是一个int类型的数值,Java中每个对象都有一个哈希值。
Java中的所有对象,都可以调用Obejct类提供的hashCode方法,返回该对象自己的哈希值。
public int hashCode():返回对象的哈希码值
对象哈希值的特点:
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的
- 不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞)
- Object的hashCode方法根据"对象地址值"计算哈希值
- 子类重写后的hashCode方法可以根据"对象属性值"计算哈希值
HashSet集合判定两个对象的标准就是两个对象的hash值是否一致, 因此我们经常重写hashcode实现集合中对象去重(同时还要重写equals方法,哈希值只能判断是否为同一个对象,对象中的具体内容需要重写的equals方法来判断)
2、底层原理
- 基于哈希表实现
- 哈希表是一种增删改查数据,性能都较好的数据结构
JDK8之前,哈希表 = 数组+链表,JDK8开始,哈希表 = 数组+链表+红黑树
HashSet的底层原理:
- 创建一个默认长度16的数组,默认加载因子为0.75,数组名table
- 使用元素的哈希值对数组的长度求余计算出应存入的位置
- 判断当前位置是否为null,如果是null直接存入 如果不为null,表示有元素,则调用equals方法比较
- 如果不为null,表示有元素,则调用equals方法比较:相等,则不存;不相等,则存入数组
- 当数组存满到16*0.75=12时,就自动扩容,每次扩容原先的两倍
- JDK8开始,当链表长度超过8,且数组长度>=64时,自动将链表转成红黑树
(三)LinkedHashSet
底层原理:
- LinkedHashSet是不可重复的,存取有序的,底层是基于哈希表(数组、链表、红黑树)实现的
- 但是,它的每个元素都额外的多了一个双链表的机制记录它前后元素的位置
(四)TreeSet
特点:
- 不重复、无索引、可排序(默认升序排序 ,按照元素的大小,由小到大排序)
- 底层是基于红黑树实现的排序
注意:
- 对于数值类型:Integer , Double,默认按照数值本身的大小进行升序排序
- 对于字符串类型:默认按照首字符的编号升序排序
- 对于自定义类型如Teacher对象,TreeSet默认是无法直接排序的,需要自定义排序规则
自定义排序规则:
方式一:自然排序 让自定义的类(如教师类)实现Comparable接口,重写里面的compareTo方法来指定比较规则。
方式二:比较器排序 通过调用TreeSet集合有参数构造器,可以设置Comparator对象(比较器对象,用于指定比较规则)。
public TreeSet(Comparator<? super E> comparator) //设置比较器对象
四、总结
Q&A
1、如果希望记住元素的添加顺序,需要存储重复的元素,又要频繁的根据索引查询数据?
答:用ArrayList集合(有序、可重复、有索引),底层基于数组的。(常用)
2、如果希望记住元素的添加顺序,且增删首尾数据的情况较多?
答:用LinkedList集合(有序、可重复、有索引),底层基于双链表实现的。
3、如果不在意元素顺序,也没有重复元素需要存储,只希望增删改查都快?
答:用HashSet集合(无序,不重复,无索引),底层基于哈希表实现的。 (常用)
4、如果希望记住元素的添加顺序,也没有重复元素需要存储,且希望增删改查都快?
答:用LinkedHashSet集合(有序,不重复,无索引), 底层基于哈希表和双链表。
5、如果要对元素进行排序,也没有重复元素需要存储?且希望增删改查都快?
答:用TreeSet集合,基于红黑树实现。