目录
集合
主要内容
-
List集合
-
Set集合
-
Map集合
集合
集合是java中提供的一种容器,可以用来存储多个对象数据。
既然有数组作为数据容器,Java为什么还提供集合容器?它们有啥区别呢?
-
数组的长度是固定的,不利于数据的增删。集合的长度是可变的。
-
数组中提供的属性和方法较少,不便于进行常用的增删改查操作,集合提供了更丰富的API。
-
数组存储数据的特点单一,即有序的,可重复的。集合有很多种类型,分别有不同的存储特点。
-
数组中可以存储基本数据类型值,也可以存储对象,而集合中只能存储对象
集合主要分为两大系列:Collection和Map,Collection 表示一组对象,Map表示一组映射关系或键值对。
1、 Collection
Collection 层次结构中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。JDK 不提供此接口的任何直接实现:它提供更具体的子接口(如 Set 和 List、Queue)实现。此接口通常用来传递 collection,并在需要最大普遍性的地方操作这些 collection。
Collection<E>是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法,这些方法可用于操作所有的单列集合。方法如下:
1、添加元素
(1)add(E obj):添加元素对象到当前集合中
(2)addAll(Collection<? extends E> other):添加other集合中的所有元素对象到当前集合中,即this = this ∪ other
2、删除元素
(1) boolean remove(Object obj) :从当前集合中删除第一个找到的与obj对象equals返回true的元素。
(2)boolean removeAll(Collection<?> coll):从当前集合中删除所有与coll集合中相同的元素。即this = this - this ∩ coll
(3) void clear(); 清空集合
3、判断
(1)boolean isEmpty():判断当前集合是否为空集合。
(2)boolean contains(Object obj):判断当前集合中是否存在一个与obj对象equals返回true的元素。
(3)boolean containsAll(Collection<?> c):判断c集合中的元素是否在当前集合中都存在。即c集合是否是当前集合的“子集”。
4、获取元素个数
(1)int size():获取当前集合中实际存储的元素个数
5、交集
(1)boolean retainAll(Collection<?> coll):当前集合仅保留与c集合中的元素相同的元素,即当前集合中仅保留两个集合的交集,即this = this ∩ coll;
6、转为数组
(1)Object[] toArray():返回包含当前集合中所有元素的数组
方法演示:
public class Demo {
public static void main(String[] args) {
//创建Collection集合
Collection c = new ArrayList();
// *1、添加元素 **
//(1)add(E obj):添加元素对象到当前集合中
c.add("hello");
c.add("world");
c.add("java");
System.out.println(c);
//(2)addAll(Collection < ? extends E > other):添加other集合中的所有元素对象到当前集合中,即this = this ∪other
Collection c2 = new ArrayList();
c2.add("tom");
c2.add("jack");
c2.add("java");
c.addAll(c2);
System.out.println(c);
// *2、删除元素 **
// (1)boolean remove (Object obj) :从当前集合中删除第一个找到的与obj对象equals返回true的元素。
c.remove("java");
// (2)boolean removeAll (Collection < ? > coll):从当前集合中删除所有与coll集合中相同的元素。即this = this - this ∩coll
c.removeAll(c2);
//(3) void clear (); 清空集合
c.clear();
// **3、判断 **
//(1)boolean isEmpty ():判断当前集合是否为空集合。
System.out.println(c.isEmpty());
//(2)boolean contains (Object obj):判断当前集合中是否存在一个与obj对象equals返回true的元素。
System.out.println(c.contains("tom"));
//(3)boolean containsAll (Collection < ? > c):判断c集合中的元素是否在当前集合中都存在。即c集合是否是当前集合的“子集”。
System.out.println(c.containsAll(c2));
//**4、获取元素个数 **
//(1)int size ():获取当前集合中实际存储的元素个数
int size = c.size();
System.out.println(size);
// **5、交集 **
//(1)boolean retainAll (Collection < ? > coll):当前集合仅保留与c集合中的元素相同的元素,即当前集合中仅保留两个集合的交集,即this = this ∩coll;
System.out.println(c.retainAll(c2));
// **6、转为数组 **
//(1)Object[] toArray ():返回包含当前集合中所有元素的数组
Object[] arrays = c.toArray();
for (int i = 0; i < arrays.length; i++) {
System.out.println(arrays[i]);
}
System.out.println(c);
}
}
2、 Iterator迭代器
1. Iterator接口API
在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口java.util.Iterator
。Iterator
接口也是Java集合中的一员,但它与Collection
、Map
接口有所不同,Collection
接口与Map
接口主要用于存储元素,而Iterator
主要用于迭代访问(即遍历)Collection
中的元素,因此Iterator
对象也被称为迭代器。
想要遍历Collection集合,那么就要获取该集合迭代器完成迭代操作,下面介绍一下获取迭代器的方法:
-
public Iterator iterator()
: 获取集合对应的迭代器,用来遍历集合中的元素的。
下面介绍一下迭代的概念:
-
迭代:即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。
Iterator接口的常用方法如下:
-
public E next()
:返回迭代的下一个元素。 -
public boolean hasNext()
:如果仍有元素可以迭代,则返回 true。 -
public void remove()
:通过迭代器删除元素
/**
* Iterator迭代器,用于遍历Collection集合
*/
public class Demo {
public static void main(String[] args) {
//创建Collection集合
Collection c = new ArrayList();
//添加元素
c.add("tom");
c.add("jack");
c.add("rose");
c.add("jerry");
//调用Colletcion集合的iterator方法获取一个迭代器 (使用迭代器遍历集合↓)
Iterator it = c.iterator(); // Iterator是接口(一种类型)
//调用Iterator的方法遍历集合
Object next = it.next();// it.next() == 取出下一个元素
System.out.println(next);
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());
// System.out.println(it.next());//NoSuchElementException
boolean b = it.hasNext();//判断是否有下一个元素
System.out.println("b = " + b);
while (it.hasNext()){//判断是否有下一个元素
Object obj = it.next();//取出下一个元素
System.out.println(obj);
}
}
}
public class Demo2 {
public static void main(String[] args) {
//创建Collection集合
Collection c = new ArrayList();
//添加元素
c.add(11);
c.add(22);
c.add(55);
c.add(44);
c.add(33);
//遍历集合
Iterator it = c.iterator();//创建迭代器
while (it.hasNext()) {//判断是否有下一个元素
Object obj = it.next();
System.out.println(obj);
}
}
}
3. 增强for与Iterable接口
增强for循环(也称foreach循环)是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。
/**
* foreach循环,也称为增强for循环:用于遍历数组和collection集合
* 格式:
* for(元素类型 变量: 数组或集合){
* //循环体
* }
*/
public class Demo {
//增强for遍历数组
@Test
public void test() {
int[] arr = {11, 22, 33, 44};
for (int i : arr) {//区别普通for循环,增强for循环的i已经是元素,不在是角标(下标)
System.out.println(i);//所以直接输出i即可打印值,非arr[i]
}
}
//增强for循环遍历Collection集合:底层实现是Iterator迭代器
@Test
public void test1(){
Collection c = new ArrayList();
c.add("hello");
c.add("java");
c.add("world");
c.add("abc");
//遍历集合 快捷写法: c.iter + 回车 , c.for + 回车
for (Object obj : c) {
System.out.println(obj);
}
}
}
增强for循环可以遍历Iterable接口的实现类:
//自定义类,继承Iterable
public class Student implements Iterable {
@Override
public Iterator iterator() {
return null;
}
}
public class Demo2 {
@Test
public void test(){
Student stu = new Student();
for (Object o : stu) { //Student继承Iterable后重写iterator方法方可调用
}
}
}
4. modCount与fail-fast机制
这里举一个小例子,让该知识点更通俗易懂:课代表要查班级中总共有多少学生,从第一位学生开始数数 1 2 3... ,数到一半的时候班主任叫走了第一位同学,但是此时课代表并不知道已经走了一位同学且还在继续往后计算,最后班长数的结果和实际不同。课代表查询的是原班级总人数,而老师在课代表查询期间改变了班级人数,这就叫并发修改异常。
迭代器的快速失败机制:当使用迭代器遍历集合时,使用迭代器之外的方式修改集合,那么迭代器就会立即抛出(ConcurrentModificationException)并发修改异常 。来阻止这样的操作,以免在将来发生更大问题。
如何实现的快速失败机制?
借助集合的一个遍历modCount实现。modCcount记录了集合的修改次数,当创建迭代器时迭代器会记录此时的集合修改次数, 在每次取元素时都会判断集合的修改次数是否有变化,如果有则立即抛异常。
public class Demo {
@Test
public void test(){
Collection c = new ArrayList();
c.add("tom");
c.add("jack");
c.add("rose");
c.add("jerry");
//遍历集合并删除指定的一个元素
Iterator it = c.iterator();
while (it.hasNext()) {//遍历
Object obj = it.next(); //java.util.ConcurrentModificationException 并发修改异常
if ("jack".equals(obj)){//判断是否存在
// c.remove(obj);//使用集合的删除方法删除元素
it.remove();//使用迭代器的删除方法,删除当前元素
}
}
//使用增强for遍历集合时不要修改集合
for (Object o : c) {
c.remove(o);//java.util.ConcurrentModificationException
}
System.out.println(c);
}
}
3. List集合
我们掌握了Collection接口的使用后,再来看看Collection接口中的子接口,他们都具备那些特性呢?
1. List接口介绍
java.util.List
接口继承自Collection
接口,是单列集合的一个重要分支,习惯性地会将实现了List
接口的对象称为List集合。
List接口特点:
-
List集合所有的元素是以一种线性方式进行存储的,例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)
-
它是一个元素存取有序的集合。即元素的存入顺序和取出顺序有保证。
-
它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)。
-
集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。
/**
* List集合
* List是一个Collection的典型子接口:
* List集合的特点:
* 1.元素有序,可重复
* 2.可以通过索引访问元素
* Set集合特点:
* 元素唯一,通常无序。
*
* List接口的特有方法:(都是与索引相关的方法)
*/
public class Demo {
@Test
public void test(){
//创建List集合
List list = new ArrayList();
list.add("hello");
list.add("world");
list.add("abc");
System.out.println(list); //[hello, world, abc]
//1、添加元素,在指定位置添加新元素
//* void add(int index, E ele)
list.add(1, "java"); //[hello, java, world, abc]
//* boolean addAll(int index, Collection<? extends E> eles) 在指定的位置添加另外一个集合
//2、获取元素
//* E get(int index)
Object obj = list.get(0);
System.out.println(obj);
//* List subList(int fromIndex, int toIndex) 截取一个子集合
List subList = list.subList(0, 2); // [hello, java] //截 取0-2位置,包含0 不包含2 【包前不包后】
System.out.println(subList);
//3、获取元素索引
//* int indexOf(Object obj) 获取元素第一次出现的位置
int index = list.indexOf("java");
System.out.println("index = " + index); //index = 1
//* int lastIndexOf(Object obj)
int lastIndexOf = list.lastIndexOf("world");
System.out.println("lastIndexOf = " + lastIndexOf); //lastIndexOf = 2
//4、删除和替换元素
//* E remove(int index) 删除指定位置的元素,返回被删除的元素
Object remove = list.remove(0);
System.out.println("remove = " + remove); //remove = hello [java, world, abc]
//* E set(int index, E ele) 修改指定位置的元素,返回被修改的元素
Object set = list.set(0, "JAVA");
System.out.println("set = " + set); //set = java [JAVA, world, abc]
System.out.println(list);
}
}
1.1 List集合的遍历方式
public class Demo2 {
@Test
public void test(){
//创建List集合
List list = new ArrayList();
//添加元素
list.add(11);
list.add(22);
list.add(33);
list.add(44);
//遍历集合
//方式一: 迭代器
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
//方式二: 增强for
for (Object obj : list) {
System.out.println(obj);
}
//方式三: 普通for
for (int i = 0; i < list.size(); i++) {
Object obj = list.get(i);
System.out.println(obj);
}
}
}
2. List接口中常用方法
List作为Collection集合的子接口,不但继承了Collection接口中的全部方法,而且还增加了一些根据元素索引来操作集合的特有方法,如下:
List除了从Collection集合继承的方法外,List 集合里添加了一些根据索引来操作集合元素的方法。
1、添加元素
-
void add(int index, E ele)
-
boolean addAll(int index, Collection<? extends E> eles)
2、获取元素
-
E get(int index)
-
List subList(int fromIndex, int toIndex)
3、获取元素索引
-
int indexOf(Object obj)
-
int lastIndexOf(Object obj)
4、删除和替换元素
-
E remove(int index)
-
E set(int index, E ele)
List集合特有的方法都是跟索引相关:
public class Student {
private String name;
private int age;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
return name != null ? name.equals(student.name) : student.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
}
public class Demo3 {
public static void main(String[] args) {
//创建List集合
List list = new ArrayList();
//添加元素
Student stu = new Student("tom", 18);
list.add(stu);
list.add(new Student("jack", 19));
list.add(new Student("rose", 18));
list.add(new Student("jerry", 20));
list.add(new Student("tom", 18));
//遍历集合
//迭代器
Iterator it = list.iterator();
while (it.hasNext()) {
Object obj = it.next();
System.out.println(obj);
}
Student student = new Student("tom",18);
boolean remove = list.remove(student);
System.out.println("remove = " + remove);
System.out.println(list);
}
}
3. ArrayList实现类
ArrayList是List接口的典型实现类,底层使用长度可变的数组实现,常用方法都来自Collection和List接口。
ArrayList因为底层使用了数组存储数据,所以具有查询快,增、删慢的特点。
数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这些运算后所得到的新结构仍然是原来的结构类型。
逻辑结构:描述元素之间的关联关系
物理结构:存储结构,有一定关系的元素数据如何存储
public class Demo {
public static void main(String[] args) {
//创建ArrayList集合
ArrayList list = new ArrayList();
list.add("hello");
list.add("java");
list.add("world");
list.add("abc");
list.add("abc");
for (Object o : list) {
System.out.println(o);
}
}
}
/**
* Arrays数组工具类的asList方法,可以快速创建一个List集合,此集合是不可变的
*/
import java.util.Arrays;
import java.util.List;
public class Demo {
public static void main(String[] args) {
List list = Arrays.asList("hello", "java", "world", "abc");
// list.add("xxx");//java.lang.UnsupportedOperationException 不支持的操作
list.remove("java");//java.lang.UnsupportedOperationException
System.out.println("list = " + list);
}
}
4. ListIterator
List 集合额外提供了一个 listIterator() 方法,该方法返回一个 ListIterator 对象, ListIterator 接口继承了 Iterator 接口,提供了专门操作 List 的方法:
-
void add():通过迭代器添加元素到对应集合
-
void set(Object obj):通过迭代器替换正迭代的元素
-
void remove():通过迭代器删除刚迭代的元素
-
boolean hasPrevious():如果以逆向遍历列表,往前是否还有元素。
-
Object previous():返回列表中的前一个元素。
-
int previousIndex():返回列表中的前一个元素的索引
-
boolean hasNext()
-
Object next()
-
int nextIndex()
public static void main(String[] args) {
List<Student> c = new ArrayList<>();
c.add(new Student(1,"张三"));
c.add(new Student(2,"李四"));
c.add(new Student(3,"王五"));
c.add(new Student(4,"赵六"));
c.add(new Student(5,"钱七"));
//从指定位置往前遍历
ListIterator<Student> listIterator = c.listIterator(c.size());
while(listIterator.hasPrevious()){
Student previous = listIterator.previous();
System.out.println(previous);
}
}