Fork me on GitHub

Java,python,c#事件机制和常用库比较

本篇博客其实是课后作业,搬过来。

最近刷题,复习Java常用类是突然想起来以前做过的比较,不甚成熟,补充在一起。

作业1

Java,Python和JavaScript中对事件机制的支持语法及使用方法

在c#中

事件在类中声明且生成,且通过使用同一个类或其他类中的委托与事件处理程序关联。

包含事件的类用于发布事件。这被称为 发布器(publisher) 类。其他接受该事件的类被称为 订阅器(subscriber) 类。事件使用 发布-订阅(publisher-subscriber) 模型。

发布器(publisher) 是一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。发布器(publisher)类的对象调用这个事件,并通知其他的对象。

订阅器(subscriber) 是一个接受事件并提供事件处理程序的对象。在发布器(publisher)类中的委托调用订阅器(subscriber)类中的方法(事件处理程序)。

观察者模式是一种常用的设计模式,这种设计模式刚好可以用于事件驱动机制。

Python中的事件机制

参考自博客(https://blog.csdn.net/brucewong0516/article/details/84031715)

监听器(subscriber)监听了事件源(publisher),当事件源发送事件时,所有监听该事件的监听器(subscriber)都会接收到消息并作出响应。

Python中也有event(事件),是线程threading模块中的一个类,它提供了简单的几个方法,set(), clear(), wait(timeout), isSet()。而我们要探讨的事件机制是以下的实现方法。

已知Python中可直接传递函数名,实现类似c#中委托的功能,故可以在注册事件的回调时,代入一个参数callback,在注册函数实体内,存在一个list将callback添加进去,

1
2
def register_callback(self, cb):
self.callbacks.append(cb)

以下是一个稍显复杂的实现方法。通过eventManager可以实现事件触发,当事件触发时,推送事件到线程中运行。eventManager主要包含以下几个方法。

run(), AddEventListener(), RemoveEventListener(), SendEvent()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
from queue import Queue, Empty
from threading import *

class EventManager:
def __init__(self):
self.__eventQueue = Queue()
self.__active = False
self.__thread = Thread(target = self.__Run) #事件处理线程,创建一个线程用于run()
self.__handlers = {}#保存对应事件的响应函数

def __Run(self):
while self.__active == True:
try:
#获取事件的阻塞时间设为1秒
event = self.__eventQueue.get(block = True, timeout = 1)
#发出通知
if event.type_ in self.__handlers:
for handler in self.__handlers[event.type_]:
handler(event)
except Empty:
pass
def Start(self):
self.__active = True
#启动事件处理线程
self.__thread.start()
def Stop(self):
self.__active = False
#等待事件处理进程退出
self.__thread.join()
def AddEventListener(self, type_, handler):
try:
handlerList = self.__handlers[type_]
except KeyError:
handlerList = []

self.handler[type_] = handlerList
if handler not in handlerList:
handlerList.append(handler)
def RemoveEventListener(self, type_, handler):
try:
handlerList = self.__handlers[type_]
if handler in handlerList:
handlerList.remove(handler)
if not handlerList:
del self.handlers[type_]
except KeyError:
pass
def SendEvent(self, event):
self.__eventQueue.put(event)

实例调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import sys
from datetime import datetime
from threading import *
from eventManager import *

#事件名称 新文章
EVENT_ARTICAL = "Event_Artical"
#事件源 公众号
class PublicAccounts:
def __init__(self,eventManager):
self.__eventManager = eventManager

def WriteNewArtical(self):
event = Event(type_=EVENT_ARTICAL)
event.dict["artical"] = u'如何写出更优雅的代码\n'
self.__eventManager.SendEvent(event) #发送事件
print(u'公众号发送新文章\n')

class Listener:
def __init__(self,username):
self.__username = username
#监听器的处理函数 读文章
def ReadArtical(self,event):
print(u'%s 收到新文章' % self.__username)
print(u'正在阅读新文章内容:%s' % event.dict["artical"])
#--------------------------------------------------------------------
def test():
# 实例化监听器
listner1 = Listener("thinkroom") #订阅者1
listner2 = Listener("steve") #订阅者2
eventManager = EventManager()
eventManager.AddEventListener(EVENT_ARTICAL, listner1.ReadArtical)
eventManager.AddEventListener(EVENT_ARTICAL, listner2.ReadArtical)
eventManager.Start()

publicAcc = PublicAccounts(eventManager)
timer = Timer(2, publicAcc.WriteNewArtical)
timer.start()

if __name__ == '__main__':
test()

Java的事件机制

参考自博客(https://www.jianshu.com/p/ccd468c6be8a) (https://www.cnblogs.com/liao13160678112/p/6596218.htm) (https://www.cnblogs.com/yulinfeng/p/5874015.html)

观察者模式实现事件机制

对于观察者模式,Java已经为我们提供了已有的接口和类。可以利用Java提供的Observer接口和Observable类实现观察者模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.util.Observable;
import java.util.Obsever;

public class Subscribe implements Observer{
public Subscribe (Observable o){
o.addObserver(this);
}
public void update(Observable o, Object arg){
System.out.println("收到通知:" + ((Publish)o).getData());
}
}

public class Publish extends Observable{
private String data = "";
public String getData(){ return data; }
public void setData(String data){
if(!this.data.equals(data)){
this.data = data;
setChanged(); //改变通知者的状态
}
notifyObservers(); //调用父类Observable方法,通知所有观察者
}
}

public class Main{
public static void main(String[] args){
Publish publish = new Publish();
Subscribe subscribe = new Subscribe(publish);
publish.setData("start!");
}
}

也可以自己实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Event {
public ArrayList<Callback> callbackList;//与事件相关的事件处理函数
//遍历所有的事件触发通知
public void emit(){
for(Callback cb : callbackList){
cb.run();
}
}
//注册事件处理函数
public registerCallback(Callback cb){
callbackList.add(cb);
}
//移除注册
public removeCallback(Callback cb){
callbackList.remove(cb);
}
}
/*事件处理函数类*/
public interface Callback {
void run();
}
public class OnClick implements Callback {
//函数
public void run(){
System.out.println("you just clicked me!");
}
}
//test
Event e = new Event();
e.registerCallback(new OnClick()); //注册到队列中
e.emit();//do something

观察者模式存在不足之处,两个观察者模式的观察者都是实现了同一接口,如果两个观察者风马牛不相及又该怎么办。参考委托和事件的关系,我们可以用callback实现一个“委托”,即也可以用反射实现事件机制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/*事件处理类*/
public class EventHandler {
//事件源
private Object sender;
//事件处理函数名称(用于反射)
private String callback;
public EventHandler(Object sender, String callback){
this.sender = sender;
this.callback = callback;
}

//事件触发
public void emit(){
Class senderType = this.sender.getClass();
try {
//获取并调用事件源sender的事件处理函数
Method method = senderType.getMethod(this.callback);
method.invoke(this.sender);
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
/*事件源*/
public class Button(){
/*可以在此设置Button类的相关属性,比如名字等*/
private String name;
...

//事件处理函数
public void onClick(){
System.out.println("you just clicked me!");
}
}
/*实现事件驱动机制*/
Button b = new Button();
if(/*收到按钮点击信号*/){
EventHandler e = new EventHandler(b, "onClick");
e.emit();
}

JavaScript的事件机制

参考自博客(https://blog.csdn.net/a2013126370/article/details/82290180)

事件是将JavaScript和网页联系在一起的主要方式。

事件:用户或浏览器自身执行的某种动作,换言之,文档或浏览器发生的一些特定的交互瞬间。

事件处理程序:又称事件侦听器,事件发生时执行的代码段。

事件流:事件流描述的是从页面中接收事件的顺序。

两种基本事件模型

事件冒泡:事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。

事件捕获:事件从最不精确的对象(document 对象)开始触发,然后到最精确。

DOM事件流

同时支持 两种基本事件模型,规定事件流包括三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。

DOM事件处理程序绑定时,程序员可以自己选择绑定事件时采用事件捕获还是事件冒泡。

IE事件流

IE只支持事件冒泡,不支持事件捕获。

事件处理程序绑定方式

DOM事件处理方式,可以通过addEventListener方法

addEventListener(“事件名”,事件处理程序,ture/false:在事件捕获/冒泡阶段调用模型)

对应的事件处理程序移除方法:removeEventListener,参数必须相同。

IE事件处理程序

程序作用域为全局作用域,this指向window对象

添加方法:attachEvent(“on+事件名”,事件处理程序)

移除方法:detachEvent(“on+事件名”,事件处理程序)

综合以上所述,写可跨浏览器的事件处理程序(构造EventUtil对象,为其添加可兼容各浏览器的事件处理方法),如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/*可跨浏览器的事件处理程序
构造EventUtil对象,为其添加可兼容各浏览器的事件处理方法
*/
var EventUtil = {
/*添加事件处理程序*/
addHandler: function (element, type, handler) {
if (element.addEventListener) {
addEventListener(type, handler, false);
} else if (element.attachEvent) {
attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
},
/*移除事件处理程序*/
removeHandler: function (element, type, handler) {
if (element.removeEventListener) {
removeEventListener(type, handler, false);
} else if (element.detachEvent) {
detachEvent("on" + type, handler);
} else {
element["on" + type] = null;
}
},
/*获得事件对象*/
getEvent: function (event) {
return event ? event : window.event;
},
/*获得事件的目标*/
getTarget: function (event) {
return event.target || event.scrElement;
},
/*取消事件的默认行为*/
preventDefault: function (event) {
if (event.preventDefault) {
event.preventDefault;
} else {
event.returValue = false;
}
},
/*阻止事件进一步冒泡*/
stopPropagation: function (event) {
if (event.stopPropagation) {
event.stopPropagation;
} else {
event.cancelBubble = true;
}
}
};

作业2

简介Java和Python中对常用数据容器的支持库和使用方式

Java的常用库

参考自菜鸟教程,Java官方中文文档

2022年补充

Java提供的数据集合主要涉及两部分,一部分是java.util.Collection的实现类,一部分是java.util.Map的实现类。

Java框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。Collection 接口又有 3 种子类型,List、Set 和 Queue,再下面是一些抽象类,最后是具体实现类,常用的有 ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap 等等。

因为相关类众多,这里做一个简单的示例。使用Java提供的类库是要使用import, 注意Java对大小写敏感。

import java.util.*;

Collection接口

image-20220413103133807.png

参考图来源

List接口是有序的collection。List最大的特点就是有序可重复。Set正与List相反,无序不可重复。

List的子类,有Vector,ArrayList,LinkedList。

1) ArrayList, 是基于数组实现的List类,它封装了一个动态的增长的,允许再分配的Object[]数组,允许对元素进行快速随机访问。

1
2
3
import java.util.ArrayList;
ArrayList<String> sites = new ArrayList<String>();
sites.add("Google");

2)Vector,是通过数组实现的,不同的是它支持线程的同步。

1
2
3
import java.util.Vector;
Vector v = new Vector();
v.addElement(new Integer(1));

3)Stack,是Vector提供的一个子类,模拟栈的数据结构。

1
2
3
import java.util.Stack
Stack<Integer> st = new Stack<Integer>();
st.push(new Integer(10));

Vector已经建议弃用了,因为它加了太多的synchronized。Stack继承了Vector也弃用了

Vector和ArrayList的区别是什么

一是线程安全问题,二是扩容问题,Vector默认扩容两倍,而ArrayList扩容实现用的算术右移,新容量是原来的1.5倍

4)LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除。

1
2
3
import java.util.LinkedList;
LinkedList<Integer> list = new LinkedList<Integer>();
list.add(2);

List的实现方式有LinkedList和ArrayList两种,如何选择,要考虑到实现的功能和效率问题。链表和数组的最大区别就是数组是可以随机访问的。这部分的效率问题其实就是数据结构的内容。

Set接口

Set接口的实现类有HashSet,TreeSet,LinkedHashSet。

1) HashSet类,用Hash算法来存储集合中的元素。

1
2
3
import java.util.HashSet;
HashSet<String> sites = new HashSet<String>();
sites.add("Google");

2)LinkedHashSet,也是根据元素的hashCode值来决定元素的存储位置,但和HashSet不同的是,它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。

3)EnumSet是一个专门为枚举类设计的集合类,EnumSet中所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显式、或隐式地指定。EnumSet的集合元素也是有序的

