Java

Java 多线程实现异步调用

August 10, 2017
Java, 多线程, 异步

异步调用最主要的特点就是调用方并不等待函数的结果,而是继续进行下面的动作,函数自己去完成相应的过程。在函数完成以后将结果以某种方式返回给调用者。 实现异步的方式有很多,在前端的技术中使用的最多,这里我记录一下Java项目的内部自己异步调用某个函数的方式。 角色 # 在异步调用中,有三个角色,分别是消费者、取货凭据、生产者。对应的就是调用方、数据返回方式、执行函数。 举小蛋糕的例子非常好理解,订蛋糕、蛋糕店给你个取蛋糕的收据,或者也可以给你送到某个地方、蛋糕店生产蛋糕。 消费者 # 首先来看一下调用方的代码应该怎么写。 public class Customer { public static void main(String[] args) { System.out.println("main BEGIN"); CakeShop host = new CakeShop(); Cake cake1 = host.request(10, 'A'); Cake cake2 = host.request(20, 'B'); Cake cake3 = host.request(30, 'C'); System.out.println("main otherJob BEGIN"); try { Thread.sleep(2000); } catch (InterruptedException e) { } System.out.println("main otherJob END"); System.out.println("cake1 = " + cake1.getCake()); System.out.println("cake2 = " + cake2.getCake()); System.out.println("cake3 = " + cake3. ...

spring-shell

August 10, 2017
Java, Spring

这是一个spring框架提供的编写shell命令的一个工具,能够让你用spring的编程模式编写命令支持在shell运行。 看一下这个工具的框架是怎么样的。documentation地址。 核心组件 # spring shell 的最核心的组件是三个:plugin model、built-in commands and converters。 Plugin Model # 插件模块我理解的就是将你写的命令加载进去。你的每个jar模块都要包含一个文件:META-INF/spring/spring-shell-plugin.xml。这个文件会在shell启动的时候被加载并生成Spring的上下文。 在这个文件里你需要定义你的命令相关的类。当然你也可以用spring的扫描功能去自己扫描一个包,然后自己将相关的类进行加载。 这个文件可以写成这样: <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> <context:component-scan base-package="org.springframework.shell.samples.helloworld.commands" /> </beans> 这里官方的文档说:所有的类都是用同一个类加载器加载的,所以建议提供一个类加载器进行隔离。 这些命令是Spring组件,使用@Component注解进行划分。例如,HelloWorldCommands类的示例应用程序是这样的 @Component public class HelloWorldCommands implements CommandMarker { // use any Spring annotations for Dependency Injection or other Spring // interfaces as required. @CliCommand(value = "delete-group", help = "delete group") public String deleteGroup(@CliOption(key = {"id"}, mandatory = true, help = "The id of " + "the group") final String id) { ClientResponse response = RestClient. ...

状态机编程

August 9, 2017
Java

状态机是一种程序设计模式。 这种设计模式适合在一些场景下使用,比如一个任务可能拆开成不同的部分去做,这些动作可能是异步的也可能是同步的,这个时候我们用一个for循环来搞就会很麻烦,最重要的是很丑。 我们希望能做成事件驱动的程序,这个时候一个很好的设计模式就是利用状态机的思路来写。 问题特征 # 一个处理流程中一些部分都可以是异步的,并且每个步骤都有可能失败。 其实最主要的就是这个问题可能失败,所以我们希望在我们将失败的原因搞定以后,再次启动这个程序的时候可以继续从上一次失败的地方继续执行而不是将原来的操作进行回退,然后再重新跑一遍。 状态机编程 # 使用状态机的设计模式可以让程序直接跳到当前状态所处的位置,继续执行。 其实是很简单的,我们给我们的每个步骤都设计几个状态,最常见的状态就是start、success、faild等。 并且在一个状态结束以后会去回调一个函数去更改相关的事件的状态。将事件的状态保存在一个诸如数据库或是什么东西上。 这种设计思路可以应用在很多场景下,比如一个用户的行为会触发一个后台流程,后台要在做完这个流程以后再把结果返回给用户。如果等待程序返回就会浪费很多时间,很可能中间的某个环节失败了,这样的话又要重新来搞一次。 如果用状态机的设计模式,行为触发状态机的一个点,然后由状态机做后续的出发,等到收集到所有事件都成功以后再给用户返回个结果就可以了。同时用户也可以将这个请求做成异步的,不必等待程序的返回值。 对于状态机编程的事件,有一个最重要的一个就是事件的触发是由于状态的改变来触发的,所以只要我们去更改一个事件的状态我们的这个动作就算是做完了,至于触发的动作什么时候做完我们不管,会有一个状态标识的方式的。 思路就是这样的,具体的实现代码以后写其他的东西的时候再贴吧。

