Java—集合详解

1. 类集

​类集就是一个动态的对象数组。Java的类集框架使程序处理对象组的方法标准化。

1.1 为什么设置类集?

​普通的对象数组的最大问题在于数组中的元素个数是固定的,不能动态的扩充大小,所以最早的时候可以通过链表实现一个动态对象数组。但是这样做毕竟太复杂了,所以在 Java 中为了方便用户操作各个数据结构, 所以引入了类集的概念,有时候就可以把类集称为 java 对数据结构的实现。

​所有类集操作的接口或类都在java.util包中。

在这里插入图片描述

1.2 Java类集结构图

在这里插入图片描述这里写图片描述Collection集合大纲

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关系

img
例:
在这里插入图片描述

3.5 Vector

Vector 类实现了一个动态数组。和 ArrayList 很相似,但是两者是不同的:

  • Vector 是同步访问的。
  • Vector 包含了许多传统的方法,这些方法不属于集合框架。

Vector 主要用在事先不知道数组的大小,或者只是需要一个可以改变大小的数组的情况。

3.5.1 Vector与Collection关系

img
例:
在这里插入图片描述

3.6 LinkedList

​链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址。

​链表可分为单向链表和双向链表。

​一个单向链表包含两个值: 当前节点的值和一个指向下一个节点的链接。

​Java LinkedList(链表) 类似于 ArrayList,是一种常用的数据容器。

​与 ArrayList 相比,LinkedList 的增加和删除对操作效率更高,而查找和修改的操作效率较低。

以下情况使用 ArrayList :

  • 频繁访问列表中的某一个元素。
  • 只需要在列表末尾进行添加和删除元素操作。

以下情况使用 LinkedList :

  • 你需要通过循环迭代来访问列表中的某些元素。
  • 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。

3.6.1 LinkedList类对List接口与Queue接口进行的扩充

在这里插入图片描述

3.6.2 LinkedList与Collection关系

img
例:
在这里插入图片描述

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关系

img
例:(无序且不可重复)
在这里插入图片描述
​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关系

img

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都是用数组实现的,主要有这么三个区别

  1. Vector是多线程安全的,线程安全就是说多线程访问同一代码,不会产生不确定的结果。而ArrayList不是,这个可以从源码中看出,Vector类中的方法很多有synchronized进行修饰,这样就导致了Vector在效率上无法与ArrayList相比;

  2. 两个都是采用的线性连续空间存储元素,但是当空间不足的时候,两个类的增加方式是不同。

  3. Vector可以设置增长因子,而ArrayList不可以。

  4. Vector是一种老的动态数组,是线程同步的,效率很低,一般不赞成使用。

    适用场景分析:

  5. Vector是线程同步的,所以它也是线程安全的,而ArrayList是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用ArrayList效率比较高。

  6. 如果集合中的元素的数目大于目前集合数组的长度时,在集合中使用数据量比较大的数据,用Vector有一定的优势。


HashSet与TreeSet的区别和适用场景

  1. TreeSet 是二叉树(红黑树的树据结构)实现的,TreeSet中的数据是自动排好序的,不允许放入null值。HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束

  2. .HashSet要求放入的对象必须实现HashCode()方法,放入的对象,是以hashcode码作为标识的,而具有相同内容的String对象,hashcode是一样,所以放入的内容不能重复。但是同一个类的对象可以放入不同的实例

  3. 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关系

img

9.4.2 HashMap 与 Hashtable 的区别

在这里插入图片描述

No.区别点HashMapHashtable
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关系

img
例:
在这里插入图片描述

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( )方法的定义,因此输出需按如下步骤进行:

  1. 使用 Map 接口中的 entrySet()方法将 Map 接口的全部内容变为 Set 集合
  2. 可以使用 Set 接口中定义的 iterator()方法为 Iterator 接口进行实例化
  3. 之后使用 Iterator 接口进行迭代输出,每一次的迭代都可以取得一个 Map.Entry 的实例
  4. 通过 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 的约定(很重要):

  1. 在一个应用程序执行期间,如果一个对象的 equals 方法做比较所用到的信息没有被修改的话,则对该对象调用 hashCode 方法多次,它必须始终如一地返回同一个整数。
  2. 如果两个对象根据 equals(Object o)方法是相等的,则调用这两个对象中任一对象的 hashCode 方法必须产生相同的整 数结果。
  3. 如果两个对象根据 equals(Object o)方法是不相等的,则调用这两个对象中任一个对象的 hashCode 方法,不要求产生 不同的整数结果。但如果能不同,则可能提高散列表的性能。

在 java 的集合中,判断两个对象是否相等的规则是:

  1. 判断两个对象的 hashCode 是否相等

    如果不相等,认为两个对象也不相等,完毕

    如果相等,转入equals()

    (这一点只是为了提高存储效率而要求的,其实理论上没有也可以,但如果没有,实际使用时效率会大大降低,所以我们 这里将其做为必需的。后面会重点讲到这个问题。)

  2. 判断两个对象用 equals 运算是否相等 如果不相等,认为两个对象也不相等 如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键)

13. 集合内存泄漏分析

​当一个对象被存进 HashSet 集合后,就不能修改这个对象中的那些参与计算的哈希值的字段了,否则,对象被修改后的哈 希值与最初存储进 HashSet 集合中时的哈希值就不同了,在这种情况下,即使在 contains 方法使用该对象的当前引用作为 的参数去 HashSet 集合中检索对象,也将返回找不到对象的结果,这也会导致无法从 HashSet 集合中删除当前对象,从而 造成内存泄露。

引用:

java集合超详解
Java 集合系列目录(Category)
Java 中 Comparable 和 Comparator 比较

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值