4)TreeSet采用红黑树结构,特点是可以有序。

Queue接口

Queue有两组API,基本功能是一样的。根据需求选择一组统一使用。

1) 提供了Deque接口,专门用于操作表头和表尾,可以当作堆栈,队列和双向队列使用。

2) PriorityQueue,优先队列按照队列中某个属性的大小来排列。

在实现普通队列时建议使用ArrayDeque,效率更高。只要不是不需要存null值,就用ArrayDeque。

Map接口

用于保存具有映射关系的数据。

Map的子类,子接口

1)HashMap和HashSet集合不能保证元素的顺序一样,HashMap也不能保证key-value对的顺序。并且类似于HashSet判断两个key是否相等的标准一样: 两个key通过equals()方法比较返回true、 同时两个key的hashCode值也必须相等

1
2
3
import java.util.HashMap;
HashMap<Integer, String> Sites = new HashMap<Integer, String>();
Sites.put(1, "Google");

2)HashTable类是一个古老的Map实现类。

3)SortedMap接口下的类,TreeMap类,是一个红黑树结构,每个键值对都作为红黑树的一个节点。TreeMap存储键值对时,需要根据key对节点进行排序,TreeMap可以保证所有的key-value对处于有序状态。 同时,TreeMap也有两种排序方式:自然排序、定制排序(类似于上面List的重写CompareTo()方法)。