SpringMVC

August 2, 2017
Java, Spring, Web, MVC

本文是我的学习过程,但是我认为学习过程中并不适合写给别人看。所以本文只是一个临时的笔记,待以后进行深入整合。 SpringMVC与Spring的关系是什么? SpringMVC是依托Spring框架开发的一个Web MVC框架,天生与Spring集成,也就是使用了Spring的依赖注入和切面编程。 上一篇中讲解了Web.xml,这是Java Web的基础,也就是Servlet容器的工作方式,那个也作为本文的基础。 SpringMVC整体可以看成是一个Servlet,事实上它就是一个Servlet。我们配置web.xml来启动这个Servlet,然后这个Servlet来负责处理每个请求。每个处理请求的类都是我们遵照SpringMVC的格式来写的类,由SpringMVC这个框架来调用完成整个请求的处理。 与原本粗放的原生Java Web的开发模式相比,做了一个聚合。原本是每个请求你都写一个Servlet去处理,在web.xml中配置好一个请求与处理类的映射关系,由容器来完成调用。然后SpringMVC是分担了容器的压力,将容器的工作交给他来做,它利用Spring框架的依赖注入的特性,以及bean的工厂模式重新实现后端的实际处理流程。 有好处也有坏处,我也不太清楚这样做的具体目的是什么,可能会有性能上的提升,因为毕竟工厂模式等特性的加入会提升一些性能和开发便利。坏处感觉也挺明显,首当其冲就是学习栈又深了,还不是深了一个单位是好几个单位。加入了新的配置等,开发过程的脑力成本又增加了。 多说也没用,也是要以后逐步深入才能参透其中的奥妙。 maven 配置项目 # 命令行使用mvn构建项目很简单。 mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-webapp -DarchetypeVersion=1.0 -DgroupId=com.demo -DartifactId=demo -Dversion=1.0 生成的目录如下: . ├── pom.xml └── src └── main ├── resources └── webapp ├── index.jsp └── WEB-INF └── web.xml 配置一下项目所需依赖关系pom.xml: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.demo</groupId> <artifactId>demo</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>demo Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <jdk.version>1.8</jdk.version> <spring.version>4.3.9.RELEASE</spring.version> <jstl.version>1.2</jstl.version> <servletapi.version>3.1.0</servletapi.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org. ...

web.xml

August 1, 2017
Java, Web, Tomcat

