第十一章 Java集合
11.1 Java集合框架概述
11.1.1 数组到集合的引申
面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象的操作,就要对对象进行存储。另一方面,使用Array存储对象方面具有一些弊端,而Java集合就像是一种容器,可以动态地把多个对象的引用放入容器中,还可用于保存具有映射关系的关联数组。
数组在内存存储方面的特点:
数组初始化以后,长度就确定了
数组声明的类型,就决定了进行元素初始化时的类型
数组在存储数据方面的弊端:
数组初始化以后,长度就不可改变了,不便于扩展
数组中提供的属性和方法少,不便于进行添加,删除,插入等操作,且效率不高,同时无法直接获取存储元素的个数
数组存储的数据是有序的,可以重复的,存储数据的特点比较单一
11.1.2 Java集合的框架体系
Java集合可分为 Collection 和 Map 两种体系:
Collection接口:单列数据,定义了存取一组对象的方法的集合
List:元素有序,可重复的集合
Set:元素无序,不可重复的集合
Map接口:双列数据,保存具有影射关系“key-value”键值对的集合
Collection接口继承树
Map接口继承树
11.2 Collection的接口方法
import java.util.*;
/**
* Welcome to Idea Java
*
* @author Administrator
* @date 2021/11/26 8:29
**/
public class CollectionTest {
public static void main(String[] args) {
Collection collection1 = new ArrayList();
//add(Object e):将元素e添加到集合collection中
collection1.add(123);
collection1.add("lisi");
collection1.add(new Date());
System.out.println(collection1);// [123, lisi, Fri Nov 26 08:36:35 CST 2021]
//size():获取元素个数
System.out.println(collection1.size());// 3
//addAll():将集合a中的元素全部添加到集合b中
Collection collection2 = new ArrayList();
collection2.addAll(collection1);
System.out.println(collection2);// [123, lisi, Fri Nov 26 08:39:39 CST 2021]
//clear():将集合元素清空
collection2.clear();
//isEmpty():判断集合是否为空
System.out.println(collection2.isEmpty());// true
//contains(Object):判断集合是否包含某个元素
System.out.println(collection1.contains("lisi"));// true
//containsAll(Collection b):判断集合b中的元素是否都存在于集合a中
collection2.add("zan");
collection2.add("lisi");
System.out.println(collection1.containsAll(collection2));// false
//remove():移除集合中的某个元素
System.out.println(collection1.remove("lisi"));// true
System.out.println(collection1);// [123, Fri Nov 26 08:48:22 CST 2021]
//removeAll():从当前集合a中移除与集合b中相同的所有元素
collection2.add(123);
System.out.println(collection2);// [zan, lisi, 123]
System.out.println(collection2.removeAll(collection1));// true
System.out.println(collection1);// [123, Fri Nov 26 08:55:48 CST 2021]
System.out.println(collection2);// [zan, lisi]
//retainAll():获取当前集合a与集合b中的交集,并返回给当前集合
collection1.add("lisi");
System.out.println(collection1);//[123, Fri Nov 26 08:58:37 CST 2021, lisi]
System.out.println(collection1.retainAll(collection2));// true
System.out.println(collection1);// [lisi]
//equals():判断集合a与集合b是否相同,需要元素全部相同才能返回true
System.out.println(collection1.equals(collection2)); // false
Collection collection3 = new ArrayList();
collection3.add("lisi");
System.out.println(collection1.equals(collection3));// true
//hashCode():返回当前集合的哈希值
System.out.println(collection1.hashCode());
//toArray():集合向数组的转型
Object[] objects = collection2.toArray();
System.out.println(Arrays.toString(objects)); // [zan, lisi]
//toList():数组向集合的转型
List<Object> objects1 = Arrays.stream(objects).toList();
System.out.println(objects1);// [zan, lisi]
}
}
11.3 Iterator迭代器遍历集合
11.3.1 Iterator迭代器的概述
①.Iterator对象称为迭代器(设计模式的一种),主要用于遍历Collection集合中的元素。
②.GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生。
③.Collection接口继承了java.lang.Iterable接口,该接口有一个itreator()方法,那么所有实现了Collection接口的集合类都有一个treator()方法,用以返回一个实现了Iterator接口的对象。
④.Iterator仅用于遍历集合,Iterator本身并不提供承装对象的能力。如果需要创建Iterator对象,则必须有一个被迭代的集合。
⑤.集合对象每次调用itreator()方法都得到一个全新的迭代对象,默认游标都在集合的第一个元素之前。
11.3.1 Iterator迭代器的使用
import java.util.*;
// 集合元素的遍历操作 使用迭代器Iterator接口
public class CollectionTest {
public static void main(String[] args) {
Collection collection1 = new ArrayList();
collection1.add(123);
collection1.add("lisi");
collection1.add(new Date());
//集合实现Iterator接口
Iterator iterator = collection1.iterator();
//使用迭代器Iterator接口对象循环遍历
while(iterator.hasNext()){
//hasNext():判断是否还有下一个元素
//next():指针下移,将下移以后集合位置上的元素返回
System.out.println(iterator.next());
}
//使用迭代器remove()方法对集合元素进行删除
Iterator iterator1 = collection1.iterator();
while(iterator1.hasNext()){
Object next = iterator1.next();
if ("lisi".equals(next)){
iterator1.remove();
}
}
}
}
注意:Iterator可以删除集合的元素,但是遍历过程中通过迭代器对象的remove方法,不是集合对象的remove方法;若果还未调用next()或在上一次调用next方法之后已经调用了remove方法,再调用remove都会报异常。
11.3.2 集合遍历的另一种实现方式
增强for循环:foreach()的使用:
import java.util.*;
// 增强for循环:foreach()的使用
public class CollectionTest {
public static void main(String[] args) {
Collection collection1 = new ArrayList();
collection1.add(123);
collection1.add("lisi");
collection1.add(new Date());
for (Object o:collection1) {
System.out.println(o);
}
}
}
11.4 Collection子接口一:List
11.4.1 List接口概述
①.鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组
②.List集合类中元素有序,且可重复,集合中的每个元素都具有其对应的顺序索引。
③.List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
④.JDK API中List接口的实现类常用的有:ArrayList,LinkedList和Vector。
11.4.2 List接口的方法
List除了从Collection集合继承的方法外,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在当前集合中首次出现的位置
int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
Object remove(int index):移除指定index位置的元素,并返回此元素
Object set(int index,Object obj):设置指定index位置的元素为ele
List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合
import java.util.*;
//List接口的方法
public class CollectionTest {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
ArrayList arrayList1 = new ArrayList();
arrayList.add(1);
arrayList.add(3);
arrayList.add(5);
arrayList.add(6);
arrayList1.add(4);
//void add(int index,Object ele):在index位置插入ele元素
arrayList.add(1,2);
System.out.println(arrayList);//[1, 2, 3, 5, 6]
//boolean addAll(int index,Collection eles):从index位置开始将eles中的所有元素添加进来
arrayList.addAll(3,arrayList1);
System.out.println(arrayList);//[1, 2, 3, 4, 5, 6]
//Object get(int index):获取指定index位置的元素
System.out.println(arrayList.get(1));//2
//int indexOf(Object obj):返回obj在当前集合中首次出现的位置
System.out.println(arrayList.indexOf(1));//0
//int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
System.out.println(arrayList.lastIndexOf(1));//0
//Object remove(int index):移除指定index位置的元素,并返回此元素
System.out.println(arrayList1);//[4]
System.out.println(arrayList1.remove(0));//4
System.out.println(arrayList1);//[]
//Object set(int index,Object obj):设置指定index位置的元素为ele
System.out.println(arrayList);//[1, 2, 3, 4, 5, 6]
arrayList.set(0,0);
System.out.println(arrayList);//[0, 2, 3, 4, 5, 6]
//List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合
System.out.println(arrayList.subList(1,6));//[2, 3, 4, 5, 6]
}
}
11.4.3 List实现类之一:ArrayList
①.ArrayList是List接口的典型实现类,主要实现类
②.本质上,ArrayList是对象引用的一个”变长“数组
③.ArrayList的JDK 1.8之前与之后的实现区别?
JDK1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组
JDK1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加一个元素时再创建一个容量为10的数组
④.ArrayList.asList()方法返回的List集合,既不是ArrayList实例,也不是Vector实例。ArrayList.asList()方法返回值是一个固定长度的List集合
11.4.4 List实现类之二:LinkedList
LinkedList:双向链表,内部没有声明数组。而定义了Node类型的first和last,用于记录首末元素。同时,定义内部类Node,作为LinkedList中保存数据的基本结构,对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高。Ndoe除了保存数据,还定义了两个变量: prev:变量记录前一个元素的位置;next:变量记录下一个元素的位置。
新增方法:void addFirst(Object obj)
void addLast(Object obj)
Object getFirst()
Object getLast()
Object removeFirst()
Object removeLast()
11.4.5 List实现类之三:Vector
Vector的内部实现类似于ArrayList,Vector也是基于一个容量能够动态增长的数组来实现的,该类是JDK1.0版本添加的类,它的很多实现方法都加入了同步语句,因此是线程安全的(但Vector其实也只是相对安全,有些时候还是要加入同步语句来保证线程的安全)。
Vector继承于AbstractList,实现了List接口,可以对它进行队列操作;实现了RandmoAccess接口,即提供了随机访问功能;实现了Cloneable接口,能被克隆;实现了Serializable接口,因此它支持序列化,能够通过序列化传输。
11.4.6 ArrayList,LinkedList,Vector三者的异同
ArrayList:作为List接口的主要实现类,线程不安全,效率高,底层使用Object[] elementData存储。
LinkedList:对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率比
ArrayList高,底层使用双向链表。
Vector:作为List接口的古老实现类,线程安全,效率低,底层使用Object[] elementData存储。
11.5 Collection子接口二:Set
11.5.1 Set接口概述
Set:存储无序的,不可重复的数据
①.无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。
②.不可重复性:保证添加的元素按照equals()判断时,不能返回true。即相同的元素只能添加一个。
③.Set接口是Collection的子接口,set接口没有提供额外的方法
④.Set集合不允许包含相同的元素,如果试着把两个相同的元素加入同一个Set集合中,则添加操作失败。
⑤.Set判断两个对象是否相同不是使用 == 运算符,而是根据equals()方法
11.5.2 Set实现类之一:HashSet
①.HashSet 是Set 接口的典型实现,大多数时候使用Set集合时都使用这个实现类。
②.HashSet 按 Hash算法来存储集合中的元素,因此具有很好的存取,查找,删除性能。
③.HashSet 具有以下特点:不能保证元素的排列顺序;HashSet不是线程安全;集合元素可以说null
④.HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode()方法比较相等,并且两个对象的 equals()方法返回值也相等。
⑤.对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等原则。即:“相等的对象必须具有相等的散列码”。
11.5.3 Set实现类之二:LinkedHashSet
①.LinkedHashSet是HashSet的子类
②.LinkedHashSet根据元素的hashCode值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
③.LinkedHashSet插入性能略低于HashSet,但在迭代访问Set里的全部元素时有很好的性能。
④.LinkedHashSet不允许集合元素重复。
11.5.4 Set实现类之二:TreeSet
①.TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。
②.TreeSet底层使用红黑树结构存储数据
③.新增的方法如下:(了解)
Comparator comparator()
Object first()
Object last()
Object lower(Object e)
Object higher(Object e)
SortedSet subSet(fromElement,toElement)
SortedSet headSet(toElement)
SortedSet tailSet(fromElement)
④.TreeSet 两种排序方法:自然排序和定制排序。默认情况下,ThreeSet采用自然排序。
11.5.5 TreeSet的练习实例
import java.util.Comparator;
import java.util.TreeSet;
/**
* Welcome to Idea Java
*
* @author Administrator
* @date 2021/11/27 9:55
**/
public class SetTest {
public static void main(String[] args) {
//为treeSet设置定制排排序,安装生日进行排序,每次创建都进行对比排序
TreeSet treeSet = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Employee && o2 instanceof Employee) {
Employee employee1 = (Employee) o1;
Employee employee2 = (Employee) o2;
MyData data1 = employee1.getBirthday();
MyData data2 = employee2.getBirthday();
int minYear = data1.getYear() - data2.getYear();
if (minYear != 0) return minYear;
int minMouth = data1.getMonth() - data2.getMonth();
if (minMouth != 0) return minMouth;
return data1.getDay() - data2.getDay();
}
return 0;
}
});
Employee employee1 = new Employee("阿哥", 12, new MyData(1998, 03, 25));
Employee employee2 = new Employee("曹哥", 45, new MyData(1991, 02, 03));
Employee employee3 = new Employee("一哥", 25, new MyData(1988, 05, 22));
Employee employee4 = new Employee("帝哥", 50, new MyData(1988, 05, 13));
Employee employee5 = new Employee("逼哥", 30, new MyData(1977, 12, 18));
treeSet.add(employee1);
treeSet.add(employee2);
treeSet.add(employee3);
treeSet.add(employee4);
treeSet.add(employee5);
for (Object O : treeSet) {
System.out.println(O);
}
}
}
//Employee类
class Employee implements Comparable {
private String name;
private int age;
private MyData birthday;
public Employee() {
}
public Employee(String name, int age, MyData birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
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 MyData getBirthday() {
return birthday;
}
public void setBirthday(MyData birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", birthday=" + birthday +
'}';
}
//Employee实现Comparable接口,按照name排序,自然排序
@Override
public int compareTo(Object o) {
if (o instanceof Employee) {
Employee e = (Employee) o;
return e.name.compareTo(this.name);
}
return 0;
}
}
//MyData类
class MyData {
private int year;
private int month;
private int day;
public MyData() {
}
public MyData(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
@Override
public String toString() {
return "MyData{" +
"year=" + year +
", month=" + month +
", day=" + day +
'}';
}
}