Python的常用库

参考自菜鸟教程, python中文手册

Python中同样提供了一些基本容器,列表,元组,字典和集合,以及collections中几种已经预先实现的容器数据结构namedtuple(), deque, ChainMap, Counter, OderedDict, defaultdict等等

同样的Python的每一数据类型都包含了很多的方法。以list为例:

1
2
3
4
5
6
7
8
9
10
11
list.append(x)
list.extend(L)
list.insert(i,x)
list.remove(x)
list.pop()
list.clear()
list.index(x)
list.count(x)
list.sort()
list.reverse()
list.copy()

具体的使用说明可参考官方文档。

Python数据类型

首先,Python中的基本顺序存储结构是列表和元组,在操作和复杂度上和数组完全相同,其中列表是可变数据类型,元组是不可变数据类型。

1)list是Python中最基本的数据结构,列表的数据项不需要具有相同的数据类型。

1
2
3
4
list = ['red', 'green', 12]
print(list[0])
list.append('blue')
del list[0]

list也可以当作堆栈和队列来用。

2)元组与列表类似,不同之处在于元组的元素不能修改。

1
2
3
4
5
tup1 = ('physics', 'chemistry', 1997, 2000)
tup2 = (50,)
print(tup1[0])
tup3 = tup1 + tup2
del tup1