其作用就是配置一个web程序的基本功能。容器在启动一个web项目的时候先读取这个配置文件,在配置文件读取无误后开始按照配置启动剩余的服务。 加载顺序 # <context-param> -> <listener> -> <filter> -> <servlet> 容器会创建一个ServletContext,整个项目都会共用这个上下文。而<context-param>的内容以键值对的形式存在这个上下文中。 然后对读取<listener>,并实例化listener类,并初始化。 listener类中会有contextInitialized(ServletContextEvent args)初始化方法,启动Web应用时,系统调用Listener的该方法,在这个方法中获得: ServletContext application = ServletContextEvent.getServletContext(); context-param的值 = application.getInitParameter("context-param的键"); 得到这个context-param的值之后,你就可以做一些操作了。 举例:你可能想在项目启动之前就打开数据库,那么这里就可以在<context-param>中设置数据库的连接方式(驱动、url、user、password),在监听类中初始化数据库的连接。这个监听是自己写的一个类,除了初始化方法,它还有销毁方法,用于关闭应用前释放资源。比如:说数据库连接的关闭,此时,调用contextDestroyed(ServletContextEvent args),关闭Web应用时,系统调用Listener的该方法。 所以归根结底,这个<context-param>就是用来存一些配置数据的地方。 比如:定义一个管理员email地址用来从程序发送错误,或者与你整个应用程序有关的其他设置。使用自己定义的设置文件需要额外的代码和管理;直接在你的程序中使用硬编码(Hard-coding)参数值会给你之后修改程序带来麻烦,更困难的是,要根据不同的部署使用不同的设置;通过这种办法,可以让其他开发人员更容易找到相关的参数,因为它是一个用于设置这种参数的标准位置。 web-app # 根元素,不用多说 display-name # 应用的名称,这个在tomcat的维护信息中会出现。 session # <session-config> <session-timeout>120</session-timeout> </session-config> listener # <!--****************************监听器配置*********************************--> <!-- Spring的log4j监听器 --> <listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> 监听器用来监听各种事件,比如:application和session事件,所有的监听器按照相同的方式定义,功能取决去它们各自实现的接口,常用的Web事件接口有如下几个: ServletContextListener:用于监听Web应用的启动和关闭; ServletContextAttributeListener:用于监听ServletContext范围(application)内属性的改变; ServletRequestListener:用于监听用户的请求; ServletRequestAttributeListener:用于监听ServletRequest范围(request)内属性的改变; HttpSessionListener:用于监听用户session的开始和结束; HttpSessionAttributeListener:用于监听HttpSession范围(session)内属性的改变。 listener类是实现了以下两个接口中任何一个接口的简单java类:javax.servlet.ServletContextListener或javax.servlet.http.HttpSessionListener,如果想让你的类监听应用的启动和停止事件,你就得实现ServletContextListener接口;想让你的类去监听Session的创建和失效事件,那你就得实现HttpSessionListener接口。 有两种为应用配置listener的方式: 使用@WebListener修饰Listener实现类即可。 在web.xml文档中使用<listener>进行配置。 在下一节配置spring的加载类的时候会给出例子。 spring相关配置 # 配置spring,必须需要<listener>,<context-param>如果有就去加载配置的路径,没有就默认是/WEB-INF/applicationContext.xml。 <!-- spring config --> <!-- if this node is not exist, --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring-configuration/*. ...

Servlet

July 28, 2017
Java, Web, Servlet

