一,概述
1.集合体系结构:单例集合,双列集合
collection代表单列集合,每个元素(数据)只包含一个值;
Map代表双列集合,每个元素包含两个值(键值对)
2.Collection集合体系
(1)List系列集合:添加的元素是有序,可重复有,有索引
ArrayList,LinekdList:有序,可重复,有索引
(2)Set系列集合:添加的元素是无序,不重复,无索引
HashSet:无序,不重复,无索引
LinkedHashSet:有序,不重复,无索引
TreeSet:按照大小默认升序排序,不重复,无索引
二,Collection的常用方法
1.collection是单列集合的祖宗,它规定的方法是全部单列集合都会继承的
Collection<String> c=new ArrayList<>();
//1.public void add(E e);添加
c.add("java1");
c.add("java1");
c.add("java2");
c.add("java2");
c.add("java3");
System.out.println(c);//会自动调用toString方法打出的是内容[java1,java1,java2,java2,java3]
//2.public void clear():清空集合的元素
//c.clear();
System.out.println(c);//[]
//3.public boolean isEmpty():判断集合是否为空 是空返回true,反之
System.out.println(c.isEmpty());
//4.public int size():获取集合的大小
System.out.println(c.size());//5
//5.public boolean contains(Object obj):判断集合中是否包含某个元素
System.out.println(c.contains("java1"));//true
//6.public boolean remove(E e):删除某个元素,如果有多个重复元素默认删除最前面一个
System.out.println(c.remove("java1"));//true
System.out.println(c);//[java1,java2,java2,java3]
//7.public Object[] toArray():把集合换成数组
Object[] arr=c.toArray();
System.out.println(Arrays.toString(arr));//[java1,java2,java2,java3]
String[] arr2=c.toArray(new String[c.size()]);
System.out.println(Arrays.toString(arr2));//[java1,java2,java2,java3]
System.out.println("------------");
//把一个集合的全部数据倒入到另一个集合中去
Collection<String> c1=new ArrayList<>();
c1.add("java1");
c1.add("java2");
Collection<String> c2=new ArrayList<>();
c2.add("java3");
c2.add("java4");
c1.addAll(c2);//就是把c2集合里面的数据全部倒入c1集合中去(c1与c2数据类型相同)
System.out.println(c);//[java1,java2,java3,java4]
三,Collection的遍历方式
迭代器
1.概述:迭代器是用来遍历集合的专用方式(数组没有迭代器0,在Java中迭代器的代表是Iterator
2.Collection集合获取迭代器的方法
Iterator<E> iterator() 返回集合中的迭代器对象,该迭代器对象默认指向当前集合的第一个元素
3.Iterator迭代器中的常用方法
boolean hasNext() 询问当前位置是否有元素存在,存在返回true,不存在返回false
E next() 获取当前位置的元素,并同时将迭代器对象指向下一个元素处
Collection<String> c=new ArrayList<>();
c.add("赵敏");
c.add("小昭");
c.add("素素");
c.add("灭绝");
Iterator<String> it = c.iterator();
// System.out.println(it.next());
//次数不能超过会出现异常
//2.我们应该使用循环结合迭代遍历集合
while (it.hasNext()){
String ele=it.next();
System.out.println(ele);
}
增强for循环
格式:for(元素的数据类型:数组或者集合){}
1.增强for可以用来遍历集合或者数组
2.增强for遍历集合,本质就是迭代器遍历集合的简化写法
Collection<String> c=new ArrayList<>();
c.add("赵敏");
c.add("小昭");
c.add("素素");
c.add("灭绝");
System.out.println(c);
//使用增强for遍历集合或者数组
for(String ele:c){
System.out.println(ele);
}
String[] names={"迪丽热巴","古力娜扎","稀奇哈哈"};
for (String name:names){
System.out.println(name);
}
Lambda表达式
1.Lambda表达式遍历集合
得益于JDK8开始的新技术Lambda表达式,提供了一种更简单,更直接的方式来比遍历集合
2.需要使用Collection的如下方法来完成
default void forEach(Consumer<? super T> action) 结合Lambda遍历集合
Collection<String> c=new ArrayList<>();
c.add("赵敏");
c.add("小昭");
c.add("素素");
c.add("周若至");
System.out.println(c);
//default void forEach(Consumer<? super T> action):结合Lambda表达式遍历集合
//Consumer是个接口不能直接创建对象,用匿名内部类
c.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
// c.forEach(s->System.out.println(s));//简写
}
四,List集合
特点,特有方法
1.List系列集合特点:有序,重复,有索引
2.List集合的特有方法
list集合因为支持索引,所以多了很多与索引相关的方法,Collection的功能list也继承了
//1.创建一个ArrayList集合对象(有序,可重复,有索引)
List<String> List=new ArrayList<>();//LinkedList<>();
List.add("蜘蛛精");
List.add("至尊宝");
List.add("至尊宝");
List.add("牛夫人");
System.out.println(List);
//2.public void add(int index,E element):在某个索引位置插入元素
List.add(2,"紫霞仙子");
System.out.println(List);
//3.public E remove(int index):根据索引删除元素,返回被删除的元素
System.out.println(List.remove(2));//紫霞仙子
System.out.println(List);
//4.public E get(int index):返回集合中指定位置处的元素
System.out.println(List.get(3));
//5.public E set(int index,E element):修改索引位置处的元素,修改成功后,会返回原来的数据
System.out.println(List.set(3,"牛魔王"));//牛夫人
遍历方式
1.List集合支持的遍历方式
(1)for循环(因为List集合有索引)
(2)迭代器
(3)增加for循环
(4)Lambda表达式
List<String> list=new ArrayList<>();
list.add("糖宝宝");
list.add("蜘蛛精");
list.add("至尊宝");
//1.for循环
for (int i=0;i<list.size();i++){
String s= list.get(i);
System.out.println(s);
}
//2.迭代器
Iterator<String> it= list.listIterator();
while(it.hasNext()){
System.out.println(it.next());
}
//3.增强for循环(foreach遍历)
for (String s : list) {
System.out.println(s);
}
//4.JDK 1.8开始之后的Lambda表达式
list.forEach(s->{
System.out.println(s);
});
ArrayList集合的底层原理
1.基于数组实现的
2.查询速度快(注意:是根据索引查询数据快):查询数据通过地址值和索引定位,查询任意数据耗时相同
3.删除效率低:可能更需要把后面很多的数据进行前移
4.添加效率极低:可能需要把后面很多的数据后移,再添加元素;或者也可能需要进行数组的扩容
查询快,增删慢
5.(1)利用无参构造器创建的集合,会在底层创建一个默认长度为0的数组
(2)添加第一个元素时,底层会创建一个新的长度为10的数组
(3)存满时,会扩容1.5倍
(4)如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准
LinkedList集合的底层逻辑
基于双链表实现的
1.什么是链表?特点?
链表中的结点是独立的对象,在内存中是不连续的,每个结点包含数据值和下一个结点的地址
值 下一个结点的地址 ----------->
值 下一个结点的地址
(1)链表的特点一:查询慢,无论查询哪个数据都要从头开始找
(2)链表的特点二:链表的增删相对快
2.单向列表,双向列表
双向列表特点:查询慢,增删相对比较快,但对首尾元素进行增删改查速度是极快的
3.LinkedList新增了很多首尾操作的方法
public void addFirst(E e) 在该列表开头插入指定的元素
public void addLast(E e) 将指定的元素追加到此列表的末尾
public E getFirst() 返回此列表中的第一个元素
public E getLast() 返回此列表中的最后一个元素
public E removeFirst() 从此列表中删除并返回第一个元素
public E removeLast() 从此列表中删除并返回最后一个元素
4.应用场景
//1.创建一个队列
LinkedList<String> queue=new LinkedList<>();
//入列
queue.addLast("第一号人");
queue.addLast("第二号人");
queue.addLast("第三号人");
queue.addLast("第四号人");
//出列
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue);
System.out.println("-----------------------");
//2.创建一个栈对象
//后进先出,先进后出
LinkedList<String> stack=new LinkedList<>();
//压线(push)
stack.addFirst("第一个子弹");
stack.addFirst("第二个子弹");
stack.addFirst("第三个子弹");
stack.push("第四个子弹");//push与addFirst一样
//出栈(pop)
System.out.println(stack.removeFirst());
System.out.println(stack.pop());
System.out.println(stack);
五,Set集合
特点
1.Set系列集合特点:无序:添加数据的顺序和获取出的数据顺序不一致;不重复,无索引;
(1)HashSet:无序,不重复,无索引
(2)LinkedHashSet:有序,不重复,无索引
(3)TreeSet:排序,不重复,无索引
注意:Set要用到的常用方法,基本上就是Collection提供的,自己几乎没有额外一些常用功能
HashSet集合的底层原理
哈希值
(1)就是一个int类型的数据,Java中每个对象都有一个哈希值
(2)Java中所有对象,都可以调用Object类提供的hashCode方法,返回该对象自己的哈希值public int hashCode():返回对象的哈希值
哈希值的特点
(1)同一个对象多次调用hashCode()方法返回的哈希值是相同的
(2)不同的对象,它们的哈希值一般不同,但也可能相同(哈希碰撞)
1.HashSet集合的底层原理
(1)基于哈希表实现
(2)哈希表是一种增删改查数据,性能都比较好的数据结构
2.哈希表
(1)JDK8之前,哈希表=数组+链表
创建一个默认长度为16的数组,默认加载因子为0.75,数组名table
使用元素的哈希值对数组的长度求余计算出存入的位置
判断当前位置是否为null,如果是null直接存入
如果不为null,表示有元素,则调用equals方法比较,相等,则不存
(JDK8之前,新元素存入数组,占老元素位置,老元素挂下面;JDK8开始,新元素直接挂在老元素下面)
如果数组快占满了,链表会过长,导致查询性能降低(扩容)
(2)JDK8开始,哈希表=数组+链表+红黑树
红黑树进一步提高了操作数据的性能
结论:如果希望Set集合认为2个内容一样的对象是重复的,必须重写对象的Hash Code()和equals()方法
LinkedHashSet集合的底层原理
1.原理
(1)依然是基于哈希表(数组,链表,红黑树)实现的
(2)但是,它的每个元素都额外的多了一个双链表记录它前后元素的位置
TreeSet
1.特点:不重复,无索引,可排序(默认升序排序,按照元素的大小,由小到大排序)
2。基层是基于红黑树实现的排序
注意:
(1)对于数组类型:Integer,Double,默认按照数据本身的大小进行升序排序
(2)对于字符串类型:默认按照首字符的编号升序排序
(3)对于自定义类型如Student对象,TreeSet默认是无法直接排序的
方法一:让自定义的类实现Comparable接口,重写方法
方法二:通过调用TreeSet集合的有参数构造器,可以设置Comparable对象(比较对象)
六,集合的并发修改异常
1.集合的并发修改异常
(1)使用迭代器遍历集合时,有同事在删除集合中的数据,程序就会出现并发修改异常的错误
(2)由于增强for循环遍历集合就是迭代器集合的简化写法,因此,使用增加for循环遍历集合,又在删除集合中的数据时,程序也会出现并发修改异常的错误
2.怎么保证遍历集合同时删除时不出现bug?
(1)使用迭代器遍历集合,但是迭代器自己的删除方法删除数据即可
(2)如果能用for循环遍历时,可以倒着遍历并删除,或者从前往后遍历,但删除元素后做i--操作