单列集合
List集合
1.List常见集合ArrayList和LinkedList
1.ArrayList是一种单列集合,存储元素可以是任意类型,其有序,有索引,元素线性存储,占据一片连续存储单元,存入顺序和取出顺序一样,因为其底层是通过数组来实现的,Object[]数组 。另外,ArrayList最大的特点是有序,元素可重复,查找快,增删慢。因为底层是数组,通过索引很容易就找到元素,但增删的时候除头尾元素都需要移动大量元素,效率很低。
2.LinkedList同样是一种单列集合,存储元素可以是任意类型,其有序,但没有索引,或者说不像ArrayList那样的索引,元素线性存储,随即占据存储单元,底层是使用双向链表来实现的。另外,LinkedList最大的特点是有序,元素可重复,但查找慢,增删快,增删时只需移动指针域指向对象就可以了,但查找时需要顺着指针域依次遍历找元素,效率很低。
2.ArrayList,LinkedList与Vector的比较
提到ArrayList那就不得不提Vector,前者是线程不同步,即线程不安全的,即任意时刻任意对象都可以操作ArrayList中的元素,但Vector不行,Vector是线程同步的,任意时刻只能有一个对象操作集合中的元素,以保证集合中的元素在使用时不会被其他对象任意改动操作,Vector是栈Stack的底层较好的实现。那么提到线程安全那就不得不提并行和并发(这里只针对于线程,不针对其他),并行是指同一时刻有两个或者两个以上线程同时运行,并发是指多个线程在很短的时间间隔内交替执行。如果是并发,两个,或者多个线程去操作同一个ArrayList集合时,会造成前后读取不一致,这也就导致线程不安全。想要线程安全,ArrayList可以通过Collections工具类的对应synchronized方法来创建集合就是线程安全的。LinkedList同理。不再赘述。
3.常用方法
import java.util.*;
public class Demo01List {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("ccc");
//指定位置添加元素
list.add(1,"bbb");
//获取元素
String s = list.get(2);
//删除元素
String remove = list.remove(2);//被删除的元素
//修改元素
String update = list.set(1, "ddd");//修改前元素内容
//将其他集合转为list集合如set集合,很重要
Set<String> set = new HashSet<>();
set.add("1234");
set.add("5678");
List<String> list1 = new ArrayList<>();
list1.addAll(set);
System.out.println(list1);//结果输出:[1234, 5678]
//元素第一次出现的位置。
list1.add("5678");
System.out.println(list1.indexOf("5678"));//结果输出:1
//转为数组
Object[] objects = list1.toArray();
//是否包含元素
boolean contains = list1.contains("1234");
//排序,使用的是1.8新特性,引用。
list1.addAll(list);
list1.add("1111");
list1.sort(String::compareTo);
System.out.println(list1);//结果输出:[1111, 1234, 5678, 5678, aaa, ddd]
}
}
Set集合
1.HashSet,LinkedHashSet,TreeSet
Set单列集合不像LIst集合有序可重复,Set集合内的元素是无序,不可重复的。那么Set集合和List集合都是添加元素,那么是怎么保证元素不重复的呢?答案是底层涉及到了equals方法,如果Set集合内的类型元素没有重写equals方法,那么Set集合也是无法保证元素是我们所想的那样,即两个元素所有属性都相等,两个对象就相等,而是只能如Object类equals方法一样,两个比较的对象必须是同一个对象才相等,所以要想使用Set集合存储某一类对象,该类除特殊情况必须是要重写equals方法的。那么说到equals方法,那么不得不说hashCode方法,为什么重写equals方法要重写hashCode方法呢?原因很简单,equals方法效率太低了,他是对该类对象的所有属性慢慢一 一比较才能得出两个对象是否相等,这个效率太低了,不能处理数据量大的情况,那么jdk为我们提供了另一种比较方法,hashCode方法,他是根据对象创建时对对象所有属性的ASCII码进行一种特殊运算来得到一个值(具体可以看源码),这个值就是哈希值,当两个对象的哈希值不同,那么这两个对象肯定不同,这个很容易明白,可当两个对象的哈希值相同时,则不能判定两个对象一定相同,因为哈希值运算操作中不能保证所有运算的哈希值不同,所以当两个对象的哈希值相同时,还要调用equals方法比较,挨个比较所有属性值才能确定两个对象是都否等。所以综上所述,重写equals方法,可以不用重写hashCode方法,但这样效率很低,不能处理数据量大的情况,所以一般重写了equals方法我们都会重写hashCode方法来提高效率(当然哈希值很难有重复的,只要不刻意一般都不会碰到哈希值相同的情况)。这样就很好的保证了Set集合的不可重复。了解了Set集合的特性,那么来了解下常用Set集合子类。HashSet:1.8以前底层通过数组 + 链表 1.8以后是通过 数组 + 链表 + 红黑树来实现的。当哈希值不同时,HashSet直接将元素存入数组中,但当哈希值相同时,则是使用链表将哈希值相同元素连接起来构成一个链表,(可以想象一下就是将哈希值相同的元素弄成了一条长链,挂在了数组上相应的哈希值元素上)但当链表元素超过八个时,则会将链表变成红黑树来加快对元素操作。这就有较高的效率来面对海量数据。(原因是链表过长的话影响效率,链表查询很慢,增删快,但删除时他每次还是得一个一个的查询元素才能删除,当链表较长时就不如树有效率,所以当元素超过8个时,就将链表转为红黑树)这个原理是哈希表中的链地址法,可以说HashSet是通过哈希表实现的。LinkedHashSet:继承HashSet,但底层是通过LinkedHashMap来实现的,在HashSet的基础上并没有什么延申,还是HashSet的一些操作,但底层实现原理变了,底层是双向链表,这就使他变得有序,怎么存入就是怎么取出。TreeSet:和LinkedHashSet一样,是Set集合中有序的实现类。TreeSet的底层是通过TreeMap来实现的,键的类型为TreeSet的泛型类型,值为Object类型。TreeSet的主要作用是进行排序,由于底层原理使用到了树结构,其本身在存储时就对元素做了一些排序。当然排序的规则要规定,可以使泛型类型实现Comparable接口或者Comparator接口。如果是Comparable接口,则compareTo方法返回的是固定值0的话,集合只有一个元素,是固定值-1的话集合倒序存储,是固定值1的话集合按序存储,当然按元素比较结果排序时,则是按红黑树结构排序。
网友评论