这里有一些概念非常的恶心,一开始的时候不太好理解是什么。通过一段时间的实践,我逐渐理解,所以直到今天才开始写这一篇。 容器Container # 这个概念从我一开始接触Java就让我很困惑。 首先你需要先去看一下我在后面的一篇关于tomcat的配置文件:server.xml。在那里了解一下配置文件的结构,你可以看懂下面的这张图片。 首先,容器这个东西是针对servlet来说的,也就是我们说的容器都是放置和运行servlet的程序。tomcat是个容器。但是上面的每一级都算是一种容器,只是大小不同。 为什么要有容器 # 其实对于计算机软件体系来说,一切的东西的出现都是因为标准和规定。 所谓容器其实就是一个程序,这个程序按照servlet需要的标准实现了一些接口,使得当你写了一个servlet以后可以成功的放在这个程序中运行,再直白一点就是,你把你的servlet程序交给这个容器,容器可以成功的运行你的程序。 他们之间通过约定一些标志物信息来实现无缝对接。 所以学习这写东西就是在学习了解他们之间约定的事物,以及,为什么这么约定,这样的约定有什么好处和坏处。 容器是一段程序吗? # 在tomcat中,并没有容器这个东西,所以容器并不是一个东西,就像水果并不是一个具体的苹果一样。容器是一个抽象的概念。 在tomcat中,如上图,有很多的层。这些层就是一个容器结构。他们一起构成了一个完整的容器。 Engine 是一段基础的引擎程序。我认为是整个tomcat的基础,所有的其他容器都在其的调度控制之下。 Host 这是一个逻辑概念,但也有对应的实体。在讲tomcat的配置文件servers.xml 的时候,有提到这个host是什么回事。有了这一层,可以在一个tomcat容器中部署不同的项目,这些项目的域名都是不同的。也就是这个容器可以容纳很多的servlet并且这些servlet可以是几种粒度的。 你可以是完全不同的两个网站,域名都不同,放在一个容器中运行。你也可以是一个网站的不同组件,放在不同的Context内运行。 所以容器是个体系,不是一个东西。 其实按照这个思路,一切皆容器。因为一切的这些都是调度管理,操作系统调度进程,进程调度线程。一切都是接口和约定。所以万物皆容器。 什么是servlet # 这是一个标准,你的程序符合这个标准就是一个servlet程序。 一个类其实就是一个servlet,但是很多个类在一起只要有一个servlet的接口,也是一个servlet。 (在此我们不再讨论tomcat的启动过程,我们更关心一个servlet放入tonmcat容器以后的过程) 来看一下这个图片,这就是servlet主要关联的几个类。 **摘抄:** 从上图可以看出 Servlet 规范就是基于这几个类运转的,与 Servlet 主动关联的是三个类,分别是 ServletConfig、ServletRequest 和 ServletResponse。这三个类都是通过容器传递给 Servlet 的,其中 ServletConfig 是在 Servlet 初始化时就传给 Servlet 了,而后两个是在请求达到时调用 Servlet 时传递过来的。 我们很清楚 ServletRequest 和 ServletResponse 在 Servlet 运行的意义,但是 ServletConfig 和 ServletContext 对 Servlet 有何价值? 仔细查看 ServletConfig 接口中声明的方法发现,这些方法都是为了获取这个 Servlet 的一些配置属性,而这些配置属性可能在 Servlet 运行时被用到。 而 ServletContext 又是干什么的呢? Servlet 的运行模式是一个典型的“握手型的交互式”运行模式。所谓“握手型的交互式”就是两个模块为了交换数据通常都会准备一个交易场景,这个场景一直跟随个这个交易过程直到这个交易完成为止。这个交易场景的初始化是根据这次交易对象指定的参数来定制的,这些指定参数通常就会是一个配置类。所以对号入座,交易场景就由 ServletContext 来描述,而定制的参数集合就由 ServletConfig 来描述。而 ServletRequest 和 ServletResponse 就是要交互的具体对象了,它们通常都是作为运输工具来传递交互结果。 ...

spring

July 28, 2017
Java, Spring

这篇文章原本是要完整的讲SpringMVC,现在觉得Spring自己要单独讲一下,SringMVC要单独写个瞎搞教程,混在一起不利于理解,而且写的太乱,所以本问只是介绍一些Spring的特性和一些概念。 Spring 是为了解决企业应用程序开发复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。 之前在学习Spring框架的时候主要研究了两个概念,一个是:控制反转(IOC),还有一个是:面向切面编程(AOP)。今天来整理一下。 Spring 模块 # 学习框架或是其他的库,先概览一下这个东西是由什么组成,然后顺着这个设计思路就可以理解其工作方式。 据说Spring的模块化做的很好,每个模块都可以单独存在。下面这附图很直观的描述了其体系结构。 分两层,共7个模块,其他模块工作在Core模块之上。 每个模块的功能如下: 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转 (IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。 Spring Context:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。 Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。 Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。 Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。 Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。 Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。 就这一段我copy过来的东西就有很多新鲜的东西。解释一下: 工厂模式和bean 这个东西就很大,简单说,工厂模式是一种设计模式。程序设计,搞到现在终于体会到什么是程序设计了吧。跟工业革命是一个道理,从手工作坊到工厂生产。这种设计模式无非就是要简化你写代码的过程,而这个工厂模式就是用来简化对象创建过程的一个设计模式。 在创建一个复杂对象的时候,往往我们要先建一些辅助对象,然后把辅助对象塞进去才能把对象真正new出来。这就是传统手工生产。 但是工厂模式也不是就那么简单的,这个工厂模式还有一个发展过程 ...

