文章目录
一、类集概述(重点)
Java中类集是对各种数据结构成熟的实现。
类集中最大的几个常用操作父接口:Collection、Map、Iterator,这三个接口为以后要使用的最重点的接口。
所有类集操作的接口或类都在java.util包中。
二、链表与二叉树
1、链表
链表(Linked List):链表是由一组连续或非连续的内存结构(节点)组成,按特定的顺序链接在一起的抽象数据类型。
eg:
补充:
抽象数据类型(Abstract Data Type [ADT]):表示数学中抽象出来的一些操作的集合。
内存结构:内存中的结构,如:struct、特殊内存块…等等之类。
数组和链表的区别和优缺点:
数组是一种连续存储线性结构,元素类型相同,大小相等。
数组的优点:存取速度快。
数组的缺点:
①事先必须知道数组的长度;
②插入删除数据很慢(效率低);
③空间通常是有限制的;
④需要大块连续的内存块;
链表是离散存储线性结构。
链表的优点:没有空间限制,插入删除元素很快。
链表的缺点:存取速度慢。
//链表节点
class Node {
Object data; //存放数据
Node next; //指向下一个节点
}
链表常用的有3类:单链表、双向链表、循环链表。
2、二叉树
二叉树是树的一种,每个节点最多可具有两个子树,即节点的度最大为2(节点度:节点所拥有的子树数)。
eg:
二叉树的优点:查找方便(类二分查找级)
通常二叉树都是有序的,根节点向下分叉,存储数据与根节点作比较,比根节点小的存至左子节点,反之存至右子节点。在之后每存储一个数据就按这个存储顺序依次向下分叉。
- 二叉树的遍历方式
- 先序遍历:先访问根节点,然后访问左节点,最后访问右节点。(根->左->右)
- 中序遍历:先访问左节点,然后访问根节点,最后访问右节点。(左->根->右)
- 后序遍历:先访问左节点,然后访问右节点,最后访问根节点。(左->右->根)
三、常见数据结构
数据存储常用结构有:栈、队列、数组、链表和红黑树。
1、栈
栈(stactk,又称堆栈):栈是限定仅在表尾进行插入和删除操作的线性表。通常把允许插入和删除的一端称为栈顶,另一端为栈底。
采用该结构的集合,对元素的存取有如下的特点:
① 先进后出,后进先出。
② 栈中数据的出入口都在栈的顶端。
需要注意的是:
- 压栈:存元素。
- 弹栈:取元素。
2、队列
队列(Queue):队列是一种特殊的线性表,是运算受限的一种线性表,只允许在表的一端进行插入,而在另一端删除元素的线性表。队尾(rear)是允许插入的一端,队头(front)是允许删除的一端。
采用该结构的集合,对元素的存取有如下的特点:
① 先进先出,后进后出。
② 队列的入口、出口各占一侧。
3、数组
数组(Array):是有序的元素序列,数组是内存中开辟一段连续的空间,并在此空间存放元素。
采用此集合有如下特点:
①查找元素快:通过索引,可以快速访问指定位置的元素。
②增删元素慢。
4、链表
略。
5、红黑树
红黑树(Red Black Tree):是一种自平衡二叉树。
特点:
① 尽量保持平衡,不会将数据只存在一端。
② 速度特快,趋近于平衡,查找叶子元素最少和最多次数不多于二倍。
四、Collection接口(重点)
在整个Java类集中Collection是最大的单值存储(每次存储只能存储一个对象)父接口,在开发中通常只用List接口、Set接口(因它俩继承了Collection接口(意味着继承了其所有方法))。
Collection接口定义如下:
public interface Collection extends Iterable
Collection接口常用方法如下:
1、List接口(重点)
在整个集合中List是Collection的子接口,里面的所有内容都是允许重复的。
List子接口的定义如下:
public interface List extends Collection
List接口对Collection接口方法的扩充如下:
常用的实现类:ArrayList(95%)、Vector(4%)、LinkedList(1%)。
1-1、ArrayList(重点)
ArrayList是List接口的子类。
ArrayList类的定义如下:
public class ArrayList extends AbstractList implements List,RandomAccess,Cloneable,Serializable
此类继承了AbstractList类。AbstractList是List接口的子类。AbstractList是个抽象类,适配器设计模式。
示例代码:
import java.util.ArrayList;
import java.util.List;
public class _1_CollectionListArrayList {
public static void main(String[] args) {
List<String> all = new ArrayList<>();
all.add("A");
all.add("B");
all.add("C");
all.add("D"); //向集合中插入一个元素
all.remove("C");//从集合中删除一个对象
all.remove(1); //删除指定位置的内容
//第一种打印方式
for(int i = 0;i < all.size();i++){
System.out.println(all.get(i)); //根据指定位置取出每一个元素,打印输出。
}
//第二种打印方式(增强for循环打印输出)
for (String s : all) {
System.out.println(s);
}
}
}
1-2、Vector(重点)
与ArrayList一样,Vector本身也属于List接口的子类。
Vector类的定义如下:
public class Vector extends AbstractList implements List,RandomAccess,Cloneable,Serializable
此类与ArrayList类一样,都是AbstractList的子类。所以,此时的操作只要是List接口的子类就都按照List进行操作。
示例代码:
import java.util.List;
import java.util.Vector;
public class _2_CollectionListVector {
public static void main(String[] args) {
List<String> all = new Vector<>();
all.add("A");
all.add("B");
all.add("C");
all.add("D");
all.add("E");
all.remove("E");
all.remove(3);
for (String s : all) {
System.out.println(s);
}
}
}
1-3、Vector类和ArrayList类的区别(重点)
No. | 区别点 | ArrayList | Vector |
---|---|---|---|
1 | 时间 | 是新的类,在JDK1.2之后推出的 | 是旧的类,在JDK1.0的时候就定义了 |
2 | 性能 | 性能较高,是采用了异步处理 | 性能较低,是采用了同步处理 |
3 | 输出 | 支持Iterator、ListIterator输出 | 除了支持Iterator、ListIterator输出,还支持Enumeration输出 |
1-4、链表操作类:LinkedList(理解)
此类的使用率非常低。
LinkedList类的定义如下:
public class LinkedList AbstractSequentialList implements List, Deque,Cloneable,Serializable
此类继承了AbstractList,所以是List的子类。此类也是Queue接口的子类,Queue接口定义了如下方法:
2、Set接口(重点)
Set接口也是Collection的子接口,与List接口不同的是Set接口里面的内容是不允许重复的。
Set接口并没有对Collection接口进行扩充,基本上与Collection接口保持一致,此接口没有List接口中定义的get(int index)方法,因此无法使用循环输出。
在此接口中有两个常用的子类:HashSet、TreeSet。
2-1、散列存放:HashSet(重点)
HashSet属于散列的存放类集,里面的内容是无序存放的。
示例代码1:
import java.util.HashSet;
import java.util.Set;
public class _4_CollectionSetHashSet {
public static void main(String[] args) {
Set<Integer> all = new HashSet<>();
all.add(100);
all.add(200);
all.add(300);
all.add(400);
all.add(500);
all.add(600);
System.out.println(all);
}
}
//输出结果:[400, 100, 500, 200, 600, 300](无序)
示例代码2:借助对象数组化,循环方式实现Set接口中的内容输出。
import java.util.HashSet;
import java.util.Set;
public class _4_CollectionSetHashSet {
public static void main(String[] args) {
Set<Integer> all = new HashSet<>();
all.add(100);
all.add(200);
all.add(300);
all.add(400);
all.add(500);
all.add(600);
Object[] o = all.toArray(); //将集合变为对象数组
for(int i = 0;i < o.length;i++){
System.out.print(o[i] + "、");
}
}
}
//输出结果:400、100、500、200、600、300、(无序)
示例代码3:凭借指定的泛型类型数组( T[] toArray(T[] a)),循环方式实现Set接口中的内容输出。
import java.util.HashSet;
import java.util.Set;
public class _4_CollectionSetHashSet {
public static void main(String[] args) {
Set<Integer> all = new HashSet<>();
all.add(100);
all.add(200);
all.add(300);
all.add(400);
all.add(500);
all.add(600);
Integer[] arr = all.toArray(new Integer[]{});
for(int i = 0;i < arr.length;i++){
System.out.println(arr[i]);
}
}
}
//输出结果:(无序)
400
100
500
200
600
300
2-2、排序的子类:TreeSet(重点)
与HashSet不同的是,TreeSet本身属于排序的子类。
TreeSet类的定义如下:
public class TreeSet extends AbstractSet implements NavigableSet,Cloneable,Serializable
示例代码:
import java.util.Set;
import java.util.TreeSet;
public class _5_CollectionSetTreeSet {
public static void main(String[] args) {
Set<Integer> all = new TreeSet<>();
all.add(500);
all.add(200);
all.add(300);
all.add(100);
all.add(400);
System.out.println(all);
}
}
//输出结果:[100, 200, 300, 400, 500](无序插入变成了有序存入)
2-3、排序说明(重点)
借助Set接口的TreeSet类排序的功能,实现自定义类Person中成员变量的排序。要实现此类成员排序在类中需实现Comparable接口,并重写CompareTo()方法添加其排序规则即可(实现原理分析源码)。
import java.util.Set;
import java.util.TreeSet;
public class _3_CollectionSetTreeSetSort {
public static void main(String[] args) {
Set<Person> all = new TreeSet<>();
all.add(new Person("小明",23));
all.add(new Person("小张",24));
all.add(new Person("小米",22));
all.add(new Person("小刚",25));
all.add(new Person("小丽",24));
for (Person person : all) {
System.out.println(person);
}
}
}
class Person implements Comparable<Person>{
private String name;
private int 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;
}
@Override
public int compareTo(Person o) {
//按照年龄升序排列,年龄相同则按字典顺序比较名字中首字母大小升序排列(避免了重复年龄不同姓名成员的丢失)
return (age > o.age) ? 1 : ((age == o.age) ? -name.compareTo(o.name) : -1);
//<==>
// if(age > o.age){
// return 1;
// }else if(age < o.age){
// return -1;
// }
//
// return -name.compareTo(o.name);
}
@Override
public String toString() {
return "姓名:" + name + ",年龄:" + age;
}
}
小结:
关于TreeSet的排序实现,若集合中对象是自定义的或其他系统定义的类没有实现Comparable接口,则不能实现TreeSet的排序,执行时会报类型转换错误。此时该类必须实现Comparable接口,并重写CompareTo()方法定排序规则。
因TreeSet的集合借用了Comparable接口,实现了去重复值的效果,而HashSet虽是Set接口子类,但对于没有重写Object的equals和hashCode方法的对象,加入了HashSet集合中也是不能去重复值的。
3、集合输出(重点)
对于集合的输出有多种形式,有如下几种:
- Iterator迭代输出(90%)、ListIterator(5%)、Enumeration(1%)、foreach(4%)
3-1、Iterator(绝对重点)
Iterator属于迭代输出,基本操作原理:是不断的判断是否有下一个元素,有则直接输出。
Iterator接口定义格式如下:
public interface Iterator
此接口规定了以下三个方法:
No. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | boolean hasNext() | 普通 | 是否有下一个元素 |
2 | E next() | 普通 | 取出内容 |
3 | void remove() | 普通 | 删除当前内容 |
通过Collection接口为其进行实例化之后,一定要记住,Iterator中的操作指针是在第一条元素之上,当调用next()方法时,获取当前指针指向的值并向下移动,使用hasNext()可以检查序列中是否还有元素。
示例代码:
import java.util.Collection;
import java.util.Iterator;
import java.util.TreeSet;
public class _1_Iterator {
public static void main(String[] args) {
Collection<String> all = new TreeSet<>();
all.add("A");
all.add("B");
all.add("C");
all.add("D");
Iterator<String> iterator = all.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
使用Iterator输出的时候有一点必须注意,在进行迭代输出的时候,若要删除当前元素,则只能使用Iterator接口中的remove()方法,而不能使用集合中的remove()方法。否则出现未知的错误!
示例代码:
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
public class _2_Iterator {
public static void main(String[] args) {
Set<String> s = new TreeSet<>();
s.add("A");
s.add("B");
s.add("C");
s.add("D");
Iterator<String> i = s.iterator();
//s.remove("B"); //error,ConcurrentModificationException
while (i.hasNext()){
String str = i.next();
if(str.equals("B")){
//s.remove("B"); //error,ConcurrentModificationException
i.remove();
}else {
System.out.println(str);
}
}
}
}
3-2、ListIterator(理解)
ListIterator是可以进行双向输出的迭代接口。
ListIterator接口的定义如下:
public interface ListIterator extends Iterator
此接口是Iterator的子接口,此接口定义了如下方法:
No. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | void add(E e) | 普通 | 增加元素 |
2 | boolean hasNext() | 普通 | 判断是否有下一个元素 |
3 | E next() | 普通 | 取出下一个元素 |
4 | boolean hasPrevious() | 普通 | 判断是否有前一个元素 |
5 | E previous() | 普通 | 取出前一个元素 |
6 | void set(E e) | 普通 | 修改元素的内容 |
7 | int previousIndex() | 普通 | 前一个索引位置 |
8 | int nextIndex() | 普通 | 下一个索引位置 |
示例代码:
import java.util.*;
public class _2_ListIterator {
public static void main(String[] args) {
List<String> all = new ArrayList<>();
all.add("A");
all.add("B");
all.add("C");
ListIterator<String> li = all.listIterator();
while (li.hasNext()){ //判断是否有下一个元素
System.out.println(li.next()); //取出下一个元素,并打印,指针再向下偏移
}
while (li.hasPrevious()){ //判断是否有前一个元素
System.out.println(li.previous()); //取出前一个元素,并打印,指针再向上偏移
}
}
}
3-3、废弃的接口:Enumeration(了解)
Enumeration是一个非常古老的输出接口,其实是一个元老级的输出接口,最早的动态数组使用Vector完成,那么只要是使用了Vector则就必须使用Enumeration进行输出。
Enumeration接口的定义如下:
public interface Enumeration
此接口常用方法如下:
No. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | boolean hasMoreElements() | 普通 | 判断是否有下一个元素 |
2 | E nextElement() | 普通 | 取出当前元素 |
但是,与Iterator不同的是,若要想使用Enumeration输出的话,则还必须使用Vector类完成,在类中定义了如下方法:
public Enumeration elements()
示例代码:
import java.util.Enumeration;
import java.util.Vector;
public class _3_Enumeration {
public static void main(String[] args) {
Vector<String> v = new Vector<>();
v.add("A");
v.add("B");
v.add("C");
v.add("D");
Enumeration<String> e = v.elements();
while (e.hasMoreElements()){
System.out.println(e.nextElement());
}
}
}
3-4、新的支持:foreach(理解)
foreach可以用来输出数组的内容,也可以输出集合中的内容。
示例代码:
import java.util.Set;
import java.util.TreeSet;
public class _4_foreach {
public static void main(String[] args) {
Set<String> s = new TreeSet<>();
s.add("D");
s.add("C");
s.add("A");
s.add("B");
for (String s1 : s) {
System.out.println(s1); //因TreeSet的有序存储的特性,有序输出
}
}
}
注意:在使用foreach输出时,里面的操作泛型要指定具体的类型,这样在输出的时候才更加有针对性。
五、Map接口(重点)
以上的Collection中,每次操作都是一个对象。若要操作两个对象,则必须使用Map接口,类似于以下一种情况:
- 小明 123455
- 小李 234162
若要保存以上的某一组俩值的信息,使用Collection就不便了,所以要使用Map接口,因Map接口都按照Key->value的形式保存,也称为二元偶对象。
Map接口的定义如下:
public interface Map<K,V>
此接口与Collection接口没有任何的关系,是第二大集合操作接口。此接口常用方法如下:
1、新的子类:HashMap(重点)
HashMap是Map的子类。
HashMap类的定义如下:
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>,Cloneable,Serializable
此类继承了AbstractMap类,同时可以被克隆,可以被序列化下来。
示例代码:
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class _1_MapHashMap {
public static void main(String[] args) {
Map<Integer,String> map1 = new HashMap<>();
map1.put(2,"小明"); //向map1集合赋予Key->Value(键值对)为“2,"小明"”的参数。(增加内容)
map1.put(1,"小黑");
map1.clear(); //清空map1集合。
if(map1.isEmpty()){ //判断map1集合为空
System.out.println("map集合为空~");
}
map1.put(null,null); //允许向map集合中赋予null值。
map1.put(5,"小刚");
map1.put(3,"小李");
map1.put(4,"小米");
if(map1.containsKey(3)){ //判断map集合中是否包含指定的Key为3。
System.out.println("Yes");
}
if(map1.containsValue("小米")){ //判断map集合中是否包含指定的Value为"小明"。
System.out.println("Yes");
}
//将Map转为Set集合,即可实现迭代(意味着可实现迭代打印输出)
Set<Map.Entry<Integer,String>> e = map1.entrySet();
for (Map.Entry<Integer, String> integerStringEntry : e) {
System.out.println(integerStringEntry);
//3=小刚
//4=小李
//5=小米
}
System.out.println(map1.get(2)); //获取key为2的Value,并打印输出:null。
Set<Integer> s = map1.keySet(); //将map集合中的所有Key变为Set集合,赋给s。
Collection<String> c = map1.values();//将map集合中的所有Value变为Collection集合,赋给c
for (int i = 0;i < s.size();i++){
System.out.println(s.toArray()[i] + " -> " + c.toArray()[i]);
// 3 -> 小李
// 4 -> 小米
// 5 -> 小刚
}
Map<Integer,String> map2 = new HashMap<>();
map2.put(2,"B");
map2.put(1,"A");
map1.(map2); //向map1集合中增加一组集合map2(拼接)。
System.out.println(map1 + " " + map2); //{1=A, 2=B, 3=小李, 4=小米, 5=小刚} {1=A, 2=B}
map1.remove(4); //删除Key为4的Value。
System.out.println(map1); //{1=A, 2=B, 3=小李, 5=小刚}
}
}
2、旧的子类:Hashtable(重点)
Hashtable是一个最早的Key->Value操作类,本身是在JDK1.0时推出的。其基本操作与HashMap是类似的。
Hashtable类的定义如下:
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>,Cloneable,java.io.Serializable
示例代码:
import java.util.Hashtable;
import java.util.Map;
public class _2_MapHashtable {
public static void main(String[] args) {
Map<Integer,String> map1 = new Hashtable<>();
map1.put(3,"C");
map1.put(1,"A");
map1.put(4,"D");
map1.put(2,"B");
//map1.put(5,null); //不允许向Hashtable中赋予null,会报空指针异常!
System.out.println(map1); //{4=D, 3=C, 2=B, 1=A}
}
}
3、HashMap 与 Hashtable 的区别(重点)
在整个集合中除了ArrayList和Vector的区别之外,另外一个最重要的区别是HashMap与Hashtable的区别。
No. | 区别点 | HashMap | Hashtable |
---|---|---|---|
1 | 推出时间 | JDK1.2之后推出的,新的操作类 | JDK1.0时推出的,旧的操作类 |
2 | 性能 | 异步处理,性能较高 | 同步处理,性能较低 |
3 | null | 运行设置为null | 不允许设置,否则将出现空指针异常 |
4 | 存储顺序 | 以Key的大小排列,顺序排列 | 以Key的大小排列,倒序排列 |
4、排序的子类:TreeMap(理解)
TreeMap子类是允许Key进行排序的操作子类,其本身在操作的时候将按Key进行排序,另外,Key中的内容可以是任意的对象,但是要求对象所在的类必须实现Comparable接口。
TreeMap类的定义如下:
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>,Cloneable,java.io.Serializable
示例代码:
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class _3_MapTreeMap {
public static void main(String[] args) {
Map<Integer,String> map1 = new TreeMap<>();
map1.put(3,"C");
map1.put(1,"A");
map1.put(2,"B");
System.out.println(map1); //{1=A, 2=B, 3=C}
Map<Student,Integer> map2 = new TreeMap<>();
map2.put(new Student("小明",12),65);
map2.put(new Student("小张",15),90);
map2.put(new Student("小雷",14),99);
map2.put(new Student("小李",13),97);
map2.put(new Student("小刚",14),82);
Set<Student> stu = map2.keySet(); //得到全部的Key,赋给stu
Iterator<Student> itr = stu.iterator(); //
while (itr.hasNext()){
Student i = itr.next();
System.out.println(i + " -> " + map2.get(i) + "分");
}
}
}
class Student implements Comparable<Student>{
private String name;
private int age;
public Student() {
}
public Student(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;
}
@Override
public String toString() {
return "姓名:" + name + ",年龄:" + age;
}
@Override
public int compareTo(Student o) {
//设置排序规则:返回比较的参数。
return (age > o.age) ? 1 : (age == o.age) ? name.compareTo(o.name) : -1;
}
}
5、关于Map集合的输出
在Collection接口中,可以使用iterator()方法为Iterator接口实例化,并进行输出操作,但是在Map接口中并没有此方法的定义,所以Map接口本身不能使用Iterator进行输出。
因Map接口中存放的每一个内容都是一对值,而Iterator接口输出的时候,每次取出的都是一个完整的对象。若要使用,按照以下步骤完成:
- 使用Map接口中的entrySet()方法将Map接口的全部变成Set集合。
- 使用Set接口中定义的iterfator()方法为Iterator接口进行实例化。
- 使用Iterator接口进行迭代输出,每一个的迭代都取出一个Map.Entry的实例。
- 通过Map.Entry进行Key和Value的分离。
浮点数
Map.Entry是一个接口,此接口定义在Map接口内部,是Map的内部接口。此内部接口被static定义,所以此接口将成为外部接口。
对于每一个存放到Map集合中的Key和Value都是变成了Map.Entry并将Map.Entry保存在了Map集合之中。
在Map.Eantry接口中以下的方法最为常用:
No. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | K getKey( ) | 普通 | 得到 key |
2 | V getValue( ) | 普通 | 得到 value |
示例代码:使用Iterator输出Map接口(常用模板)
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set
public class MapOutDemo{
public static void main(String[] args){
Map<String,String> map = new HashMap<>();
map.put("a","A");
map.put("c","C");
map.put("b","B");
Set<Map.Entry<String,String>> set = map.entrySet(); //变为Set实例
Iterator<Map.Entry<String,String>> iter = set.iterator();
while(iter.HasNext()){
Map.Entry<String,String> m = iter.next;
System.out.println(m.getKey() + " -> " + m.getValue());
}
}
}
六、两种关系(理解)
使用类集,除了可以清楚的表示出动态数组的概念及各个数据结构的操作之外,也可以表示出以下的两种关系。
1、第一种关系:一对多关系
eg:一个学校有多个学生,是一个典型的一对多的关系。
示例代码:
① 定义学生类,一个学生属于一个学校
package com.listdemo.casedemo01;
public class Student{
private String name;
private int age;
private School school;
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;
}
public String toString(){
return "学生信息" + "\n" + "\t|- 姓名:" + this.name + "\n" + "\t|- 年龄:" + this.age;
}
}
② 定义学校类,一个学校有多个学生
package com.listdemo.casedemo01;
import java.util.ArrayList;
import java.util.List;
public class School{
private String name;
private List<Student> allStudents = null;
public School(){
this.allStudents = new ArrayList<Student>();
}
public School(String name){
this();
this.name = name;
}
public List<Student> getAllStudents(){
return allStudents;
}
public String toString(){
return "学校信息:" + "\n" + "\t- 学校名称:" + this.name;
}
}
③ 主方法建立以上两者的关系
package com.listdemo.casedemo01;
import java.util.Iterator;
public class TestDemo{
public static void main(String[] args){
Student stu1 = new Student("小A",10);
Student stu2 = new Student("小B",11);
School sch = new School("LAMP JAVA");
sch.getAllStudents().add(stu1); //一个学校有多个学生
stu1.setSchool(sch); //一个学生属于一个学校
sch.getAllStudents().add(stu2); //一个学校有多个学生
stu2.setSchool(sch); //一个学生属于一个学校
System.out.println(sch);
Iterator<Student> iter = sch.getAllStudents().iterator();
while(iter.hasNext()){
System.out.println(iter.next());
}
System.out.println(stu1.getSchool());
}
}
2、第二种关系:多对多关系
eg:一个学生可以选择多门课程,一门课程允许有多个学生参加。
示例代码:
① 定义学生类,一个学生可以选择多门课程
package com.listdemo.casedemo02;
import java.util.ArrayList;
import java.util.List;
public class Student{
private String name;
private int age;
private List<Course> allCourses;
public Student(){
this.allCourses = new ArrayList<Course>();
}
public Student(String name,int age){
this();
this.name = name;
this.age = age;
}
public List<Course> getAllCourses(){
return allCourses;
}
public String toString(){
return "学生信息:" + "\n" + "\t- 姓名:" + this.name + "\n" + "\t- 年龄:" + this.age;
}
}
② 定义课程类,一门课程可以有多个学生参加
package com.listdemo.casedemo02;
import java.util.ArrayList;
import java.util.List;
public class Course{
private String name;
private int credit;
private List<Student> allStudents = null;
public Course(){
this.allStudents = new ArrayList<Student>();
}
public Course(String name,int credit){
this();
this.name = name;
this.credit = credit;
}
public List<Student> getAllStudents(){
return allStudents;
}
public String toString(){
return "课程信息:" + "\n" + "\t- 课程名称:" + this.name + "\n" + "\t- 课程学分:" + this.credit;
}
}
③ 主方法建立以上两者的关系
package com.listdemo.casedemo02;
import java.util.Iterator;
public class TestDemo{
public static void main(String[] args){
Student stu1 = new Student("A",10);
Student stu2 = new Student("B",11);
Student stu3 = new Student("C",12);
Course c1 = new Course("Oracle",5);
Course c2 = new Course("Java SE基础",10);
c1.getAllStudents().add(stu1); //参加第一门课程
c1.getAllStudents().add(stu2); //参加第一门课程
stu1.getAllCourse().add(c1); //学生选择课程
stu2.getAllCourse().add(c1); //学生选择课程
c2.getAllStudents().add(stu1); //参加第二门课程
c2.getAllStudents().add(stu2); //参加第二门课程
c2.getAllStudents().add(stu3); //参加第二门课程
stu1.getAllCourse().add(c1); //学生选择课程
stu2.getAllCourse().add(c2); //学生选择课程
stu3.getAllCourse().add(c2); //学生选择课程
System.out.println(c2);
Iterator<Student> iter = c2.getAllStudents().iterator();
while(iter.hasNext()){
System.out.println(iter.next());
}
System.out.println("-----------------------------");
System.out.println(stu1);
Iterator<Course> iters = stu1.getAllCourses().iterator();
while(iters.hasNext()){
System.out.println(iters.next());
}
}
}
七、Collections类(理解)
Collections实际上是一个集合的操作类,此类的定义如下:
public class Collections extends Object
此类与Collection接口没有任何关系。是一个单独存在的类。
示例代码:返回空的List集合
import java.util.Collections;
import java.util.List;
public class CollectionsDemo{
public static void main(String[] args){
List<String> all = Collections.emptyList(); //空的集合
all.add("A");
}
}
使用Collections类返回的空的集合对象,本身是不支持任何的修改操作的,因为所有的方法都没有实现。
示例代码:使用Collections进行增加元素的操作
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionsDemo{
List<String> all = new ArrayList<>();
Collections.addAll(all,"A","B","C"); //向集合增加元素
System.out.println(all);
}
但是,从实际考虑,使用此类操作并不是很方便,最好的做法就是使用各个接口的直接操作的方法完成。此类只是一个集合的操作类。
八、分析equals、hashCode与内存泄漏(理解)
equals的作用:比较两个对象的地址值是否相等
equals()方法在Object类中的定义如下:
public boolean equals(Object obj){
return (this == obj);
}
需注意的是,当String、Math、及Integer、Double等这些封装类在使用equals()方法时,已覆盖了Object类的equals()方法,不再是地址的比较而是内容的比较。
还应注意,Java语言对equals()的要求如下,这些要求必须遵循:
- 对称性:如果x.equals(y)返回“true”,那么y.equals(x)也应返回“true”。
- 自反性:x.equals(x)必须返回“true”。
- 传递性:如果x.equals(y)返回“true”,且y.equals(z)返回“true”,那么z.equals(x)也应返回“true”。
- 一致性:如果x.equals(y)返回“true”,只要x和y内容一直不变,无论你重复x.equals(y)多少次,返回都是“true”。
- 非空性:任何情况下,x.equals(null),永远返回“false”;x.equals(和x不同类型的对象),永远返回“false”。
以上五点是重写equals()方法时,必须遵守的准则。
hashCode()方法,在Object类中的定义如下:
public native int hashCode();
这是一个本地方法,它的实现是根据本地机器相关的。当我们可以在自己写的类中覆盖hashCode()方法,比如:String、Integer、Double等这些类都是覆盖了hashCode()方法的。
java.lang.Object中对hashCode的约定(特重要):
- 在一个应用程序执行期间,如果一个对象的equals方法做比较所用到的信息没有被修改的话,则对该对象调用hashCode方法多次,它必须始终如一地返回同一个整数。
- 如果两个对象根据equals(Object o)方法是相等的,则调用这两个中任一对象的hashCode方法必须产生相同的整数结果。
- 如果两个对象根据equals(Object o)方法是不相等的,则调用这两个对象中任一个对象的hashCode方法,不要求产生不同的整数结果。但如果能不同,则可能提高散列表的性能。
在java的集合中,判断两个对象是否相等的规则是:
(1) 判断两个对象的hashCode是否相等
如果不相等,认为两个对象也不相等,完毕。
如果相等,转入2
(这一点只是为了提高存储效率而要求的,其实理论上没有也可以,但如果没有,实际使用时效率会大大降低,所以我们这里将其作为必需的。)
(2) 判断两个对象用equals运算是否相等
如果不相等,认为两个对象也不相等
如果相等,认为连个对象相等(equals()是判断两个对象是否相等的关键)
提示:
当一个对象被存进HashSet集合后,就不能修改这个对象中的那些参与计算的哈希值的字段了,否则,对象被修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中删除当前对象,从而造成内存泄漏。
总结:
- 类集就是一个动态的对象数组,可以向集合中加入任意多的内容。
- List接口中是允许有重复元素的,Set接口中是不允许有重复元素。
- 所有的重复元素依靠hashCode()和equals进行区分。
- List接口的常用子类:ArrayList、Vector
- Set接口的常用子类:HashSet、TreeSet
- TreeSet是可以排序,一个类的对象依靠Comparable接口排序。
- Map接口中允许存放一对内容,key->value。
- Map接口的子类:HashMap、Hashtable、TreeMap。
关的。当我们可以在自己写的类中覆盖hashCode()方法,比如:String、Integer、Double等这些类都是覆盖了hashCode()方法的。
java.lang.Object中对hashCode的约定(特重要):
- 在一个应用程序执行期间,如果一个对象的equals方法做比较所用到的信息没有被修改的话,则对该对象调用hashCode方法多次,它必须始终如一地返回同一个整数。
- 如果两个对象根据equals(Object o)方法是相等的,则调用这两个中任一对象的hashCode方法必须产生相同的整数结果。
- 如果两个对象根据equals(Object o)方法是不相等的,则调用这两个对象中任一个对象的hashCode方法,不要求产生不同的整数结果。但如果能不同,则可能提高散列表的性能。
在java的集合中,判断两个对象是否相等的规则是:
(1) 判断两个对象的hashCode是否相等
如果不相等,认为两个对象也不相等,完毕。
如果相等,转入2
(这一点只是为了提高存储效率而要求的,其实理论上没有也可以,但如果没有,实际使用时效率会大大降低,所以我们这里将其作为必需的。)
(2) 判断两个对象用equals运算是否相等
如果不相等,认为两个对象也不相等
如果相等,认为连个对象相等(equals()是判断两个对象是否相等的关键)
提示:
当一个对象被存进HashSet集合后,就不能修改这个对象中的那些参与计算的哈希值的字段了,否则,对象被修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中删除当前对象,从而造成内存泄漏。
总结:
- 类集就是一个动态的对象数组,可以向集合中加入任意多的内容。
- List接口中是允许有重复元素的,Set接口中是不允许有重复元素。
- 所有的重复元素依靠hashCode()和equals进行区分。
- List接口的常用子类:ArrayList、Vector
- Set接口的常用子类:HashSet、TreeSet
- TreeSet是可以排序,一个类的对象依靠Comparable接口排序。
- Map接口中允许存放一对内容,key->value。
- Map接口的子类:HashMap、Hashtable、TreeMap。
- Map使用Iterator输出的详细步骤。