学编程,来源栈;先学习,再交钱
当前系列: J&C 修改讲义

集合概览

集合是同一类型元素的组合,本身也是一个类/对象,就像一个容器一样,(可以)装着它的元素。

PS:早期的集合元素类型都是Object,但现在我们都使用泛型。(@想一想@:为什么?)

数组就是一种集合。

除此以外,Java和C#还内置了:

  • 存储单个元素的:Collecition,又分为:
    • 有序的、可以根据下标找到元素的:List(类似于数组,但比数组更强大)
    • 没有下标的、元素能不重复的Set

  • 存储键值对的:Map/Dictionary。
    成绩单就是典型的键值对,学生姓名就是,学生成绩就是
    一个班级所有学生成绩的成绩单就可以用Map/Dictionary存储,其特点是键不能重复,值可以重复,通过键就能找到值。

再下面又根据其数据结构,派生出:

  • Hash:哈希表
  • Queue:队列
  • Stack:栈
  • Tree:树
  • Linked:链表

以上粗体为Web开发常用集合。(理解起来觉得抽象的同学,可以先看Java/C#部分的演示)

所有集合中都内置了相应的增删改查等方法。

PS:现在终于可以回答这个问题:为什么实际开发中我没有写过数据结构和算法呢?


迭代器模式

所有的集合都有一个要求:遍历(迭代/枚举)每一个元素。

我们能不能提供一个统一的方法,实现这个功能?

很简单,让所有集合类实现一个接口(Java中Iterable/C#中IEnumerable),在接口中定义一个遍历的方法(Java中iterator()/C#中GetEnumerator()),不就OK了吗?

注意这个接口方法返回的值又是一个接口(Java中Iterator<T>/C#中IEnumerator<T>),它又有两个方法:

  • 迭代/枚举是否已经结束:Java中hasNext()/C#中MoveNext()
  • 给我当前/下一个元素:Java中next()/C#中Current)

这样,我们要遍历任何一个集合的时候,都只需要这样机械地调用:

//拿到集合students的迭代器
Iterator<Person> iterator = students.iterator();
//只要没有遍历结束
while(iterator.hasNext()) {	
	//获取下一个元素
	Person current = iterator.next();
	System.out.println(current.Name);
}

@想一想@:为什么还要引入Iterator,而不直接在Iterable中定义hasNext()和next()呢?

  • 封装:所有遍历的功能实现都交给Iterator去做(单一职责)
  • 重用:依赖于组合,而不是继承


学习前提:Java/C#中集合

ER模型

Entity-Relationship Model:实体关系模型。

是一种流行的、用于将现实世界映射到软件系统的概念模型。具体来说,它由以下三部分组成:

  • 实体:比如一个学生就可以是一个实体,实体由属性组成。
  • 属性:实体的特征,或者构成实体的组成部分。比如“学生”这个实体的属性就可以是:姓名、年龄、性别等
  • 关系:实体和实体之间是如何关联的。


什么是实体?

^_^,good question!

简单理解:实在在存在的物

面向对象的世界里,万物皆对象。但

有些对象是能够映射到现实世界实际存在的物体的,或者拥有实实在在的数据信息的,比如:

  • 学生Student,有姓名、年龄……
  • 课程Major,有名称、课时、难易程度……

他们就是实体。

有些对象则不然,他们没有对应的实体,在系统中作为辅助、运算、工具类存在,比如:生成随机数的Random对象,对应啥?对应一个色子?就算这样,这个色子本身也没有任何数据需要记录,我们只是需要它的运算结果。还有StringBuilder、集合对象……他们就不是实体。

PS:这个问题其实在学了数据库之后更好理解,凡是要存到数据库的,都是实体。

关系:引用/依赖

实体和实体之间,会产生关系。

可以通过一个对象的属性指向另一个/系列的对象来体现,这种组织形式又被称之为:引用/关联/组合(复习:继承是为了重用,重用使用组合

public class Student {
    private Teacher teacher;

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }
}
Student atai = new Student();
Teacher fg = new Teacher();
atai.setTeacher(fg); 

PS:引用(属性依赖)是依赖关系的一种。依赖的概念比引用大,只要一个对象/类使用了另一个对象/类,就算是形成了依赖关系。

除了属性依赖,还可以有:

  • 构造函数依赖:构造函数的参数是另外一个对象
  • 方法依赖:方法的参数是另外一个对象
  • ……

X对X

我们也把对象之间的关联关系概括为(以老师/学生关系为例):

  • 一对一(1:1),1个老师只有1个学生  且 1个学生也只有1个老师 (现实世界:一夫一妻)
  • 一对多(1:n),1个老师有多个学生  且 1个学生只能有1个老师 (现实世界:一个妈妈多个孩子)
  • 多对多(n:n),1个老师有多个学生  且 1个学生可以有多个老师(现实世界:中小学老师学生)

注意:我们上面说的“有”,是规则允许“可以有”“最多有”的意思。比如:

  • 1:1,是一个丈夫可以有一个妻子,实际上他还没有,也OK的(没有1:0这种说法,或者说1:1其实就包含了1:0)
  • 1:n,实质上也可能是某个妈妈只有一个独生子女,但规则允许一个妈妈有多个孩子,那妈妈和孩子之间就是1:n的关系
  • n:n,我就省略了?^_^

表现形式

可以用单个属性引用表示1,集合表示n。比如一个学生多个老师:

关系首先可以分成单向双向,比如:

public class Student {
    private Teacher teacher;

public class Teacher {
    private Student student;

Student引用了Teacher,Teacher也引用了Student,我们就说Student和Teacher之间形成了“双向”引用。

代码演示

构建ER模型,可以灵活使用单向/双向关系:

  • 多对多:最简单,只能双向:
    public class Teacher {
    	private List<Student> students;
    public class Student {
        private List<Teacher> teachers;
    
  • 一对多:一个老师多个学生
    • 单向:
      • 1引用n:
        public class Student {
        	private Teacher teacher;
      • n引用1
        public class Teacher {
        	private List<Student> students;
    • 双向:上述两个单向代码都写上。
  • 一对一:通常使用双向,因为单向能确保是1:1
    public class Student {
    	private Teacher teacher;
    public class Teacher {
    	private Student student;

反向推导

然后,通过对象间引用,我们可以反推:


Student Teacher 可能的关系
双向 Teacher Student 1:1
Teacher IList<Student> 1:n
IList<Teacher> IList<Student> n:n
单向 Teacher 看内容:1:1或1:n
IList<Teacher> 1:n


Repoistory

用户注册,系统应该新生成了一个User对象(包含用户名密码等)

@想一想@:这个对象存放在哪里?假设我们把对象都存储在集合里,这个集合是静态的,还是实例的好?

用户登录,我们要在集合里去检查登录用户的输入是否正确

@想一想@:应该怎么操作?本质上是根据用户名进行检索查找。

某一天被用户注销,我们又该怎么办?当然是在集合中删除这个对象。

OK,现在总结出来,存放、查找删除都要封装成方法,这些方法放在哪里?

三种方案:

  1. User类的实例方法:存放和删除能够接受,但查找……
  2. User类的静态方法:马马虎虎,但是不如
  3. 新建一个专门的UserRepository类:最流行的模式,推荐,@想一想@:为什么?

对应enities,建立专门的repository(仓库类),有以下好处:

  • 职责分明:repository负责对象的存、取、删;entity负责对象的改。
  • repository作为独立的类,可以通过继承、泛型、组合等实现灵活的重用。
class Repository<T>{
	public static List<Object> entities;		//C#可以直接使用T
	
	public void Save(T entity) {
		entities.add(entity);
	}
	
	public void Delete() {
		entities.remove(this);
	}
}
class StudentRepository extends Repository<Student>{
	public boolean Find(String name) {


作业

  1. 让之前的双向链表,能够:被foreach迭代
  2. 新建一个UserRepository,添加方法:
    1. Save(user):可以把user保存到仓库
    2. GetByName(name):可以根据用户名查找到某个用户
    利用上述方法再次实现用户登录LogOn(),控制台输出三种可能的结果:
    • 根据Name没有找到相应的用户:* 用户名不存在
    • 根据Name找到了相应的用户,但密码不对:* 用户名或密码错误
    • 信息正确无误:* 成功登录
  3. 在现有作业的基础上,观察一起帮文章板块,以此为蓝本,补充(如果还没有的话)声明:
    • 评论(Comment)类
    • 评价(Appraise)类:包括“赞(Agree)”和“踩(Disagree)”
    • 关键字(Keyword)类
    并构建以下关系:
    • 一篇文章可以有多个评论
    • 一个评论必须有一个它所评论的文章
    • 每个文章和评论都有一个评价
    • 一篇文章可以有多个关键字,一个关键字可以对应多篇文章
  4. 在之前“文章/评价/评论/用户/关键字”对象模型的基础上,添加相应的数据,然后新建相应的repository类,利用Linq(C#)Stream(Java)完成以下操作:
    1. 找出“飞哥”发布的文章
    2. 找出2019年4月21日以后“小鱼”发布的文章
    3. 按发布时间升序/降序排列显示文章
    4. 统计每个用户各发布了多少篇文章
    5. 找出包含关键字“C#”或“.NET”的文章
    6. 找出评论数量最多的文章
    7. 找出每个作者评论数最多的文章
    8. 找出每个作者最近发布的一篇文章
    9. 找出每一篇求助的悬赏都大于5个帮帮币的求助作者

学习笔记
源栈学历
键盘敲烂,月薪过万作业不做,等于没学

作业

觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 J&C 中继续学习:

多快好省!前端后端,线上线下,名师精讲

  • 先学习,后付费;
  • 不满意,不要钱。
  • 编程培训班,我就选源栈

更多了解 加:

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧

公众号:源栈一起帮

二维码