Java-Log日志

July 26, 2017
Java, Log

日志以我目前使用到的,有两个功能。 第一,在上线调试的时候查看问题所在。 第二,在线上运行的时候,出现故障回看日志查询问题所在。 总之就是查问题出在哪里,因为编译型的程序没有办法直接搞中间输出来确定问题所在,写在文件里是最好的方式了。 Java中有很多日志系统,但是根据经验,一定有一个日志的抽象层来统一各个日志系统。没错,今天就直接来用这个抽象层的日志工具:SLF4J(Simple logging facade for Java)。它允许你在后台使用任意一个日志类库。如果是在编写供内外部都可以使用的API或者通用类库,那么你真不会希望使用你类库的客户端必须使用你选择的日志类库。 如果一个项目已经使用了log4j,而你加载了一个类库,比方说 Apache Active MQ——它依赖于于另外一个日志类库logback,那么你就需要把它也加载进去。但如果Apache Active MQ使用了SLF4J,你可以继续使用你的日志类库而无需忍受加载和维护一个新的日志框架的痛苦。 日志原理 # 一般的日志都是由三个部分组成:logger、appenders、layouts。logger负责捕获记录信息,转给appender去转发到配置的目的地,layouts负责定义日志的格式。 下面就具体的介绍一下Log4j帮助理解。 Log4j # log4j 有很多优点: Log4j是高度可配置的,并可通过在运行时的外部文件配置。它根据记录的优先级别,并提供机制,以指示记录信息到许多的目的地,诸如:数据库,文件,控制台,UNIX系统日志等。 已经扩展到C/C++、C#、Perl、Python等语言中。 线程安全、速度快 多输出 也有一些缺点,比如不能保证所有日志信息都送到目的地在其出现故障的时候,会减慢程序的速度,不过这是所有日志都面临的问题,貌似也没有什么好的解决办法。 log4j 在使用的时候需要一个配置文件:log4j.properties或log4j.xml。 如果是java project项目,则在项目的根目录下创建log4j.properties而不是在src目录下。 如果是java web项目,则在src目录下创建log4j.properties配置文件,因为这个时候tomcat会去默认的加载这个配置文件,而不需要我们手动的加载log4j的配置文件。 两种配置的基本配置选项都是一样的,log4j.properties的格式是更好理解的,奈何我拿到的是个该死的xml格式的项目。直观看很丑!非常丑!xml在我这的印象分又掉了一半。 还是先介绍各个选项的内容吧,形式不重要。 选项参数 # 上面讲了日志的三个组成部分,其配置也就是针对这三个部分来搞的。 以properties的格式先介绍一下。 #配置根Logger (Loggers) log4j.rootLogger = [ level ] , appenderName1 , appenderName2 , … #配置日志信息输出目的地 (Appenders) log4j.appender.appenderName = fully.qualified.name.of.appender.class log4j.appender.appenderName.option1 = value1 … log4j.appender.appenderName.optionN = valueN #配置日志信息的格式(Layouts) log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class log4j.appender.appenderName.layout.option1 = value1 … log4j. ...

java 线程与池

July 25, 2017
Java, 多线程

