集合
- 集合的概念
集合是java中提供的一种容器,可以用来存储多个数据
- 集合和数组的区别
(1)数组的长度是固定的,集合的长度是可变的
(2)数组中可以存储基本数据类型的值,也可以存储对象;而集合中只能存储对象 - 集合分类
集合分为单列集合(即单元素集合)和双列集合(即键值对集合)
若一次只存一个元素,则使用的就是单列集合(即单元素集合),它的顶层接口是Collection集合
若一次存一对元素,则使用的就是双列集合(即键值对集合),它的顶层接口是Map集合
- 集合体系结构
- 单列集合
Collection接口有两个子接口:List集合(List接口)和Set集合(Set接口)
其中List集合可以出现重复元素,而Set集合不可以出现重复元素
List集合有两个实现类:ArrayList、LinkedList
Set集合有两个实现类:HashSet、TreeSet
List集合和Set集合都是接口,平时所说的List集合和Set集合其实说的是它们的实现类
在创建List集合和Set集合的对象时创建的是它们的实现类 - 双列集合
即键值对集合:每个元素由键和值两部分组成
Map接口有两个实现类,即:HashMap和TreeMap
Collection集合
-
解释
(1)是单列集合(单元素集合的顶层接口),它表示一组对象,这些对象也称为Collection的元素
(2)JDK不提供该接口的任何直接实现,它提供更具体的子接口(如List和Set)实现 -
怎么创建Collection集合的对象
(1)以多态的方式创建
(2)具体的实现类ArrayList -
注意
父接口包含所有子接口的特点,父接口的方法在子接口中均有
Collection集合的常用方法
- 注意
(1)其子接口继承Collection集合的所有方法
(2)Collection接口有时允许元素重复有时不允许,具体要看该接口的实现类(List接口的实现类允许重复,Set接口的实现类不允许重复) - 添加元素
方法 | 解释 |
---|---|
add(E e) | 添加元素对象到当前集合中,且一次只能添加一个元素对象 |
addAll(Collection<?extends E> c) | 将指定集合c中的所有元素对象到当前集合中(即将参数集合中的所有元素都添加进当前集合中) |
- 删除元素
方法 | 解释 |
---|---|
boolean remove(Object o) | 将参数从集合中移除,若参数在集合中有重复元素,则只会移除第一个重复元素 |
boolean removeAll(Collection<?> c) | 在原集合中移除参数集合中的所有元素(重复元素均移除) |
boolean removelf(Predicate<? super E> filter) | 删除满足给定条件(谓词即filter)的此集合中的所有元素(JDK1.8开始有了,JDK1.8之前用的是Iterator迭代器中的remove()方法) filter :一个谓词(Predicate)函数,它确定哪些元素应该被删除。谓词函数接受一个参数,通常是集合中的元素,然后返回一个布尔值,表示是否应该删除该元素。示例如下: |
boolean retainAll(Collection<?> c) | 保留当前集合中与指定集合(coll)中的元素相同的元素,而删除其他元素 |
void clear() | 清空集合 |
boolean removelf(Predicate<? super E> filter)
方法示例
- 方式一
package at.guigu.jihe.collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Predicate;
public class TestTwo {
public static void main(String[] args) {
Collection<String> col = new ArrayList<>();
col.add("aaa");//将元素添加进集合且一次只能添加一个元素
col.add("bbb");
col.add("ccc");
col.add("dddd");
//Lambda表达式是为了重写接口Predicate中的抽象方法:boolean test(T t)
Predicate<String> isEven = s -> s.length() == 3;
col.removeIf(isEven);
System.out.println(col);
}
}
/*
boolean removeIf(Predicate<? super E> filter)------在原集合中移根据给定的条件(谓词即filter)删除集合中的元素
*/
- 方式二
package at.heima.test2;
import java.util.ArrayList;
import java.util.Collection;
public class TestTwo {
public static void main(String[] args) {
Collection<String> col = new ArrayList();
col.add("aaa");//将元素添加进集合且一次只能添加一个元素
col.add("bbb");
col.add("ccc");
col.add("dddd");
//Lambda表达式
//removeIf底层原理:removeIf底层会遍历集合,得到集合中的每一个元素
//s依次表示集合中的每一个元素,然后每一个元素都会到Lambda表达式中判断一下长度是否是3,若返回的是ture则删除;反之则不删除
//Lambda表达式是为了重写接口Predicate中的抽象方法:boolean test(T t)
col.removeIf(
(String s) -> {
return s.length() == 3 ;
}
);
System.out.println(col);
}
}
注意:
removeIf()方法写Lambda表达式的原因:ctrl右键
removeIf
看removeIf
方法的源码,发现其参数是引用数据类型(如下图1所示),然后ctrl右键Predicate
查看Predicate
源码发现Predicate类中只有一个抽象方法boolean test(T t)
且其参数类型是引用数据类型(如下图2所示)并且返回类型是boolean型,所以你需要按例子所示方式来写Lambda表达式
注意:
可以传递以下类型的对象给removeIf方法:
(1)Lambda表达式: 这是最常见的用法,你可以直接编写一个Lambda表达式作为谓词函数,如上面示例中的isEven。
(2)方法引用: 你可以引用一个已经存在的方法作为谓词函数。例如,Integer::isEven是一个引用了Integer类中的isEven方法的方法引用。
(3)自定义的Predicate实现类: 你也可以创建一个实现了java.util.function.Predicate接口的自定义类的实例,然后将其传递给removeIf方法。
无论使用哪种方式,重要的是谓词函数必须符合Predicate接口的要求,即接受一个参数并返回一个布尔值。这种灵活性允许你以不同的方式指定要在removeIf中使用的条件。
- 查询与获取元素
方法 | 解释 |
---|---|
boolean isEmpty() | 判断当前集合是否为空集合 |
boolean contains(Object o) | 判断当前集合中是否存在一个与obj对象equals返回true的元素(判断集合中是否包含参数元素) |
boolean containsAll(Collection<?> c) | 判断c(参数)集合中的元素是否在当前集合中都存在,即c集合是否是当前集合的”子集“ |
int size() | 获取当前集合中实际存储的元素个数 |
package at.guigu.test1;
import java.util.ArrayList;
import java.util.Collection;
public class TestOne {
public static void main(String[] args) {
//Colection接口的常用方法
Collection col = new ArrayList();//父类接口的引用指向子类接口实现类的对象
Collection colOne = new ArrayList();
Collection col1 = new ArrayList();
col1.add("Jerry");
col1.add("Mary");
colOne.add("小明");
colOne.add("小强");
System.out.println(col.size());//返回当前集合中的元素数
col.add("Tom");//将元素添加进集合且一次只能添加一个元素
col.add("Tom");
col.add("Tom");
col.add("Jerry");
col.add("June");
col.add("Mary");
col.add("小明");
col.add("小强");
System.out.println(col.size());//返回当前集合中的元素数
col.addAll(colOne);//将参数集合中的所有元素都添加进当前集合中
System.out.println(col.size());
System.out.println(col);//对于数组来说若直接打印数组名则运行结果为数组元素的首地址,而对于集合来说运行结果为集合中的元素
col.remove("Tom");//将参数从集合中移除,若参数在集合中有重复元素,则只会移除第一个重复元素
System.out.println(col);
col.removeAll(colOne);//在原集合中,移除参数集合中的所有元素
System.out.println(col);
// col.clear();//清空集合
System.out.println(col.contains("Tom"));//判断集合中是否包含参数元素
System.out.println(col.containsAll(colOne));//判断参数集合中的元素是否在当前集合中都存在
System.out.println("-----------------------------------------");
col.retainAll(col1);//保留当前集合中与参数集合中的元素相同的元素,而删除其他元素
System.out.println(col);
}
}
Collection集合的遍历
普通for循环
package at.heima.test2;
import java.util.ArrayList;
import java.util.Collection;
public class TestFive {
public static void main(String[] args) {
Collection<String> col = new ArrayList<>();
col.add("a");
col.add("b");
col.add("b");
col.add("c");
col.add("d");
for (int i = 0; i < col.size(); i++) {
String s = ((ArrayList<String>) col).get(i);
System.out.println(s);
}
}
}
Iterator迭代器遍历
-
该遍历是集合的专用遍历方式
注意:由于不知道该接口Iterator的实现类是什么,所以没办法创建实现类的对象,所以需要调用集合中的iterator()方法来返回一个迭代器对象(哪个集合调用iterator()
方法,迭代器就迭代哪个集合)
Iterator<E> iterator()
:返回集合中的迭代器对象,该迭代器对象默认指向当前集合的0索引 -
Iterator中的常用方法
boolean hasNext()
:判断迭代器的指针当前所在位置是否有元素。如果有,则返回true;否则返回false
E next()
:返回集合中当前指针指向的元素,并将迭代器的指针向后移动一个位置 -
代码示例
package at.heima.collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class TestOne {
public static void main(String[] args) {
Collection<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
Iterator<String> iter = list.iterator();//返回一个迭代器对象
//判断迭代器是否还有下一个元素
while (iter.hasNext()) {
//返回集合中的下一个元素,并将迭代器的指针向前移动一个位置
System.out.println(iter.next());
}
}
}
注意:进行遍历时不能直接调用迭代器对象的next()方法,因为集合可能为空,,导致报异常,所以需要引入迭代器对象的hasNext()方法来判断是否存在下一个元素
Iterator迭代器删除
java.util.Iterator迭代器中有一个方法------void remove()
JDK1.8之前用的是Iterator迭代器中的remove()方法,在循环遍历时通过某个条件来删除元素,JDK1.8之后通过Collection接口中的removelf()方法来删除满足给定条件的此集合的所有元素
在遍历时只能用迭代器的移除,不能用Collection接口中的removelf()来移除,否则会报异常
例题:创建一个集合,有元素a、b、c、d,使用循环遍历该集合,判断当前获取到的元素是否为b,若是则删除
方式一
package at.heima.test2;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class TestFour {
public static void main(String[] args) {
Collection<String> col = new ArrayList<>();
col.add("a");
col.add("b");
col.add("b");
col.add("c");
col.add("d");
for (int i = 0; i < col.size(); i++) {
String s = ((ArrayList<String>) col).get(i);
if ("b".equals(s)) {
col.remove(s);
}
System.out.println(s);
}
}
}
注意:要是按照以上for循环方式进行删除时,最后删除不彻底仍会有b元素的存在,原因:集合的长度是可以自动改变的,所以在你删除第一个b元素后此时该元素后面的元素会自动往前进一位,所以原来在索引2位置上的元素会移动到已删除的索引1位置上,从而导致没办法完全删除,所以在进行删除操作时需要有个i–操作来避免该现象的存在,所以应改为如下方式
package at.heima.test2;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class TestFour {
public static void main(String[] args) {
Collection<String> col = new ArrayList<>();
col.add("a");
col.add("b");
col.add("b");
col.add("c");
col.add("d");
for (int i = 0; i < col.size(); i++) {
String s = ((ArrayList<String>) col).get(i);
if ("b".equals(s)) {
col.remove(s);
i--;
}
}
for (String s : col) {
System.out.println(s);
}
}
}
方式二
package at.heima.test2;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class TestFour {
public static void main(String[] args) {
Collection<String> col = new ArrayList<>();
col.add("a");
col.add("b");
col.add("b");
col.add("c");
col.add("d");
//方式一:for循环删除
// for (int i = 0; i < col.size(); i++) {
// String s = ((ArrayList<String>) col).get(i);
// if ("b".equals(s)) {
// col.remove(s);
// i--;
// }
// }
// for (String s : col) {
// System.out.println(s);
// }
//方式二:迭代器方式删除
Iterator<String> iter = col.iterator();
while (iter.hasNext()) {
if ("b".equals(iter.next())) {
iter.remove();//从迭代器所在的集合中删除当前指向的元素
}
}
System.out.println(col);
}
}
增强for循环------JDK1.5之后出现
- 作用
简化数组和Collection集合的遍历
- 适用范围
(1)实现Iterator接口的实现类才可以使用迭代器和增强for循环
(2)数组
- 格式
for (元素数据类型 变量名 :数组名或者Collection集合对象名){
//在此处使用变量即可,该变量就是元素
}
注意:数据类型一定是集合或者数组中元素的类型
- 注意
(1)增强for循环在遍历数组时,底层用的是普通for循环
(2)增强for循环在遍历集合时,底层用的是迭代器
package at.heima.test2;
import java.util.ArrayList;
import java.util.Collection;
public class TestFive {
public static void main(String[] args) {
Collection<String> col = new ArrayList<>();
col.add("a");
col.add("b");
col.add("b");
col.add("c");
col.add("d");
for (String s : col) {
System.out.println(s);
}
}
}
- 注意
修改第三方变量的值不会影响集合中的元素
package at.heima.test2;
import java.util.ArrayList;
import java.util.Collection;
public class TestFive {
public static void main(String[] args) {
Collection<String> col = new ArrayList<>();
col.add("a");
col.add("b");
col.add("b");
col.add("c");
col.add("d");
//增强for循环中的String s 属于第三方变量
for (String s : col) {
s = "q";
System.out.println(s);
}
System.out.println(col);
}
}
三种循环的选择
若需要操作索引则使用普通for循环
如果需要在遍历过程中删除元素则使用迭代器
若仅仅只想遍历则使用增强for循环
List集合
Collection接口中的方法,List接口中均有(原因:List接口为Collection接口的子接口)
- 概述
(1)list集合是有序(新增顺序和遍历顺序一致)集合,且该集合中允许有重复元素
(2)用户可以精确控制列表中每个元素的插入位置
(3)用户可以通过整数索引访问元素,并搜索列表中的元素
- 特点
(1)有序:存入顺序和遍历顺序一致
(2)有索引:可以通过索引操作元素
(3)可重复:存储的元素可以重复
package at.heima.list;
import java.util.ArrayList;
import java.util.List;
public class TestOne {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
for (String s : list) {
System.out.println(s);
}
}
}
List集合特有方法
方法 | 解释 |
---|---|
void add(int index, E element) | 将第二个参数元素新增到集合的第1个参数索引位置,原来在指定位置的元素以及后续的元素将向后移动一个索引位置。 |
boolean addAll(int index, Collection<? extends E> c) | 将第二个参数集合新增到当前集合的第1个参数索引位置,原来在指定位置的元素以及后续的元素将向后移动一个索引位置。 |
E set(int index, E element) | 将第二个参数元素替换集合的第1个参数索引位置**,**原来在指定位置处的元素消失 |
E remove(int index) | 移除指定下标索引处的元素,该下标之后的元素会自动向前移动 |
E get(int index) | 使用下标获取单个元素 |
package at.guigu.test1;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class TestThree {
public static void main(String[] args) {
List list = new ArrayList();
List list1 = new ArrayList();
//新增元素
list.add("Tom");
list.add("Jerry");
list.add("Mary");
list.add("June");
list1.add("小白");
list1.add("小强");
System.out.println(list.size());
System.out.println(list);
//将第二个参数元素新增到集合的第1个参数索引位置,原来在指定位置的元素以及后续的元素将向后移动一个索引位置。
list.add(1,"小花");
System.out.println(list);
//将第二个参数集合新增到当前集合的第1个参数索引位置,原来在指定位置的元素以及后续的元素将向后移动一个索引位置。
list.addAll(1,list1);
System.out.println(list);
System.out.println("----------------------------------------");
//将第二个参数元素替换集合的第1个参数索引位置,原来在指定位置处的元素消失
list.set(1,"小黑");
System.out.println(list);
list.remove(1);//移除指定下标索引处的元素,该下标之后的元素会自动向前移动
System.out.println(list);
String s = (String)list.get(0);//使用下标获取单个元素
System.out.println(s);
}
}
List集合的实现类ArrayList
-
特点
(1)ArrayList底层使用可变长度数组(即动态数组,为自动扩容的数组)实现
(2)ArrayList数组在集合第一次调用add方法时初始化数组,且初始化长度为10
(3)当元素数超过数组长度时扩容,按照原来长度的1.5倍扩容
(4)遍历效率高,追加元素时效率高,但是在元素插队或者删除时效率低(即频繁存取效率低) -
注意
ArrayList底层使用可变长度数组,所以ArrayList的效率一定体现了数组的效率 -
代码格式
List<String> list = new ArrayList<>();
List集合的实现类LinkedList
- 特点
(1)LinkedList底层使用双向链表实现(所以LinkedList的效率一定体现的是双向链表的效率)
(2)LinkedList有一套对首尾元素单独操作的方法(此时只能用LinkedList声明)
(3)遍历效率低(因为链表的数据元素不是在一起的),但是频繁存取的效率高
特有方法
package at.heima.list;
import java.util.LinkedList;
public class TestTwo {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList();
list.add("aaa");
list.add("bbb");
list.add("ccc");
System.out.println(list);
//列表开头添加指定的元素
list.addFirst("qqq");
System.out.println(list);
//列表末尾添加指定的元素
list.addLast("www");
System.out.println(list);
//返回列表中的第一个元素
String s = list.getFirst();
System.out.println(s);
//返回列表中的最后一个元素
String ss = list.getLast();
System.out.println(ss);
//删除并返回列表中的第一个元素
String s1 = list.removeFirst();
System.out.println(s1);
//删除并返回列表中的最后一个元素
String s2 = list.removeLast();
System.out.println(s2);
}
}
问题
- 什么时候用ArrayList,什么时候用LinkedList?
根据两者的特点逐情选取,如果需要快速读取元素,较少插入和删除操作,可以选择ArrayList;如果需要频繁插入和删除元素,可以选择LinkedList。