List判断两个对象相等只通过equals方法比较返回true即可。
public class A {
@Override
public boolean equals(Object arg0) {
return true;
}
}
public class SameAListTest {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
//[1, 2, 3]
System.out.println(list);
list.remove(new A());
//[2, 3]
System.out.println(list);
list.remove(new A());
//[3]
System.out.println(list);
}
}
从上面程序可以看出,当程序试图删除一个A对象,List将会调用该A对象的equals方法依次与集合元素进行比较,如果该equals方法以某个集合元素作为参数时返回true,List将会删除该元素,A重写了equals方法,该方法总是返回true。
当调用List的set(int index,Object object)方法来改变List集合指定所引处的元素时,指定的索引必须是List集合的有效索引。例如集合长度是4,就不能指定替换索引为4处的元素--也就是说,set(int index,Object object)方法不会改变List集合的长度。
List还额外提供了一个listIterator方法,该方法返回一个ListIterator对象,ListIterator接口继承了Iterator接口,提供了专门操作List的方法。
public class ListIterators {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("123");
list.add("231");
list.add("132");
/*正向迭代
* Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}*/
/*//从指定的索引以后的元素进行迭代 Lambda迭代
ListIterator listIterator = list.listIterator(1);
//231 132
listIterator.forEachRemaining((e) -> System.out.println(e));*/
ListIterator iterator = list.listIterator();
while (iterator.hasNext()) {
String next = iterator.next();
System.out.println(next);
if ("132".equals(next)) {
iterator.add("新添加的");
}
}
System.out.println("反向迭代 +++++++++++++++++++++");
while (iterator.hasPrevious()) {
System.out.println(iterator.previous());
}
//[123, 231, 132, 新添加的]
System.out.println(list);
/*
* 123
231
132
反向迭代 +++++++++++++++++++++
新添加的
132
231
123
[123, 231, 132, 新添加的]
*/
}
}
ListIterator与Iterator相比,ListIterator是反向迭代,Iterator是正向迭代,而且ListIterator还可以通过add方法向List集合中添加元素,Iterator只能删除元素、
ArrayList和Vector实现类
ArrayList和Vector类都是基于数组实现的List类,所以ArrayList和Vector类分装了一个动态的,允许再分配的Object[]数组。ArrayList或Vector对象使用initialCapacity参数来设置该数组的长度,当向ArrayList或Vector中添加元素超出了该数组的长度时,他们的initialCapacity会自动增加。
ArrayList和Vector的区别是:ArrayList是线程不安全的,当多个线程访问同一个ArrayList集合时,如果有超过一个线程修改了ArrayList集合,则程序必须手动保证该集合的同步性,但Vector集合则是线程安全的,无须程序保证该集合的同步性。因此Vector是线程安全的,所以Vector的性能比ArrayList的性能要低;
Vector提供了Stack子类,它用于模拟栈这种数据结构,栈筒仓是指后进先出LIFO的容器,最后push进栈的元素,将最先被pop出栈,出入栈的都是Object,
如果程序需要使用栈这种数据结构,则可以考虑ArrayDeque。
ArrayDeque底层是基于数组实现的,因此性能很好。
public class ArrayListAndVector {
public static void main(String[] args) {
Stack vector = new Stack<>();
vector.push("1");
vector.push("2");
vector.push("3");
while (!vector.empty()) {
System.out.println(vector.pop());// 3 2 1
//System.out.println(vector.peek()); 会死循环,因为栈内不会弹出所以判断会一直执行。
}
}
}
固定长度的List
Arrays提供了asList(Object...a)方法,该方法可以把一个数组或指定个数的对象转化成一个List集合,这个List集合时Arrays的内部类ArrayList的实例。
Array.ArrayList是一个固定长度的List集合,程序只能遍历该集合里的元素,不可增加,删除该集合里的元素。
public class FixedSizeLists {
public static void main(String[] args) {
List asList = Arrays.asList(new String[]{"1","@","#","$"});
//Exception in thread "main" java.lang.UnsupportedOperationException
//System.out.println(asList.add("dsdsd"));
}
}
Queue集合
Queue用于模拟队列这种数据结构,队列通常是指“先进先出FIFO”的容器。队列的头部保存在队列中存放时间最长的元素,队列的尾部保存在队列中存放时间最短的元素。新元素插入offer到队列的尾部,访问元素poll操作会返回队列头部的元素。
Queue接口有一个接口Deque,Deque代表一个双端队列,双端队列可以同时从两端来添加元素,删除元素,因此Deque的实现类即可当成队列使用,也可当成栈使用。Java为Deque提供了ArrayDeque和LinkedList两个实现类。
public class QueueTest {
public static void main(String[] args) {
Queue queue = new ArrayDeque<>();
//将指定元素加入此队列的尾部,当使用有容量限制的队列时,此方法通常比add方法更好。
queue.offer(44);
queue.add(2);
//[44, 2]
System.out.println(queue);
queue.add(3);
//[44, 2, 3]
System.out.println(queue);
System.out.println(queue.poll());//44
System.out.println(queue);//[2, 3]
System.out.println(queue.peek());//2
System.out.println(queue);//[2, 3]
queue.remove();
System.out.println(queue);//[3]
queue.add(3434);
System.out.println(queue);//[3, 3434]
//返回队列头部元素,但是不删除该元素
System.out.println(queue.element());//3
System.out.println(queue);//[3, 3434]
System.out.println(queue.remove(3434));//true
System.out.println(queue);//[3]
}
}
PriorityQueue实现类 priority 优先的
PriorityQueue保存队列元素的元素并不是按加入队列的顺序,而是按队列元素的大小进行重新排序,因此当调用peek方法或者poll方法去除队列中的元素时,并不是取出最先进入队列的元素,而是取出队列中的最小的元素。PriorityQueue已经违反了队列的最基本的原则:先进先出
public class PriorityQueues {
public static void main(String[] args) {
PriorityQueue priorityQueue = new PriorityQueue<>();
priorityQueue.add(12);
priorityQueue.add(-6);
priorityQueue.add(-9);
priorityQueue.add(1);
//[-9, 1, -6, 12]
System.out.println(priorityQueue);
priorityQueue.poll();
//[-6, 1, 12]
System.out.println(priorityQueue);
}
}
PriorityQueue不允许插入null元素,PriorityQueue可以定制排序和自然排序。
PriorityQueue自然排序的元素必须实现Comparable接口,而且应该是同一个类的实例
PriorityQueue不要求队列元素实现Comparable接口。
Deque接口和ArrayDeque实现类
Deque接口是Queue接口的子接口,他代表一个双端队列。
ArrayList和ArrayDeque两个集合类的实现机制基本相同,他们的底层都是采用一个动态的可重新分配的Object[]数组来保存集合元素,当集合元素超出了该数组的容量时,系统会在底层重新分配一个Object[]数组来存储集合元素。
把ArrayDeque当成栈来使用
public class ArryDeque {
public static void main(String[] args) {
Deque deque = new ArrayDeque<>();
deque.push(1);
deque.push(2);
deque.push(3);
deque.push(4);
//[4, 3, 2, 1]
System.out.println(deque);
System.out.println(deque.pop()); // 4
System.out.println(deque);//[3, 2, 1]
}
} //后入先出
把ArrayDeque当成队列使用
public class ArryDeque2 {
public static void main(String[] args) {
Deque deque = new ArrayDeque<>();
deque.offer(1123);
deque.offer(143);
deque.offer(-11);
System.out.println(deque);//[1123, 143, -11]
System.out.println(deque.poll());//1123
System.out.println(deque);//[143, -11]
System.out.println(deque.poll());//143
System.out.println(deque);//[-11]
}
}
ArrayDque不仅可以作为栈使用,也可以作为队列使用。
LinkedList实现类
可以根据索引来随机访问集合中的元素,LinkedList还是实现了Deque接口,可以被当成双端队列来使用,因此既可以被当成栈使用,也可以当做为队列使用。
public class LinkedListTest {
public static void main(String[] args) {
LinkedList linkedList = new LinkedList<>();
//将数组元素加入栈顶
linkedList.push(1);
//将数组元素加入栈底
linkedList.offer(2);
//[1, 2]
System.out.println(linkedList);
//加入栈顶
linkedList.offerFirst(3);
//[3, 1, 2]
System.out.println(linkedList);
for (int i = 0; i < linkedList.size(); i++) {
System.out.println(linkedList.get(i)); //3 1 2
}
//访问但不删除栈底
System.out.println(linkedList.peekLast());//2
//[3, 1, 2]
System.out.println(linkedList);
//访问但不删除栈顶
System.out.println(linkedList.peekFirst());//3
//[3, 1, 2]
System.out.println(linkedList);
//访问并删除栈顶
System.out.println(linkedList.pollFirst());//3
//[1, 2]
System.out.println(linkedList);
}
}
LinkedList和ArrayList和ArrayDeque实现机制完全不同
ArrayList,ArrayDeque内部以数组的形式来保存集合中的元素,因此随机访问几个元素时具有较好的性能,而LinkedList内部以链表的形式来保存集合中的元素,因此随机访问集合元素时性能较差,但在插入,删除元素时性能比较出色,只需要改变指针所指的地址即可。
对于所有的内部基于数组的集合实现,例如ArrayList和ArrayDeque等,使用随机访问的性能比使用Iterator迭代访问的性能要好,因为随机访问会被映射成对数组元素的访问。
各种线性表的性能分析
Java提供的List就是一个线性表接口,而ArrayList,LinkedList又是线性表的两种典型实现:基于数组的线性表和基于链的线性表。Queue代表了队列,Deque代表了双端队列,既可以作为队列使用,又可以当做栈使用。
LinkedList集合不仅提供了List的功能,还提供了双端队列,栈的功能。
一般来说,由于数组以一块连续内存区来保存所有的数组元素,所以数组在随机访问时性能最好,所有的内部以数组作为底层实现的集合在随机访问时性能都比较好。而内部以链表作为底层实现的集合在执行插入,删除操作时有较好的性能。但总体来说,ArrayList的性能比LinkedList的性能要好,因此大部分时候都应该考虑使用ArrayList。
使用List集合的一些建议
如果需要遍历List集合,对于ArrayList,Vector集合,应该是用随机访问方法get来遍历集合元素,这样性能更好。对于LinkedList集合,则应该采用迭代器Iterator来遍历集合元素。
如果需要经常执行插入,删除操作来改变含大量数据的List集合的大小,则可考虑使用LinkedList集合,使用ArrayList,Vector集合可能需要经常分配内部数组的大小,效果可能较差。
如果有多个线程需要访问List集合中的元素,需要考虑使用Collections将几个包装成线程安全集合。