Vector
同步:Vector的实现与 ArrayList 类似,但是方法上使用了 synchronized 进行同步
构造:默认长度为 10 的数组
扩容:Vector 的构造函数可以传入 capacityIncrement 参数,作用是在扩容时使容量 capacity 增长 capacityIncrement,如果这个参数的值小于等于 0(默认0),扩容时每次都令 capacity 为原来的两倍
对比 ArrayList
-
Vector 是同步的,开销比 ArrayList 要大,访问速度更慢。最好使用 ArrayList 而不是 Vector,因为同步操作完全可以由程序来控制
-
Vector 每次扩容请求其大小的 2 倍(也可以通过构造函数设置增长的容量),而 ArrayList 是 1.5 倍
-
底层都是
Object[]
数组存储
LinkedList
介绍
LinkedList 也是 List 的实现类:基于双向链表实现,使用 Node 存储链表节点信息,增删比较快,查询慢
LinkedList 除了拥有 List 集合的全部功能还多了很多操作首尾元素的特殊功能:
public boolean add(E e)
:将指定元素添加到此列表的结尾public E poll()
:检索并删除此列表的头(第一个元素)public void addFirst(E e)
:将指定元素插入此列表的开头public void addLast(E e)
:将指定元素添加到此列表的结尾public E getFirst()
:返回此列表的第一个元素public E getLast()
:返回此列表的最后一个元素public E removeFirst()
:移除并返回此列表的第一个元素public E removeLast()
:移除并返回此列表的最后一个元素public E pop()
:从此列表所表示的堆栈处弹出一个元素public void push(E e)
:将元素推入此列表所表示的堆栈public int indexOf(Object o)
:返回此列表中指定元素的第一次出现的索引,如果不包含返回 -1public int lastIndexOf(Object o)
:从尾遍历找public boolean remove(Object o)
:一次只删除一个匹配的对象,如果删除了匹配对象返回truepublic E remove(int index)
:删除指定位置的元素
public class ListDemo { public static void main(String[] args) { // 1.用LinkedList做一个队列:先进先出,后进后出。 LinkedList<String> queue = new LinkedList<>(); // 入队 queue.addLast("1号"); queue.addLast("2号"); queue.addLast("3号"); System.out.println(queue); // [1号, 2号, 3号] // 出队 System.out.println(queue.removeFirst());//1号 System.out.println(queue.removeFirst());//2号 System.out.println(queue);//[3号] // 做一个栈 先进后出 LinkedList<String> stack = new LinkedList<>(); // 压栈 stack.push("第1颗子弹");//addFirst(e); stack.push("第2颗子弹"); stack.push("第3颗子弹"); System.out.println(stack); // [ 第3颗子弹, 第2颗子弹, 第1颗子弹] // 弹栈 System.out.println(stack.pop());//removeFirst(); 第3颗子弹 System.out.println(stack.pop()); System.out.println(stack);// [第1颗子弹] } }
源码
LinkedList 是一个实现了 List 接口的双端链表,支持高效的插入和删除操作,另外也实现了 Deque 接口,使得 LinkedList 类也具有队列的特性
核心方法:
-
使 LinkedList 变成线程安全的,可以调用静态类 Collections 类中的 synchronizedList 方法:
List list = Collections.synchronizedList(new LinkedList(...));
-
私有内部类 Node:这个类代表双端链表的节点 Node
private static class Node<E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } }
-
构造方法:只有无参构造和用已有的集合创建链表的构造方法
-
添加元素:默认加到尾部
public boolean add(E e) { linkLast(e); return true; }
-
获取元素:
get(int index)
根据指定索引返回数据- 获取头节点 (index=0):getFirst()、element()、peek()、peekFirst() 这四个获取头结点方法的区别在于对链表为空时的处理,是抛出异常还是返回null,其中getFirst() 和element() 方法将会在链表为空时,抛出异常
- 获取尾节点 (index=-1):getLast() 方法在链表为空时,会抛出NoSuchElementException,而peekLast() 则不会,只会返回 null
-
删除元素:
- remove()、removeFirst()、pop():删除头节点
- removeLast()、pollLast():删除尾节点,removeLast()在链表为空时抛出NoSuchElementException,而pollLast()方法返回null
对比 ArrayList
- 是否保证线程安全:ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全
- 底层数据结构:
- Arraylist 底层使用的是
Object
数组 - LinkedList 底层使用的是双向链表数据结构(JDK1.6 之前为循环链表,JDK1.7 取消了循环)
- Arraylist 底层使用的是
- 插入和删除是否受元素位置的影响:
- ArrayList 采用数组存储,所以插入和删除元素受元素位置的影响
- LinkedList采 用链表存储,所以对于
add(E e)
方法的插入,删除元素不受元素位置的影响
- 是否支持快速随机访问:
- LinkedList 不支持高效的随机元素访问,ArrayList 支持
- 快速随机访问就是通过元素的序号快速获取元素对象(对应于
get(int index)
方法)。
- 内存空间占用:
- ArrayList 的空间浪费主要体现在在 list 列表的结尾会预留一定的容量空间
- LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)
Set
概述
Set 系列集合:添加的元素是无序,不重复,无索引的
- HashSet:添加的元素是无序,不重复,无索引的
- LinkedHashSet:添加的元素是有序,不重复,无索引的
- TreeSet:不重复,无索引,按照大小默认升序排序
面试问题:没有索引,不能使用普通 for 循环遍历
HashSet
哈希值:
-
哈希值:JDK 根据对象的地址或者字符串或者数字计算出来的数值
-
获取哈希值:Object 类中的 public int hashCode()
-
哈希值的特点
- 同一个对象多次调用 hashCode() 方法返回的哈希值是相同的
- 默认情况下,不同对象的哈希值是不同的。而重写 hashCode() 方法,可以实现让不同对象的哈希值相同
HashSet 底层就是基于 HashMap 实现,值是 PRESENT = new Object()
Set集合添加的元素是无序,不重复的。
-
是如何去重复的?
1.对于有值特性的,Set集合可以直接判断进行去重复。 2.对于引用数据类型的类对象,Set集合是按照如下流程进行是否重复的判断。 Set集合会让两两对象,先调用自己的hashCode()方法得到彼此的哈希值(所谓的内存地址) 然后比较两个对象的哈希值是否相同,如果不相同则直接认为两个对象不重复。 如果哈希值相同,会继续让两个对象进行equals比较内容是否相同,如果相同认为真的重复了 如果不相同认为不重复。 Set集合会先让对象调用hashCode()方法获取两个对象的哈希值比较 / \ false true / \ 不重复 继续让两个对象进行equals比较 / \ false true / \ 不重复 重复了
-
Set系列集合元素无序的根本原因
Set系列集合添加元素无序的根本原因是因为底层采用了哈希表存储元素。 JDK 1.8 之前:哈希表 = 数组(初始容量16) + 链表 + (哈希算法) JDK 1.8 之后:哈希表 = 数组(初始容量16) + 链表 + 红黑树 + (哈希算法) 当链表长度超过阈值8且当前数组的长度 > 64时,将链表转换为红黑树,减少了查找时间 当链表长度超过阈值8且当前数组的长度 < 64时,扩容
每个元素的 hashcode() 的值进行响应的算法运算,计算出的值相同的存入一个数组块中,以链表的形式存储,如果链表长度超过8就采取红黑树存储,所以输出的元素是无序的。
-
如何设置只要对象内容一样,就希望集合认为它们重复了:重写 hashCode 和 equals 方法
Linked
LinkedHashSet 为什么是有序的?
LinkedHashSet 底层依然是使用哈希表存储元素的,但是每个元素都额外带一个链来维护添加顺序,不光增删查快,还有顺序,缺点是多了一个存储顺序的链会占内存空间,而且不允许重复,无索引
TreeSet
TreeSet 集合自排序的方式:
- 有值特性的元素直接可以升序排序(浮点型,整型)
- 字符串类型的元素会按照首字符的编号排序
- 对于自定义的引用数据类型,TreeSet 默认无法排序,执行的时候报错,因为不知道排序规则
自定义的引用数据类型,TreeSet 默认无法排序,需要定制排序的规则,方案有 2 种:
-
直接为对象的类实现比较器规则接口 Comparable,重写比较方法:
方法:`public int compareTo(Employee o): this 是比较者, o 是被比较者` * 比较者大于被比较者,返回正数(升序) * 比较者小于被比较者,返回负数 * 比较者等于被比较者,返回 0
-
直接为集合设置比较器 Comparator 对象,重写比较方法:
方法:
public int compare(Employee o1, Employee o2): o1 比较者, o2 被比较者
- 比较者大于被比较者,返回正数
- 比较者小于被比较者,返回负数
- 比较者等于被比较者,返回 0
注意:如果类和集合都带有比较规则,优先使用集合自带的比较规则
public class TreeSetDemo{ public static void main(String[] args){ Set<Student> students = new TreeSet<>(); Collections.add(students,s1,s2,s3); System.out.println(students);//按照年龄比较 升序 Set<Student> s = new TreeSet<>(new Comparator<Student>(){ @Override public int compare(Student o1, Student o2) { // o1比较者 o2被比较者 return o2.getAge() - o1.getAge();//降序 } }); } } public class Student implements Comparable<Student>{ private String name; private int age; // 重写了比较方法。 // e1.compareTo(o) // 比较者:this // 被比较者:o // 需求:按照年龄比较 升序,年龄相同按照姓名 @Override public int compareTo(Student o) { int result = this.age - o.age; return result == 0 ? this.getName().compareTo(o.getName):result; } }
比较器原理:底层是以第一个元素为基准,加一个新元素,就会和第一个元素比,如果大于,就继续和大于的元素进行比较,直到遇到比新元素大的元素为止,放在该位置的左边。(树)