集合容器
Array和ArrayList的区别是什么?
- Array是数组,ArrayList是类
- Array是定长的(需要手动扩容),ArrayList长度可变(使用过程中自动扩容)
- ArrayList的底层是Array
数组(Array)有什么缺点
- 再创建数组的时候,就需要定死长度,在运行过程中需要判断剩余长度,并动态扩容
- 在中间插入的时候,需要将后面的元素进行后移
- 在中间删除的时候,需要将后面的元素进行前移
数组的特点:有序的,不唯一的
数组的封装类 - 列表(ArrayList)
学习一个类的步骤:
1、构造方法(先研究如何构造对象)
ArrayList()
ArrayList(int)
2、成员变量(研究构造出来的对象中有什么)
elementData Object[ ]类型
size int类型
3、成员方法(研究如何操作对象中的成员)
add(int) 在数组尾部的第一个不为空的位置添加
add(int,E) 在数组中的指定位置插入
set(int,E) 修改数组指定位置
remove(int) 根据索引号移除
remove(Object) 根据指针移除,如果存在多个同样值,只会删除第一个
get(int) 根据指定的索引号找到某个元素并返回
size是记录容器中数组不为null的个数,私有静态方法
size()方法是查看容器中数组不为null的个数
补充上图:
我们不需要考虑数组的扩容问题
我们不需要遍历数组,找第一个不为NULL的位置(官方在原码中维护了一个size变量)
添加:
add()无参
add(index,)有参
删除:
remove(index)根据索引号进行删除
remove(Object)根据指针进行删除
修改:
set()
ArrayList类中的那个“E”到底是什么?
泛型 - 作者在编写的时候用 E 来表达未知的数据类型
- 调用者在使用的时候,可以给出 E 的具体类型,也可以不给出
- 如果给出E的具体类型,是在构造ArrayList对象的时候给出
例如:E = String E = Car
不给的话 E = Object
给出E的具体数据类型的语法
ArrayList<String>al1 = new ArrayList();
ArrayList<Car>al1 = new ArrayList();
用泛型来实现类中用到的数据类型(可以是参数类型,可以是返回值类型,可以是任意变量的类型,可以是向下转型的类型)
用泛型的好处是被调用方可以帮调用方写向下转型的代码
双向链表 - LinkedList类(底层是Node类)
1、构造方法
2、成员变量
3、成员方法
增:
addFirst(E) 在头部添加
addLast(E) 在尾部添加
add(E) 在尾部追加
add(int,E) 在指定位置插入
链表在中间插入的速度要优于数组,只需要找到插入的位置的节点,创建新节点,然后修改指针即可
数组在尾部插入比较快,但是在首部或者中间插入比较慢,因为需要后移
速度对比:
数组:遍历数组,指定位置后面的元素要后移
链表:遍历链表,指定的位置插入
删:
removeFirst(E)删除头部
removeLast(E)删除尾部
remove(int)根据指定位置删除
remove(Object)根据指定元素删除
改:
set(int,E)
查:
get(int)链表并没有索引的机制,底层通过遍历链表然后通过二分查找法找到指定位置的节点,并返回节点中的item
从查询角度看数组的性能要优于链表
中间插入和删除,链表优于数组
查询和修改,数组要优于链表
链表不需要扩容和转移,数组需要
LinkedList的内部类 - Node类(用于创建双向链表中的节点对象)
ArrayList和LinkedList的区别是什么?
1、底层数据结构的实现:ArrayList底层数据结构是动态数组,而LinkedList的底层数据结构是双向链表
2、随机访问(即读)效率:ArrayList比LinkedList在随机访问的时候效率要高,因为ArrayList底层是数组,可以通过索引号快速访问,LinkedList是通过二分查找遍历链表节点进行查找的
3、增加和删除效率:在非首尾的增加和删除操作,LinkedList要比ArrayList效率要高,因为ArrayList增删操作需要大量的前移或者后移,这个过程中涉及到大量的赋值操作比较耗费时间,LinkedList只需要修改节点对象的左右指针即可
4、内存空间占用:LinkedList比ArrayList更占内存,因为LinkedList的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素
5、综合来说,在需要频繁读取集合中的元素时,更推荐ArrayList,而在插入和删除操作较多时,更推荐使用LinkedList
遍历ArrayList和LinkedList的几种方式
1、普通for循环
2、增强型for循环
3、forEach循环
4、迭代器
import java.util.ArrayList;
import java.util.Iterator;
public class Demo04 {
public static void main(String[] args) {
ArrayList<String> al1 = new ArrayList();
al1.add("张飞");
al1.add("赵云");
al1.add("马超");
al1.add("关羽");
al1.add("黄忠");
System.out.println("*****遍历方式一:普通for循环*****");
for (int i = 0; i< al1.size();i++){
System.out.print(al1.get(i) + " ");
}
System.out.println("\n*****遍历方式二:增强型for循环*****");
//冒号左边是一个临时变量String item 这个临时变量的数据类型与al1的泛型必须保持一致
//每循环一次就会自动从al1中取出一个元素付给item
//1、不需要写循环四要素
//2、不需要手动执行al1.get(i)操作了
for (String item : al1){
System.out.print(item+" "); //不需要执行al1.get(i)
}
System.out.println("\n*****遍历方式三:forEach*****");
//用al1调用forEach函数
//forEach函数的参数是一个箭头函数
//箭头函数的语法为:(参数名) -> {}
//al1.forEach 循环存在 在forEach函数里面 forEach会遍历al1 取出每个值后会回调你的箭头函数,并把值传给箭头函数的参数
al1.forEach((item) -> {
System.out.print(item + " ");
});
System.out.println("\n*****遍历方式四:迭代器*****");
//调用iterator()方法,返回一个迭代器对象
//使用iterator类声明一个迭代器指针,迭代器的泛型必须和al1的泛型保持一致
Iterator<String> iterator = al1.iterator();
//迭代器的hasNext()方法可以判断是否还有下一个元素
while (iterator.hasNext()){
//迭代器next()方法可以取出下一个元素
String item = iterator.next();
System.out.print(item + " ");
}
}
}
整理ArrayList和LinkedList的常用方法
ArrayList和Vector的区别(带一下CopyOnWriteArrayList)
ArrayList是线程不安全的,Vector是线程安全的。ArrayList中的所有方法都没有加同步锁,Vector中所有方法都加了synchronized同步锁。官方在JDK1.5版本中又推出了一个CopyOnWriteArrayList,使用了Lock锁实现线程安全,然后弃用Vector,因为Lock锁的性能比synchronized锁的性能好。
在并发编程中,如果多个线程共享一个ArrayList,那么必须考虑线程安全的问题,可以自己在代码中对ArrayList操作代码加锁,或者直接用线程安全的CopyOnWriteArrayList类,在不考虑线程安全的环境下,用ArrayList性能更好,因为加锁和开锁是很消耗性能的