Java中的集合是我们编程中常用的东西,集合的作用是用来存放对象的,让我更好的管理对象!
Java集合分为三类:List、Queue、Set
List是一个集合,按照元素插入的先后顺序进行排列。
1.1、ArrayList
ArrayList的底层是数组,当空间不足的时候,会进行动态扩容。
数组的好处就是随机存储块,但是插入和删除效率不高,删除需要搬运元素,插入有的时候需要动态扩容。
ArrayList是线程不安全的,没有加锁,会出现线程不安全问题
ArrayList需要注意的就是遍历删除指定的元素,这点有点坑。
//错误代码
ArrayList<String> list = new ArrayList<String>();
list.add("jaychou");
list.add("jaychou");
list.add("jay");
for (int i = 0;i < list.size();i++){
if(list.get(i).equals("jaychou")){
list.remove(i);
}
}
上面的代码其实是有问题的,因为当删除下标为0第一个"jaychou"的时候,后面的那个“jaychou”,会被移动到下标为0的位置,而下次循环的时候i变成了1,第二个"jaychou"就侥幸逃脱了。
如果通过list.remove(“jaychou”),跟上面的效果是一样的。
//正确代码
int i = 0;
while(i < list.size()){
if(list.get(i).equals("jaychou")){
list.remove(i);
}else{
i++;
}
}
上述代码才能正确的删除。
也可以通过Iterator来删除
Iterator<String> it = list.iterator();
while (it.hasNext()){
if (it.next().equals("jaychou")){
it.remove();
}
}
创建不变的ArrayList集合
List<String> list = Arrays.asList("jaychou","jaychou");
通过Arrays.asList(),创建的list无法插入和删除元素,会报错!
这种创建的ArrayList的效率更高。
2、Vector
和ArrayList几乎一致,底层都是数组实现的,只不过Vector是线程安全的。
删除等方法都和ArrayList一致
3、LinkedList
LinkedList的底层结构是链表,特点就是删除和插入效率高,查询效率低。
LinkedList除了当线性表外,还可以当Queue,也就是队列。
LinkedList的底层是双向链表,类中实现了首尾节点,实现了双端队列。
add(E e):向队尾加入元素
poll():从队首弹出并返回元素
peek():获取队首元素
addFirst(E e):向队首中加入元素
removeFirst():从队首弹出并返回元素
peekFirst():获取队首元素
addLast(E e):向队尾加入元素
removeLast():从队尾弹出并返回元素
peekLast():获取队尾元素
LinkedList也是线程不安全的。
名称 | 底层实现 | 线程安全 |
---|---|---|
ArrayList | 数组 | 否 |
Vector | 数组 | 是 |
LinkedList | 双向链表 | 否 |
这里我们只说下常用的LinkedList和优先队列PriorityQueue和ArrayDeque
LinkedList上面我们已经说过了不在说了
ArrayDeque
ArrayDeque也是双端队列,和LinkedList一样,只不过LinkedList的底层是链表实现的,而ArrayDeque是依靠循环数组实现的。
当ArrayDeque满的时候,会进行双倍扩容。
其使用方法和LinkedList大致一样,就不再单独叙述了。
PriorityQueue
优先队列的底层结构是数组,是用数组模拟的满二叉树。
如下图所示,堆分为大顶堆和小顶堆。PriorityQueue的默认为小顶堆。
如果想要实现自定义排序,可以自定义Comparator对象,然后通过构造方法传入就可以了。
PriorityQueue pq = new PriorityQueue(myCmp);
常用方法:
add(E e):向堆中加入元素
remove(E e):从堆中移除指定元素
poll():从堆顶弹出并返回元素
peek():获取堆顶元素
set也是一个集合,只不过不允许存在重复的元素
本篇文章,我们主要介绍HashSet、LinkedHashSet和TreeSet。
1、HashSet
从名字就可以看出来,HashSet的底层是涉及到Hash表的。源码中HashSet中是依靠HashMap实现的。
HashMap的实现你们可以参考上一篇文章:第十一篇:Java中Map详解 HashMap、LinkedHashMap、TreeMap、HashMap扩容原理详解
HashSet<Integer> set = new HashSet<>();
set.add(1);
HashSet中内部定义了一个PRESENT常量;
private final Object PRESENT = new Object();
比如set.add(1),是将1作为key,PRESENT作为value,插入map中。
通过HashMap,HashSet能够做到元素不重复。
主要方法:
add(E e):加入元素e
remove(E e):移除元素e
contains(E e):判断是否存在元素e
遍历方式:
1、Iterator
Iterator<String> it = set.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
2、for循环
for (String s:set){
System.out.println(s);
}
通过for循环我们可以看出来,HashSet中是没有下标的概念的,无法通过下标来获得数据。HashSet中的元素不是按照元素的插入顺序排列的,而是按照一定的Hash算法来排列的。
2、LinkedHashSet
通过Linked关键词,就大概知道LinkedHashSet和HashSet的大概区别了。
HashSet中的元素遍历输出不是按照元素的插入顺序排列的,而LinkedHashSet是按照元素插入元素排列的。
LinkedHashSet是通过LinkedHashMap来实现的。
LinkedHashMap的实现你们可以参考上一篇文章:第十一篇:Java中Map详解 HashMap、LinkedHashMap、TreeMap、HashMap扩容原理详解
其使用方法和HashSet基本一致。
3、TreeSet
TreeSet是依照TreeMap的原理实现的
TreeMap的实现你们可以参考上一篇文章:第十一篇:Java中Map详解 HashMap、LinkedHashMap、TreeMap、HashMap扩容原理详解
TreeSet和HashSet、LinkedHashSet的区别就是TreeSet中的元素是有序的。
当用户初始化TreeSet的时候,可以传入自定义的Comparator。
如果是自定义对象的TreeSet的话,自定义对象需要实现Comparable接口,要不再put元素的时候会报错。