3)字典是另一种可变容器模型,可存储任意类型对象

1
2
3
4
5
dict = {'a': 1, 'b': 2}
print(dict['a'])
dict['a'] = 8
del dict['a']
dict.clear()

4)集合set是一个无序的不重复元素序列。基本功能包括关系测试和消除重复元素。集合对象还支持 union(联合),intersection(交),difference(差)和 sysmmetric difference(对称差集)等数学运算。

1
2
3
4
thisset = set(("google", "runoob", "taobao"))
thisset.add("facebook")
thisset.update([1,4],[5,6])
thisset.remove("taobao")

collections模块

Python的collections模块实现了一些数据结构,使用时可以用import导入

import collections

栈和队列

1)namedtuple是继承自tuple的子类,namedtuple创建一个和tuple类似的对象,而且对象拥有可访问的属性。

1
2
3
from collections import namedtuple
Point = namedtuple('point', ['x', 'y'])
p = Point(1, 2)

2)队列

1
2
3
4
import queue
Q = queue.Queue()
Q.put(10)
print(Q.get())

3)双端队列

1
2
from collections import deque
q = deque([1, 2])

作业3

编写有序表合并算法代码,以及相应的单元测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
namespace demo
{
public class MyObject
{
//升序的有序表合并
public int[] UpMerge(int[] l1, int[] l2)
{
int[] L = new int[l1.Length + l2.Length];
int count = 0;
int i = 0, j = 0;
while (i < l1.Length && j < l2.Length)
{
if (l1[i] < l2[j])//相对于升序的有序表合并,降序的有序表仅需改动此处符号
{
L[count++] = l1[i++];
}
else
{
L[count++] = l2[j++];
}
}
if (i < l1.Length)
{
while (i < l1.Length)
L[count++] = l1[i++];
}
else
{
while (j < l2.Length)
L[count++] = l2[j++];
}
return L;
}
//降序的有序表合并,与以上代码基本相同
//public int[] DownMerge(int[] l1, int[] l2)
//{
//}
}//MyObject
}

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using demo;
namespace test01
{
[TestClass]
public class UnitTest4
{
[TestMethod]
public void TestMethod1()
{
int[] L1 = { 2, 4, 6, 8 };
int[] L2 = { 1, 3, 5, 7, 9, 10, 11, 12};
MyObject my = new MyObject();
int[] LU = my.UpMerge(L1, L2);
for(int i = 0; i < 12; i++)
{
Assert.AreEqual(i + 1, LU[i]);
}
}
}
}


本文标题:Java,python,c#事件机制和常用库比较

文章作者:tsuki

发布时间:2021.06.02 - 16:48

最后更新:2022.04.13 - 11:34

原始链接:https://tsuki419.github.io/homework02.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------THE END-------------
0%