1. 类集
类集就是一个动态的对象数组。Java的类集框架使程序处理对象组的方法标准化。
1.1 为什么设置类集?
普通的对象数组的最大问题在于数组中的元素个数是固定的,不能动态的扩充大小,所以最早的时候可以通过链表实现一个动态对象数组。但是这样做毕竟太复杂了,所以在 Java 中为了方便用户操作各个数据结构, 所以引入了类集的概念,有时候就可以把类集称为 java 对数据结构的实现。
所有类集操作的接口或类都在java.util包中。
1.2 Java类集结构图
2. Collection 集合
2.1 概述
集合是java中提供的一种容器,可以用来存储多个数据。
集合和数组既然都是容器,它们有啥区别呢?
数组的长度是固定的。
集合的长度是可变的。 数组中存储的是同一类型的元素,可以存储基本数据类型值。集合存储的都是对象。而且对象的类型可以不一致。在开发中一般当对象多的时候,使用集合进行存储。
Collection 接口是在整个 Java 类集中保存单值的最大操作父接口,里面每次操作的时候都只能保存一个对象的数据。 此接口定义在 java.util 包中。
2.2 Collection 常用功能
Collection是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法, 这些方法可用于操作所有的单列集合。
在开发中不会直接使用 Collection 接口。而是使用其操作的子接口:List、Set
3. List 接口
3.1 概述
在List集合中允许出现重复的元素,所有的元素是以一种线性方式进行存储的,在程序中可以通过索引来访问集合中的指定元素。另外,List集合还有一个特点就是元素有序,即元素的存入顺序和取出顺序一致
3.2 List接口对已有的Collection接口进行的方法扩充
3.3 实现类
ArrayList:底层数据结构是数组,查询快,增删慢,线程不安全,效率高,可以存储重复元素
LinkedList :底层数据结构是链表,查询慢,增删快,线程不安全,效率高,可以存储重复元素
Vector:底层数据结构是数组,查询快,增删慢,线程安全,效率低,可以存储重复元素
3.4 ArrayList
ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。
ArrayList 继承了 AbstractList ,并实现了 List 接口。
3.4.1 ArrayList与Collection关系
例:
3.5 Vector
Vector 类实现了一个动态数组。和 ArrayList 很相似,但是两者是不同的:
- Vector 是同步访问的。
- Vector 包含了许多传统的方法,这些方法不属于集合框架。
Vector 主要用在事先不知道数组的大小,或者只是需要一个可以改变大小的数组的情况。
3.5.1 Vector与Collection关系
例:
3.6 LinkedList
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址。
链表可分为单向链表和双向链表。
一个单向链表包含两个值: 当前节点的值和一个指向下一个节点的链接。
Java LinkedList(链表) 类似于 ArrayList,是一种常用的数据容器。
与 ArrayList 相比,LinkedList 的增加和删除对操作效率更高,而查找和修改的操作效率较低。
以下情况使用 ArrayList :
- 频繁访问列表中的某一个元素。
- 只需要在列表末尾进行添加和删除元素操作。
以下情况使用 LinkedList :
- 你需要通过循环迭代来访问列表中的某些元素。
- 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。
3.6.1 LinkedList类对List接口与Queue接口进行的扩充
3.6.2 LinkedList与Collection关系
例:
4. Set接口
Set 接口也是 Collection 的子接口,与 List 接口最大的不同在于,Set 接口里面的内容是不允许重复的。
Set 接口并没有对 Collection 接口进行扩充,基本上还是与 Collection 接口保持一致。因为此接口没有 List 接口中定义 的 get(int index)方法,所以无法使用循环进行输出。
那么在此接口中有两个常用的子类:HashSet、TreeSet
4.1 散列存放:HashSet
HashSet 属于散列的存放类集,里面的内容是无序存放的。
HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。
HashSet 允许有 null 值。
HashSet 是无序的,即不会记录插入的顺序。
HashSet 不是线程安全的, 如果多个线程尝试同时修改 HashSet,则最终结果是不确定的。 您必须在多线程访问时显式同步对 HashSet 的并发访问。
4.1.1 HashSet与Set关系
例:(无序且不可重复)
HashSet 是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于: hashCode 与 equals 方法。
HashSet默认初始化容量16,负载因子0.75f。
迭代此集合需要的时间与HashSet
实例的大小(元素数量)加上后备HashMap
实例的“容量”(桶数)之和成比例。因此,如果迭代性能很重要,则不要将初始容量设置得太高(或负载因子太低)。
Object类中的hashCode()的方法是所有子类都会继承这个方法,这个方法会用Hash算法算出一个Hash值返回,HashSet会用Hash值去和数组长度取模,(这个模就是对象要存放在数组中的位置)模相同时才会判断数组中的元素和要加入的对象的内容是否相同,如果不同才会添加进去。
4.2 LinkedHashSet
底层数据结构采用链表和哈希表共同实现,链表保证了元素的顺序与存储顺序一致,哈希表保证了元素的唯一性。线程不安全,效率高。
例:
4.3 TreeSet
TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。
TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0。
4.3.1 TreeSet与Collection关系
5. Comparable 和 Comparator 比较
5.1 Comparable 简介
Comparable 是排序接口。
若一个类实现了Comparable接口,就意味着“该类支持排序”。 即然实现Comparable接口的类支持排序,假设现在存在“实现Comparable接口的类的对象的List列表(或数组)”,则该List列表(或数组)可以通过 Collections.sort(或 Arrays.sort)进行排序。
此外,“实现Comparable接口的类的对象”可以用作“有序映射(如TreeMap)”中的键或“有序集合(TreeSet)”中的元素,而不需要指定比较器。
5.1.1Comparable 定义
Comparable 接口仅仅只包括一个函数,它的定义如下:
package java.lang;
import java.util.*;
public interface Comparable<T> {
public int compareTo(T o);
}
说明:
假设我们通过 x.compareTo(y) 来“比较x和y的大小”。若返回“负数”,意味着“x比y小”;返回“零”,意味着“x等于y”;返回“正数”,意味着“x大于y”。
5.2 Comparator 简介
Comparator 是比较器接口。
我们若需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口);那么,我们可以建立一个“该类的比较器”来进行排序。这个“比较器”只需要实现Comparator接口即可。
也就是说,我们可以通过“实现Comparator类来新建一个比较器”,然后通过该比较器对类进行排序。
5.2.1 Comparator 定义
Comparator 接口仅仅只包括两个个函数,它的定义如下:
package java.util;
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}
说明:
(1) 若一个类要实现Comparator接口:它一定要实现compare(T o1, T o2) 函数,但可以不实现 equals(Object obj) 函数。
为什么可以不实现 equals(Object obj) 函数呢? 因为任何类,默认都是已经实现了equals(Object obj)的。 Java中的一切类都是继承于java.lang.Object,在Object.java中实现了equals(Object obj)函数;所以,其它所有的类也相当于都实现了该函数。
(2) int compare(T o1, T o2) 是“比较o1和o2的大小”。返回“负数”,意味着“o1比o2小”;返回“零”,意味着“o1等于o2”;返回“正数”,意味着“o1大于o2”。
5.3 Comparator 和 Comparable 比较
Comparable是排序接口;若一个类实现了Comparable接口,就意味着“该类支持排序”。
而Comparator是比较器;我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。
我们不难发现:Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。
6. 关于排序与重复元素的说明
6.1 排序
6.1.1 自然排序
自然排序使用要排序元素的CompareTo(Object obj)方法来比较元素之间大小关系,然后将元素按照升序排列。
Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现了该接口的对象就可以比较大小。
obj1.compareTo(obj2)方法如果返回0,则说明被比较的两个对象相等,如果返回一个正数,则表明obj1大于obj2,如果是 负数,则表明obj1小于obj2。
如果我们将两个对象的equals方法总是返回true,则这两个对象的compareTo方法返回应该返回0
例:
6.1.2 定制排序
自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现 int compare(T o1,T o2)方法
例:(对象排序)
public class Tree{
public static void main(String[] args) {
Set<Person> person = new TreeSet<Person>();
Person p1 = new Person("张三", 21);
Person p2 = new Person("李四", 19);
Person p3 = new Person("王五", 19);
person.add(p1);
person.add(p2);
person.add(p3);
System.out.println(person);// [Person{name='李四', age=19}, Person{name='王五', age=19}, Person{name='张三', age=21}]
}
static class Person implements Comparable<Person>{
private String name;
private int age;
@Override
public int compareTo(Person p) {
if (this.age > p.age){
return 1;
}else if (this.age < p.age){
return -1;
}else {
// 当年龄相同时,按字符串进行排序
return this.name.compareTo(p.name);
}
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
}
}
6.2 重复元素
使用 Comparable 完成的对于重复元素的判断,那么 Set 接口定义的时候本身就是不允许重复元素的,那么证明 如果现在真的是有重复元素的话,使用 HashSet 也同样可以进行区分。
例:
public class Hash {
public static void main(String[] args) {
Set<Person> person = new HashSet<Person>();
Person p1 = new Person("张三", 21);
Person p2 = new Person("李四", 19);
Person p3 = new Person("王五", 19);
Person p4 = new Person("李四", 19);
person.add(p1);
person.add(p2);
person.add(p3);
person.add(p4);
System.out.println(person);// [Person{name='王五', age=19}, Person{name='张三', age=21}, Person{name='李四', age=19}]
}
static class Person{
private String name;
private int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
}
}
如果要想判断两个对象是否相等,则有两种方法可以完成:
第一种判断两个对象的编码是否一致,这个方法需要通过 hashCode()完成,即:每个对象有唯一的编码
还需要进一步验证对象中的每个属性是否相等,需要通过 equals()完成。
6.3 排序与重复总结
关于 TreeSet 的排序实现,如果是集合中对象是自定义的或者说其他系统定义的类没有实现 Comparable 接口,则不能实现 TreeSet 的排序,会报类型转换(转向 Comparable 接口)错误。 换句话说要添加到 TreeSet 集合中的对象的类型必须实现了 Comparable 接口。
不过 TreeSet 的集合因为借用了 Comparable 接口,同时可以去除重复值,而 HashSet 虽然是 Set 接口子类,但是对于没有复写 Object 的 equals 和 hashCode 方法的对象,加入了 HashSet 集合中也是不能去掉重复值的。
7. List和Set总结:
(1)List,Set都是继承自Collection接口,Map则不是
(2)List特点:元素有放入顺序,元素可重复 ,Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉
注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的,加入Set 的Object必须定义equals()方法 ,另外list支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值
(3)Set和List对比:
Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
(4)ArrayList与LinkedList的区别和适用场景
Arraylist:
优点:ArrayList是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作效率会比较高(在内存里是连着放的)。
缺点:因为地址连续, ArrayList要移动数据,所以插入和删除操作效率比较低。
LinkedList:
优点:LinkedList基于链表的数据结构,地址是任意的,所以在开辟内存空间的时候不需要等一个连续的地址,对于新增和删除操作add和remove,LinedList比较占优势。LinkedList 适用于要头尾操作或插入指定位置的场景
缺点:因为LinkedList要移动指针,所以查询操作性能比较低。
适用场景分析:
当需要对数据进行对此访问的情况下选用ArrayList,当需要对数据进行多次增加删除修改时采用LinkedList。
ArrayList与Vector的区别和适用场景
ArrayList有三个构造方法:
public ArrayList(int initialCapacity) //构造一个具有指定初始容量的空列表。
public ArrayList() //默认构造一个初始容量为10的空列表。
public ArrayList(Collection<? extends E> c) //构造一个包含指定 collection 的元素的列表
Vector有四个构造方法:
public Vector()//使用指定的初始容量和等于0的容量增量构造一个空向量。
public Vector(int initialCapacity)//构造一个空向量,使其内部数据数组的大小,其标准容量增量为零。
public Vector(Collection<? extends E> c)//构造一个包含指定 collection 中的元素的向量
public Vector(int initialCapacity,int capacityIncrement)//使用指定的初始容量和容量增量构造一个空的向量
ArrayList和Vector都是用数组实现的,主要有这么三个区别:
-
Vector是多线程安全的,线程安全就是说多线程访问同一代码,不会产生不确定的结果。而ArrayList不是,这个可以从源码中看出,Vector类中的方法很多有synchronized进行修饰,这样就导致了Vector在效率上无法与ArrayList相比;
-
两个都是采用的线性连续空间存储元素,但是当空间不足的时候,两个类的增加方式是不同。
-
Vector可以设置增长因子,而ArrayList不可以。
-
Vector是一种老的动态数组,是线程同步的,效率很低,一般不赞成使用。
适用场景分析:
-
Vector是线程同步的,所以它也是线程安全的,而ArrayList是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用ArrayList效率比较高。
-
如果集合中的元素的数目大于目前集合数组的长度时,在集合中使用数据量比较大的数据,用Vector有一定的优势。
HashSet与TreeSet的区别和适用场景
-
TreeSet 是二叉树(红黑树的树据结构)实现的,TreeSet中的数据是自动排好序的,不允许放入null值。HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束
-
.HashSet要求放入的对象必须实现HashCode()方法,放入的对象,是以hashcode码作为标识的,而具有相同内容的String对象,hashcode是一样,所以放入的内容不能重复。但是同一个类的对象可以放入不同的实例
-
HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。为快速查找而设计的Set,我们通常都应该使用HashSet,在我们需要排序的功能时,我们才使用TreeSet。
8. Iterator
在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口 java.util.Iterator 。 Iterator 接口也是Java集合中的一员,但它与 Collection 、 Map 接口有所 不同, Collection 接口与 Map 接口主要用于存储元素,而 Iterator 主要用于迭代访问(即遍历) Collection 中的元素,因此 Iterator 对象也被称为迭代器。
例:
8.1 ListIterator(双向输出的迭代接口)
如果要想使用 ListIterator 接口,则必须依靠 List 接口进行实例化
例:
8.2 废弃的接口:Enumeration(了解)
使用 Enumeration 输出,必须使用 Vector 类完成
例:
8.3 增强for(for each循环)
专门用来遍历数组和集合的高级for循环,内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作
例:
tips: 新for循环必须有被遍历的目标。目标只能是Collection或者是数组。新式for仅仅作为遍历操作出现。
9. Map接口
9.1 概述
Map用于保存具有映射关系的数据,Map里保存着两组数据:key和value,它们都可以使任何引用类型的数据,但key不能重复。所以通过指定的key就可以取出对应的value。里面的所有内容都按照 key—>value 的形式保存,也称为二元偶对象。
9.2 Map接口主要方法
Map 本身是一个接口,所以一般会使用以下的几个子类:HashMap、TreeMap、Hashtable
9.3 HashMap
9.3.1 概述
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步。
HashMap 是无序的,即不会记录插入的顺序。
HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口。
9.3.2 HashMap与Map关系
例:
9.4 Hashtable
操作的时候,可以发现与 HashMap 基本上没有什么区别,而且本身都是以 Map 为操作标准的,所以操作的结果形式 都一样。但是 Hashtable 中是不能向集合中插入 null 值的
9.4.1 Hashtable与Map关系
9.4.2 HashMap 与 Hashtable 的区别
No. | 区别点 | HashMap | Hashtable |
---|---|---|---|
1 | 推出时间 | JDK 1.2 之后推出的,新的操作类 | JDK 1.0 时推出的,旧的操作类 |
2 | 性能 | 异步处理,性能较高 | 同步处理,性能较低 |
3 | 是否能设置null值 | 允许设置为 null | 不允许设置,否则将出现空指向异常 |
9.5 TreeMap
TreeMap 是一个有序的key-value集合,它是通过红黑树实现的。
TreeMap 子类是允许 key 进行排序的操作子类,其本身在操作的时候将按照 key 进行排序,另外,key 中的内容可以 为任意的对象,但是要求对象所在的类必须实现 Comparable 接口。
9.5.1 基于Map接口扩展的方法
9.5.2 TreeMap与Map关系
例:
9.6 Map的其它类
9.7 HashMap与TreeMap总结
HashMap :
非线程安全,基于哈希表实现。使用HashMap要求添加的键类明确定义了hashCode()和equals()[可以重写hashCode()和equals()],为了优化HashMap空间的使用,您可以调优初始容量和负载因子。
TreeMap:
非线程安全,基于红黑树实现。TreeMap没有调优选项,因为该树总处于平衡状态。
适用场景分析:
HashMap和HashTable:
HashMap去掉了HashTable的contains方法,但是加上了containsValue()和containsKey()方法。HashTable同步的,而HashMap是非同步的,效率上比HashTable要高。HashMap允许空键值,而HashTable不允许。
HashMap:
适用于Map中插入、删除和定位元素。
Treemap:
适用于按自然顺序或自定义顺序遍历键(key)。
9.8 Map集合的输出
因为Map接口没有Iterator( )方法的定义,因此输出需按如下步骤进行:
- 使用 Map 接口中的 entrySet()方法将 Map 接口的全部内容变为 Set 集合
- 可以使用 Set 接口中定义的 iterator()方法为 Iterator 接口进行实例化
- 之后使用 Iterator 接口进行迭代输出,每一次的迭代都可以取得一个 Map.Entry 的实例
- 通过 Map.Entry 进行 key 和 value 的分离
Map.Entry 本身是一个接口。此接口是定义在 Map 接口内部的,是 Map 的内部接口。此内部接口使用 static 进行定义, 所以此接口将成为外部接口。
实际上来讲,对于每一个存放到 Map 集合中的 key 和 value 都是将其变为了 Map.Entry 并且将 Map.Entry 保存在了 Map 集合之中。
10. 两种关系
10.1 一对多关系
例:
public class Student {
private String name;
private int age;
private School school;
@Override
public String toString() {
return "学生信息:" +
"姓名:" + name +
" 年龄:" + age ;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public School getSchool() {
return school;
}
public void setSchool(School school) {
this.school = school;
}
}
-----------------------------------------------------------------------------------------
import java.util.ArrayList;
import java.util.List;
public class School {
private String name;
private List<Student> allStudent;
public School() {
allStudent = new ArrayList<Student>();
}
public School(String name) {
this();
this.name = name;
}
@Override
public String toString() {
return "学校信息:" +
"学校名称:" + name;
}
public List<Student> getAllStudent() {
return allStudent;
}
public void setAllStudent(List<Student> allStudent) {
this.allStudent = allStudent;
}
}
-----------------------------------------------------------------------------------------
import java.util.Iterator;
public class Main {
public static void main(String[] args) {
Student stu1 = new Student("张三",18);
Student stu2 = new Student("李四",19);
Student stu3 = new Student("王五",17);
School sch1 = new School("JAVA");
sch1.getAllStudent().add(stu1);
stu1.setSchool(sch1);
stu2.setSchool(sch1);
stu3.setSchool(sch1);
sch1.getAllStudent().add(stu2);
sch1.getAllStudent().add(stu3);
System.out.println(sch1);
Iterator<Student> iter = sch1.getAllStudent().iterator();
while (iter.hasNext()){
System.out.println(iter.next());
}
}
}
输出结果:
10.2 多对多关系
例:
import java.util.ArrayList;
import java.util.List;
public class Student1 {
private String name;
private int age;
private List<Course> allCourses;
public Student1() {
allCourses = new ArrayList<Course>();
}
@Override
public String toString() {
return "学生信息:" +
" 姓名:" + name + " 年龄:" + age;
}
public List<Course> getAllCourses() {
return allCourses;
}
public void setAllCourses(List<Course> allCourses) {
this.allCourses = allCourses;
}
public Student1(String name, int age) {
this();
this.name = name;
this.age = age;
}
}
-----------------------------------------------------------------------------------------
import java.util.ArrayList;
import java.util.List;
public class Course {
private String name;
private int score;
private List<Student1> allStudent;
public Course() {
allStudent = new ArrayList<Student1>();
}
public Course(String name, int score) {
this();
this.name = name;
this.score = score;
}
@Override
public String toString() {
return "课程信息:" +
" 名称:" + name +
" 学分:" + score;
}
public List<Student1> getAllStudent() {
return allStudent;
}
public void setAllStudent(List<Student1> allStudent) {
this.allStudent = allStudent;
}
}
-----------------------------------------------------------------------------------------import java.util.Iterator;
public class Main {
public static void main(String[] args) {
Student1 stu1 = new Student1("张三",18);
Student1 stu2 = new Student1("李四",19);
Student1 stu3 = new Student1("王五",17);
Course course1 = new Course("Oracle",1);
Course course2 = new Course("JAVA",3);
course1.getAllStudent().add(stu1);
course1.getAllStudent().add(stu2);
stu1.getAllCourses().add(course1);
stu2.getAllCourses().add(course1);
course2.getAllStudent().add(stu1);
course2.getAllStudent().add(stu2);
course2.getAllStudent().add(stu3);
stu1.getAllCourses().add(course2);
stu2.getAllCourses().add(course2);
stu3.getAllCourses().add(course2);
System.out.println(course1);
Iterator<Student1> iterator1 = course1.getAllStudent().iterator();
while (iterator1.hasNext()){
System.out.println(iterator1.next());
}
System.out.println("---------------------");
System.out.println(stu1);
Iterator<Course> iterator2 = stu1.getAllCourses().iterator();
while (iterator2.hasNext()){
System.out.println(iterator2.next());
}
}
}
输出结果:
11. Collections 类
从实际考虑,使用此类操作并不是很方便,最好的做法就是使用各个接口的直接操作的方法完成。此类只是 一个集合的操作类
常用方法举例:
12. hashCode()方法
java.lnag.Object 中对 hashCode 的约定(很重要):
- 在一个应用程序执行期间,如果一个对象的 equals 方法做比较所用到的信息没有被修改的话,则对该对象调用 hashCode 方法多次,它必须始终如一地返回同一个整数。
- 如果两个对象根据 equals(Object o)方法是相等的,则调用这两个对象中任一对象的 hashCode 方法必须产生相同的整 数结果。
- 如果两个对象根据 equals(Object o)方法是不相等的,则调用这两个对象中任一个对象的 hashCode 方法,不要求产生 不同的整数结果。但如果能不同,则可能提高散列表的性能。
在 java 的集合中,判断两个对象是否相等的规则是:
-
判断两个对象的 hashCode 是否相等
如果不相等,认为两个对象也不相等,完毕
如果相等,转入equals()
(这一点只是为了提高存储效率而要求的,其实理论上没有也可以,但如果没有,实际使用时效率会大大降低,所以我们 这里将其做为必需的。后面会重点讲到这个问题。)
-
判断两个对象用 equals 运算是否相等 如果不相等,认为两个对象也不相等 如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键)
13. 集合内存泄漏分析
当一个对象被存进 HashSet 集合后,就不能修改这个对象中的那些参与计算的哈希值的字段了,否则,对象被修改后的哈 希值与最初存储进 HashSet 集合中时的哈希值就不同了,在这种情况下,即使在 contains 方法使用该对象的当前引用作为 的参数去 HashSet 集合中检索对象,也将返回找不到对象的结果,这也会导致无法从 HashSet 集合中删除当前对象,从而 造成内存泄露。
引用:
java集合超详解
Java 集合系列目录(Category)
Java 中 Comparable 和 Comparator 比较