Arrays.asList()
java
复制代码
关于:Collection coll1 = Arrays.asList(123, 4567);
1、
Arrays.asList()方法属于"Arrays类",返回的是一个"java.util.Arrays.ArrayList"(这是一个 AbstractList的私有静态内部类,不是"java.util.ArrayList")
所以,使用 Arrays.asList(123, 4567) 时,需要导入 "import java.util.Arrays"
Arrays.asList(),返回的是AbstractList的一个实例,
这里发生了向上转型,即一个更具体的类(AbstractList)的实例被赋值给了一个更一般的接口(Collection)类型的变量。
2、
Arrays.asList(123, 4567),详解如下:
将一组元素,转换为,一个固定大小的列表(List)的静态方法,
需要注意的是,返回的列表(List)大小是固定的,不能添加或删除元素,
如果,你尝试添加、删除,将会抛出"UnsupportedOperationException"。
另,
如果你需要一个可以修改的列表,
应该使用 new ArrayList<>(Arrays.asList(123, 4567)) 来创建一个新的 ArrayList 实例,这样就可以修改它的内容了。
3、代码示例
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public static void main(String[] args) {
// 创建一个不可修改的固定大小列表
List<Integer> fixedList = Arrays.asList(123, 4567);
// 使用固定大小列表创建一个可修改的ArrayList
List<Integer> modifiableList = new ArrayList<>(fixedList);
System.out.println("Fixed List= " + fixedList); // Fixed List= [123, 4567]
System.out.println("Modifiable List= " + modifiableList); // Modifiable List= [123, 4567]
// 向可修改的ArrayList中添加一个新元素
modifiableList.add(8910);
System.out.println("Modifiable= " + modifiableList); // Modifiable= [123, 4567, 8910]
// 尝试向固定大小的列表添加元素,这将抛出 UnsupportedOperationException 的异常
fixedList.add(9876); // Exception in thread "main" java.lang.UnsupportedOperationException
}
17、集合框架
java
复制代码
1、"集合的概述":集合、数组都是对多个数据进行存储操作的结构,简称Java容器。
java集合类,可以用与存储数量不等的多个对象,还可用于保存具有映射关系的关联数组。
2、集合可分为"Collection" 和 "Map"两种体系,如下,
a、Collection接口:单例数据,用来存随一个一个的对象,定义了存取一组对象的方法集合
List:元素有序,可重复的集合
Set:元素无序,不可重复的集合
b、Map接口,双列数据,用来存随一对一对的,保存具有映射关系"key-value对"的集合
Collection接口
java
复制代码
Collection接口:集合框架的顶级接口,所有接口都是从"Collection接口"继承过来的,
是"Set 和 List的父接口",但"不是Map的父接口"。
常用方法-1
java
复制代码
1、add(Object e):将元素e添加到集合中
2、size():获取添加元素的个数
3、addAll(Collection coll):将coll集合中的元素添加到当前的集合中
4、clear():清空集合元素
5、isEmpty():判断当前集合是否为空
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
public static void main(String[] args) {
Collection coll = new ArrayList();
// add(Object e):将元素e添加到集合中
coll.add("AA");
coll.add("BB");
coll.add(123); // 自动装箱
coll.add(new Date());
System.out.println("coll= " + coll); // [AA, BB, 123, Wed Feb 28 15:15:51 CST 2024]
// size():获取添加元素的个数
System.out.println(coll.size()); // 4
Collection coll2 = new ArrayList();
coll2.add(456);
coll2.add("CC");
System.out.println("coll2= " + coll2); // coll2= [456, CC]
// addAll(Collection coll):将coll集合中的元素添加到当前的集合中
coll.addAll(coll2);
System.out.println("coll= " + coll); // coll= [AA, BB, 123, Wed Feb 28 15:17:44 CST 2024, 456, CC]
System.out.println(coll.isEmpty()); // false
// clear():清空集合元素
coll.clear();
System.out.println("coll= " + coll); // []
// isEmpty():判断当前集合是否为空
System.out.println(coll.isEmpty()); // true
}
常用方法-2
java
复制代码
1、contains(Object obj):判断当前集合中是否包含obj
2、containsAll(Collection coll):判断形参coll中的所有元素,是否都存在于当前集合中
3、remove(Object obj):从当前集合中移除obj元素
4、removeAll(Collection coll):差集,从当前集合中移除coll中所有的元素
5、retainAll(Collection coll):交集,获取当前集合和coll集合的交集,并返回给当前集合
6、equals(Object obj):要想返回true,需要当前集合和形参集合的元素都相同
7、hashCode():返回当前对象的哈希值
8、集合 ---> 数组:toArray()
"注意":向Collection接口的实现类的对象中,添加数据obj时,要求obj所在类要重写equals()
java
复制代码
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(789);
coll.add(new String("Tom"));
System.out.println(coll); // [123, 456, 789, Tom]
// ---------- contains(Object obj):判断当前集合中是否包含obj ----------
// 我们在判断时,会调用obj对象所在类的equals()
boolean contains1 = coll.contains(123);
boolean contains2 = coll.contains(1234);
System.out.println(contains1); // true
System.out.println(contains2); // false
// ---------- containsAll(Collection coll):判断形参coll中的所有元素是否都存在于当前集合中 ----------
Collection coll2 = Arrays.asList(123,456);
System.out.println(coll.containsAll(coll2)); // true
Collection coll3 = Arrays.asList(123,4567);
System.out.println(coll.containsAll(coll3)); // false
// ---------- remove(Object obj):从当前集合中移除obj元素 ----------
coll.remove(123);
System.out.println(coll); // [456, 789, Tom]
// ---------- removeAll(Collection coll):差集,从当前集合中移除coll中所有的元素 ----------
Collection coll1 = Arrays.asList(123,456);
coll.removeAll(coll1);
System.out.println(coll); // [789, Tom]
}
java
复制代码
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(789);
coll.add(new String("Tom"));
System.out.println(coll); // [123, 456, 789, Tom]
// ---------- retainAll(Collection coll):交集,获取当前集合和coll集合的交集,并返回给当前集合 ----------
Collection coll2 = Arrays.asList(123,456,789);
coll.retainAll(coll2);
System.out.println(coll); // [123, 456, 789]
// ---------- equals(Object obj):要想返回true,需要当前集合和形参集合的元素都相同 ----------
System.out.println(coll.equals(coll2)); // true
coll.add(000);
System.out.println(coll.equals(coll2)); // false
}
java
复制代码
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(789);
coll.add(new String("Tom"));
System.out.println(coll); // [123, 456, 789, Tom]
// ---------- hashCode():返回当前对象的哈希值 ----------
System.out.println(coll.hashCode()); // 5134763
// ---------- 集合 ---> 数组:toArray() ----------
Object[] arr = coll.toArray();
System.out.println(Arrays.toString(arr)); // [123, 456, 789, Tom]
for (int i = 0; i < arr.length; i++) {
if(i != arr.length-1){
System.out.print(arr[i] + "-");
} else {
System.out.print(arr[i]);
}
}
// 这个for循环,最终打印的是:123-456-789-Tom
// ---------- 拓展:数组 -> 集合:调用Arrays类的静态方法asList() ----------
List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});
System.out.println(list); // [AA, BB, CC]
List arr1 = Arrays.asList(new int[]{123, 456});
System.out.println(arr1.size()); // 1
List arr2 = Arrays.asList(new Integer[]{123, 456});
System.out.println(arr2.size()); // 2
}
迭代器接口
js
复制代码
GOP给迭代器模式定义为:
提供一种方法,访问容器对象中各个元素,而又不需暴露该对象内部细节,迭代器模式,就是为容器而生
用于遍历Collection集合中的元素,"Collection接口继承了java.long.Iterable接口",该接口有一个 iterator()方法,
那么,所有实现了Collection接口的集合类,都有一个 iterator()方法。
java
复制代码
/*
集合元素的遍历操作,使用迭代器Iterator接口,
1、集合对象(Collection对象),每次调用 iterator() 方法,都得到一个全新的迭代器对象
2、hasNext() 和 next() 方法
hasNext()方法,用于检查集合中是否还有下一个元素。
如果,迭代器指向的集合中还有元素,则此方法返回 true;否则返回 false。
next()方法,用于返回迭代器指向的下一个元素,并将迭代器向前移动一位。
如果,迭代器已经指向集合的末尾(即没有下一个元素),则调用此方法会抛出 NoSuchElementException。
3、默认游标,都在集合的第一个元素之前:
当你首次获取一个迭代器时,它的"游标"或"位置",位于集合的第一个元素之前,
调用 next() 方法,会使游标移动到第一个元素,
随后的 next() 调用,会继续移动游标到下一个元素。
4、内部定义了 remove() 方法
Iterator接口中的remove()方法,允许,在迭代过程中,安全地删除集合中的当前元素(即上一个被 next() 方法返回的元素),
这是 Iterator 特有的,不同于集合直接调用 remove() 方法,因为集合的 remove() 方法需要知道要删除的确切元素。
在迭代过程中使用Iterator.remove(),可以避免,在遍历过程中由于集合结构的变化(如直接调用remove()方法)而引发的ConcurrentModificationException。
*/
java
复制代码
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public static void main(String[] args) {
coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(789);
coll.add(new String("Tom"));
Iterator iterator = coll.iterator();
// hasNext():判断是否还有下一个元素
while (iterator.hasNext()) {
// next():1、指针下移,2、将下移以后集合位置上的元素返回
System.out.println(iterator.next());
}
// 错误方式一:
Iterator iterator = coll.iterator();
while((iterator.next()) != null){
System.out.println(iterator.next());
}
// 错误方式二:
/*
每次调用coll.iterator().hasNext() 和 coll.iterator().next()都会创建一个新的迭代器,
而不是使用已经存在的iterator变量,
因为,每次调用coll.iterator()都会返回一个新的迭代器,并且迭代器是独立的,它们不会互相影响,因此循环永远不会结束。
*/
// 这个 while 会死循环
while (coll.iterator().hasNext()){
System.out.println(coll.iterator().next());
}
}
java
复制代码
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add("Tom");
coll.add(789);
Iterator iterator = coll.iterator();
// hasNext():判断是否还有下一个元素
while (iterator.hasNext()) {
Object obj = iterator.next();
if ("Tom".equals(obj)) {
// 使用迭代器的remove()方法,删除当前元素
iterator.remove();
System.out.println("Removed: " + obj);
} else {
System.out.println("Kept: " + obj);
}
}
}
/*
打印的是,
Kept: 123
Kept: 456
Removed: Tom
Kept: 789
*/
Collection - List
java
复制代码
List接口的实现类有:ArrayList LinkedList Vector
java
复制代码
1、List 接口的常用方法:
void add(int index, Object ele):在index位置插入ele元素
boolean addAll(int index, Collection eles):从index位置开始,将eles中的所有元素添加进来
Object get(int index):获取指定index位置的元素
int indexOf(Object obj):返回obj在集合中首次出现的位置,如果不存在 返回-1
int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
Object remove(int index):移除指定index位置的元素,并返回此元素
Object set(int index, Object ele):设置指定index位置的元素为ele
List subList(int fromIndex, int toIndex):返回,从fromIndex到toIndex位置的子集合
2、总结:常用方法
增:add(Object obj)
删:remove(int index) / remove(Object obj)
改:set(int index, Object ele)
查:get(int index)
插:add(int index, Object ele)
长度:size()
遍历:Iterator迭代器方式
增强for循环
普通的循环
java
复制代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
list.add(456);
System.out.println("list=" + list); // list=[123, 456, AA, 456]
// void add(int index, Object ele):在index位置插入ele元素 -----------------------------------
list.add(1, "BB");
System.out.println(list); // [123, BB, 456, AA, 456]
// boolean addAll(int index, Collection eles):从index位置开始,将eles中的所有元素添加进来 -----------------------------------
List<Integer> list1 = Arrays.asList(1, 2, 3);
list.addAll(list1);
System.out.println(list); // [123, BB, 456, AA, 456, 1, 2, 3]
// Object get(int index):获取指定index位置的元素 -----------------------------------
System.out.println(list.get(1)); // BB
// int indexOf(Object obj):返回obj在集合中首次出现的位置 -----------------------------------
System.out.println(list.indexOf(456)); // 2
System.out.println(list.indexOf(456789)); // -1
// int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置 -----------------------------------
System.out.println(list.lastIndexOf(456)); // 4
// Object remove(int index):移除指定index位置的元素,并返回此元素 -----------------------------------
Object o = list.remove(0);
System.out.println(o); // 123
System.out.println(list); // [BB, 456, AA, 456, 1, 2, 3]
// Object set(int index, Object ele):设置指定index位置的元素为ele -----------------------------------
list.set(1, "CC");
System.out.println(list); // [BB, CC, AA, 456, 1, 2, 3]
// List subList(int fromIndex, int toIndex):返回,从[fromIndex, toIndex)位置的子集合 -----------------------------------
List list2 = list.subList(0, 3);
System.out.println(list2); // [BB, CC, AA]
// 遍历List -----------------------------------
// 遍历方式 - 1、迭代器
Iterator iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
// 遍历方式 - 2、增强for
for (Object obj: list){
System.out.println(obj);
}
// 遍历方式 - 2、普通for
for (int i = 0;i < list.size();i++){
System.out.println(list.get(i));
}
}
ArrayList
java
复制代码
1、
内部存储用的数据结构,是用**数组(动态调整大小)**实现,默认初始容量为10。每次扩容大小是增加50%(在java8版本以及之后的版本,java6使用的是1.5倍)。
2、
优点:
使用数组实现,因此,内部元素可以通过索引实现快速随机访问。
缺点:
a. 从ArrayList中间位置插入和删除元素,都需要循环移动其他元素元素的位置。
b. 数组空间不够需要扩容时,会开辟一个新的数组,把旧的数组元素拷贝过去,比较耗性能。
c. 线程不安全。
3、扩容
java8及之后,扩容计算方法:
int newCapacity = oldCapacity + (oldCapacity >> 1);
这意味着,在原来数组大小的基础上,扩大50%作为新数组容量的大小。
java6的计算方法:
int newCapacity = (oldCapacity * 3)/2 + 1;
这意味着,在原来数组大小的基础上,扩大1.5倍作为新数组容量的大小。
4、
"总之,ArrayList基于数组实现 查改快,增删慢,线程不安全"
"在需要做一次性插入 和 多次查询业务时,可以使用此集合,但是,ArrayList不保证线程安全,只能在单线程时候做使用"
源码解析
java
复制代码
"源码":
class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
js
复制代码
"AbstractList<E>":是List接口第一抽象类
"RandomAccess":RandomAccess接口是一个标志接口(Marker)它支持快速随机访问
"Cloneable":支持克隆
"java.io.Serializable":RandomAccess接口也是是一个标志接口(Marker) 它支持序列化和反序列化
java
复制代码
"属性":
private static final long serialVersionUID = 8683452581122892189L; // 序列化编号
private static final int DEFAULT_CAPACITY = 10; // 使用无参构造器时,第一次扩容的数组默认大小10
private static final Object[] EMPTY_ELEMENTDATA = {}; // 用于空实例的共享空数组实例
// 用于,默认大小的空实例的共享空数组实例。
// 我们将其与 EMPTY_ELEMENTDATA 区分开来,以了解添加第一个元素时扩容多少。
// MARK:无参构造函数,使用该数组初始化,与 EMPTY_ELEMENTDATA 的区别主要是区分作用,用来减少空数组的存在,优化内存使用 1.8后的优化
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData; // 底层的数据结构Object类型的数组
private int size; // 记录数组中元素的个数
"无参构造器 - 构造一个初始容量为10的一个数组":
public ArrayList() {
// private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/*
这里其实是赋值了一个共享的空数组,
数组在第一次添加元素时,会判断 elementData 是否等于 DEFAULTCAPACITY_EMPTY_ELEMENTDATA ,
假如等于,则会初始化容量为 DEFAULT_CAPACITY 也就是10
*/
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
"带参构造器 - 1":
int initialCapacity为ArrayList 底层数组初始的长度,构造一个指定长度的数组
public ArrayList(int initialCapacity) {
// 参数合法性检验
if (initialCapacity > 0) {
// 创建一个长度为 initialCapacity 的数
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 将原有的空数组EMPTY_ELEMENTDATA赋给底层数组elementData
this.elementData = EMPTY_ELEMENTDATA;
} else {
// 参数不合法抛出IllegalArgumentException异常
throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
}
}
"带参构造器 - 2"
"Collection<? extends E> c":c是泛型E的子类,构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray(); // 将c集合中的数据拷贝到a数组中
if ((size = a.length) != 0) { // 判空并给size赋值
if (c.getClass() == ArrayList.class) { // 如果c是ArrayList类的对象之间将a数组地址赋值给elementData数组
elementData = a;
} else {
// 进行拷贝
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// 如果为c集合为空,则将原有的空数组EMPTY_ELEMENTDATA赋给底层数组elementData
elementData = EMPTY_ELEMENTDATA;
}
}
"扩容机制"
private Object[] grow() { // 数组容量不足时,直接调用的扩容方法,返回一个Object数组
return grow(size + 1);
}
private Object[] grow(int minCapacity) { // 数组拷贝的实现,newCapacity(minCapacity) 为实际的扩容大小
return elementData = Arrays.copyOf(elementData, newCapacity(minCapacity));
}
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;//原来数组的长度
int newCapacity = oldCapacity + (oldCapacity >> 1); //新数组的长度,在原来数组的长度上加原来数组的二分之一
if (newCapacity - minCapacity <= 0) { //如果新数组的长度小于等于size+1 比如原来数组长度为0或者1,那么新数组长度也为0或者1无法体现扩容,则需要重新处理:处理如下
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// 判断,现在的数组,是否为无参构造器创建的数组,如果是,就返回DEFAULT_CAPACITY,即无参构造器第一次扩容为10
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // 参数无效抛出异常
throw new OutOfMemoryError();
return minCapacity; //扩容+1;
}
// MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
return (newCapacity - MAX_ARRAY_SIZE <= 0) // 如果,新数组长度,大于最大扩容边界需要做特殊处理
? newCapacity
: hugeCapacity(minCapacity); // 特殊处理
}
private static int hugeCapacity(int minCapacity) {
// 参数合法性检验
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) // 如果,size+1大于最大扩容边界,则扩容到 Integer.MAX_VALUE(2 的 31 次方 - 1),否则扩容到最大扩容边界
? Integer.MAX_VALUE
: MAX_ARRAY_SIZE;
}
Vector
java
复制代码
1、
基于 数组(动态调整大小) 数据结构实现,初始容量是10。
2、
优点:线程安全。
缺点:效率低,增加元素、删除元素、查找元素都很慢。
LinkList
java
复制代码
1、
内部存储用的数据结构,是用双向链表实现。
2、
优点:
使用链表实现,适合动态的插入和删除。
缺点:
a. 随机访问元素的速度相对较慢。
b. 基于链表数据结构的实现,占用的内存空间比较大(除了保存数据本身,还要保存指针信息)。
"总之",
LinkedList,底层通过双向链表的形式实现,增删快,遍历和查询慢,线程不安全。
LinkedList,底层使用双向链表的形式存储数据,不用向ArrayList存在地址浪费,并且在增删时效率高,我们可以在需要经常增删时使用此集合来提高我们的效率。
java
复制代码
"源码":
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
与 ArrayList 不同的是,
LinkedList 继承的是 AbstractSequentialList,
但,AbstractSequentialList 也是继承自 AbstractList,
其他和 ArrayList 相同
java
复制代码
"属性"
transient int size = 0; // 记录数据多少
transient Node<E> first; // 双向链表的头结点
transient Node<E> last; // 双向链表的尾结点
"构造方法"
public LinkedList() {} // LinkedList的构造方法,是一个空方法,此时指针变量first和last的初始值都为null。
public LinkedList(Collection<? extends E> c) { // 拷贝的构造器
this();
addAll(c); // 将所有c中的数据拷贝过来
}
"链表节点静态类,储存的数据的实体Node"
private static class Node<E> {
E item; // 需要储存的数据
Node<E> next; // 后继,连接下一个数据,如果它是最后一个,则为null
Node<E> prev; // 前驱,连接上一个数据,如果它是第一个,则为null
Node(Node<E> prev, E element, Node<E> next) { // Node的构造器
this.item = element;
this.next = next;
this.prev = prev;
}
// 明显看出,这是一个双向链表节点,item是用来存放节点值,next是尾指针指向下一个节点,prev是头指针指向前一个节点。
}