线程与池这两个东西在很多语言中都存在,这是一种编程模式。 池的思维是根本。举个例子,如果某个数据库有一个远程交互的API允许你写代码来远程操作,那么你如果不用池,每条命令都重新发起一个连接,然后要数据库认证身份,然后执行命令。这样的效率不高,你会很当然的想到我要把连接保持住,让多个命令的执行只进行一次身份认证。这个时候就引入池的概念。每次从池中取出对应的连接,执行完命令再放回到池里。 同样的思维在ACM比赛中我们常用内存池的方法来避免频繁申请内存。 线程作为一种资源,也需要申请,申请也要开销,所以能将申请次数降低就可以提高效率,所以我们搞个池。 线程是一个进程的执行单元。一个进程中可以有若干线程,每个线程都是独立的执行单元。线程的调度比进程调度要快很多,因为线程调度的资源涉及更小。 那么为什么要多线程? 先来讲一下并行和并发。 并行:“真正的两个机器同时工作”; 并发:“看上去的同时执行的,实际上在同一个cpu上轮转执行的”; 并发的场景非常多,最基本的就是同时过来两个请求需要做,如果你是单线程的程序,就必须等待前一个完全做完再处理后面一个。如果你是多线程程序,就可以从线程池里取一个线程来处理新的请求,可以保证工作效率。这个地方其实有两个东西,一个是用户体验,一个是异步操作。前一个好理解,后一个讲一下我的理解。 现在很多时候我们都要将处理步骤拆成异步的来做,异步处理的实现方式其中就有多线程。同步就是我前一句代码没有做完,后面的代码后面的步骤就阻塞在那里了。如果我们用异步的方式,开一个新的线程来搞某个步骤。例如写日志和返回执行结果,我们就可以起一个线程去写日志,然后主线程直接返回处理结果,这个处理结果对日志是弱依赖的,日志写的如何并不影响我返回结果的操作,不能因为我日志写失败了我就不把结果返回去,有可能磁盘页被其他程序上锁了,一时半会儿都搞不完,这个时候多线程的优势就显现出来了。 感觉到这里讲了很多东西,都可以讲的很细的,但是讲的太细看的人都没兴趣了,我自己也不需要写太多。 如何实现多线程 # 在Java中封装的很好,用起来也很简单。实现多线程有两种方法,一种是继承Thread类,一种是实现Runable 接口。 Thread # class Thread1 extends Thread{ private String name; public Thread1(String name) { this.name=name; } public void run() { for (int i = 0; i < 5; i++) { System.out.println(name + "运行 : " + i); try { sleep((int) Math.random() * 10); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Main { public static void main(String[] args) { Thread1 mTh1=new Thread1("A"); Thread1 mTh2=new Thread1("B"); mTh1. ...

iterator 迭代器

July 23, 2017
C++, Java

迭代器是一种对数据结构数据进行遍历的模式,也成为游标(Cursor)模式。这种模式为了适应各种被封装了的复杂数据结构的完全顺序遍历而设计。其设计思想依旧是封装的思想,屏蔽各种数据结构底层的存储差异,使用统一的方法来遍历所有的数据。 C++ # 先以C++中的迭代器的使用来说一下。举两个常用的容器的例子。 vector # 遍历、删除元素 # vector<int>::iterator it; for( it = A.begin(); it!=A.end(); it++){ cout << *it << endl; if(*it == 3){ A.erase(it); } } /* 上面这种写法是有问题的,当你删掉3这元素的时候,it再++,直接就到了5了,4就跳过去了。 因为erase后,后面的元素都会前移。 从这里我们可以看到,vector中的迭代器应该就是指针。 正确的写法如下: */ vector<int>::iterator it; for( it = A.begin(); it!=A.end(); it++){ cout << *it << endl; if(*it == 3){ vector<int>::iterator it_tmp = it; it--; A.erase(it_tmp); } } set # set<int>::iterator it; for(it = S.begin(); it!=S.end(); it++){ cout << *it << endl; if(*it == 3){ set<int>::iterator it_tmp=it; it--; S. ...