集合
1 概念:
保存一组元素的容器,里面提供了一系列的用于对里面的元素进行操作的方法(增删改查)
2 集合和数组的对比:
-
相同点:
都是一种容器,都是用于保存一组元素 -
不同点:
数组: 1、数组的长度必须提前指定,而且一旦指定不能更改 2、数组只能保存相同类型的元素 集合: 1、集合在使用时,长度不用指定,而且可以实现自动扩容或截断 2、集合没有指定泛型之前,默认保存的是任意类型的元素(Object类型) 指定泛型之后,可以保存对应类型 的元素
-
总结:
数组:比较适合保存 基本类型的元素
集合:比较适合保存引用类型的元素(对象)
示例代码:
1、使用数组--------------------
//创建一个Animal类型的数组,
Animal[] animals = new Animal[3];
//Animal类型的数组的空间存放一个Animal对象
animals[0] = new Animal();
animals[1] = new Animal();
animals[2] = new Animal();
//由上数组存储对象的方式,看起来较复杂
//扩容数组的方式
// 创建一个比原数组更大的数组
int n=1;
Animal[] newAni = new Animal[animals.length+n];
//复制数组
//添加新元素
animals=newAni;
2、使用集合--------------------
List list= new ArrayList();
//添加对象到集合中
list.add(new Animal());
集合的框架体系图 ★
1.Collection接口的特点和使用
Collection的特点:
单列集合
-
用于保存一组元素
-
里面没有提供直接的实现类,而是提供了子接口
-
子接口中有具体的实现类,(有的允许重复、不允许重复,有的有序、无序)
-
该接口中提供了一系列常见的集合操作的方法:增加、删除、查找
常见方法 ★
- add / addAll (增加单个元素/批量增加)
- remove / removeAll (删除指定的单个元素/~~)
- contains / containsAll (查找某元素是否存在/~~)
- clear (清除)
- size (获取实际元素的个数)
- isEmpty (判断集合是否为空)
- iterator (获取迭代器对象,用于遍历)
遍历方式★
迭代器工作特点:★
-
每次只能下移一位
-
只能下移,不能上移!
-
比较适合做读取操作,不适合做增删改操作
注意:★
- 使用迭代器过程,不适合做增删,容易报异常 ConCurrentModificationException
- 使用迭代器过程,可以做修改,但如果修改地址,没有效果!
- 使用迭代器过程,如果非要做删除,可以使用迭代器本身的remove方法!
- 如果更改的是地址,不影响集合原本的元素;
- 如果更改的是内容,可以影响集合原本的元素不可以添加!
方式1:使用迭代器
迭代器的使用步骤:
①获取迭代器对象,指针默认在最上方
②通过调用hasNext判断下一个是否有元素
③通过调用next下移一位并获取当前元素
public void test1(){
//3.遍历集合col
//①获取迭代器
Iterator iterator = col.iterator();
//②判断,如果返回true,则进行下一步
while(iterator.hasNext()){
//③下移一位,并获取对应元素
System.out.println(iterator.next());
}
方式二:为了简化Iterator的语法,jdk5.0出现了增强for-is
// 增强for的本质就是Iterator,只是语法简化了!
//语法:
for(元素类型 元素名:集合或数组名){
//访问元素即可
}
@Test
public void test2() {
//3.遍历集合
for(Object o: col){
System.out.println(o);
}
}
2 .List接口的特点和使用
List接口的特点
-
有序(插入和取出的顺序一致的),提供整数索引(从0开始)
-
允许重复
List接口的特有方法
- add(object)增 (这个是老方法,为了凑够增删改查插)
- remove(index)删 删除指定索引处的元素
- set(index,object)改 修改指定索引处的元素
- indexOf(object)查 获取元素的索引,如果找不到返回-1
- add(index,object)插 在列表的指定位置插入指定元素
- get(index)获取 获取指定索引处的元素
List接口的遍历方式
方式1:使用iterator
@Test
public void test1() {
//3.遍历
Iterator iterator = list.iterator();
while(iterator.hasNext()){
Object book = iterator.next();
System.out.println(book);
}
}
方式2:使用增强for (推荐)
@Test
public void test2() {
//3.遍历
for (Object object : list) {
System.out.println(object);
}
}
方式3:使用普通for
@Test
public void test3() {
for(int i=0;i<list.size();i++){
Object object = list.get(i);//
System.out.println(object);//或者是System.out.println(list.get(i));
}
}
2.1 (List接口的实现类:)ArrayList★
底层结构:可变数组
jdk8:ArrayList中维护了Object[] elementData,初始容量为0.
第一次添加时,将初始elementData的容量为10 再次添加时,如果容量足够,则不用扩容直接将新元素赋值到第一个空位上,如果容量不够,会扩容1.5倍
jdk7:ArrayList中维护了Object[] elementData,初始容量为10.
添加时,如果容量足够,则不用扩容直接将新元素赋值到第一个空位上 如果容量不够,会扩容1.5倍
jdk7和jdk8区别:
jdk7 相当于饿汉,创建对象时,则初始容量为10
jdk8 相当于懒汉式,创建对象时,并没有初始容量为10,而在添加时才去初始容量为10
2.2. (List接口的实现类):Vector
底层结构:可变数组,和ArrayList很像。 线程安全的
2.3. (List接口的实现类):LinkedList
底层结构:双向链表,(线程不安全)
LinkedList中维护了两个重要的属性 first和last,分别指向首节点和尾节点。
每个节点(Node类型)里面又维护了三个属性item、next、prev,分别指向当前元素、下一个、上一个元素。
最终实现手拉手的链表结构!
总结:实现类对比
一、ArrayList和Vector的对比
二、ArrayList和LinkedList的对比
结论:
1.如果考虑线程安全问题:Vector
2.不考虑线程安全问题:
查找较多:ArrayList
增删较多:LinkedList
3.Set接口的特点和使用
Set接口的特点
不允许重复,至多一个null
无序(插入和取出的顺序不一致),没有索引
Set接口的特有方法
没有特有方法,都是从Collection继承来的
Set接口的遍历方式
和Collection的遍历方式同
3.1 (Set接口的实现类):HashSet★
底层结构:
维护了一个HashMap对象,也就是和HashMap的底层一样,基于哈希表结构的
如何实现去重:
底层通过调用hashCode方法和equals方法实现去重
先调用hashCode,如果不相等,则直接可以添加。
如果hashCode相等,则继续判断equals。如果equals也不相等,则可以添加,否则返回false
应用:
通过HashSet添加元素时,如果认为内容相等的为重复元素,则需要重写该元素的hashCode和equals方法
3.2 Set接口的实现类:TreeSet★
特点:
不允许重复,里面不允许null
可以实现对里面元素进行排序 自然排序、 定制排序
底层结构:
底层维护了一个TreeMap,而TreeMap底层是红黑树结构,可以实现对元素进行排序
应用:
方式一:自然排序 要求:
必须让添加元素的类型实现Comparable接口,实现里面的compareTo方法//
方式二:定制排序 要求:
创建TreeSet对象时,传入一个Comparator接口的对象,并实现里面的compare方法
如何实现去重: 通过比较方法的返回值是否为0来判断是否重复
-补充java.lang.Comparable接口,用于比较对象。里面有compareTo(To)方法比较此对象与指定对象 的顺序。在String类中实现了compareTo方法,按字典顺序比较两个字符串,逐个比较大小, 不同时,前一个字符串减后一个,
LinkedHashSet
底层结构:同LinkedHashMap,都是基于哈希表+双向链表结构
特点:
1、不允许重复
2、插入和取出的顺序是一致的