-
操作场景DLI支持用户使用Hive UDTF(User-Defined Table-Generating Functions)自定义表值函数,UDTF用于解决一进多出业务场景,即其输入与输出是一对多的关系,读入一行数据,输出多个值。约束限制在DLI Console上执行UDTF相关操作时,需要使用自建的SQL队列。跨账号使用UDTF时,除了创建UDTF函数的用户,其他用户如果需要使用时,需要先进行授权才可使用对应的UDTF函数。授权操作参考如下:登录DLI管理控制台,选择“ 数据管理 > 程序包管理”页面,选择对应的UDTF Jar包,单击“操作”列中的“权限管理”,进入权限管理页面,单击右上角“授权”,勾选对应权限。自定义函数中引用static类或接口时,必须要加上“try catch”异常捕获,否则可能会造成包冲突,导致函数功能异常。环境准备在进行UDTF开发前,请准备以下开发环境。表1 UDTF开发环境准备项说明操作系统Windows系统,支持Windows7以上版本。安装JDKJDK使用1.8版本。安装和配置IntelliJ IDEAIntelliJ IDEA为进行应用开发的工具,版本要求使用2019.1或其他兼容版本。安装Maven开发环境的基本配置。用于项目管理,贯穿软件开发生命周期。开发流程DLI下UDTF函数开发流程参考如下:图1 UDTF开发流程表2 开发流程说明序号阶段操作界面说明1新建Maven工程,配置pom文件IntelliJ IDEA参考操作步骤说明,编写UDTF函数代码。2编写UDTF函数代码3调试,编译代码并导出Jar包4上传Jar包到OBSOBS控制台将生成的UDTF函数Jar包文件上传到OBS目录下。5创建DLI的UDTF函数DLI控制台在DLI控制台的SQL作业管理界面创建使用的UDTF函数。6验证和使用DLI的UDTF函数DLI控制台在DLI作业中使用创建的UDTF函数。操作步骤新建Maven工程,配置pom文件。以下通过IntelliJ IDEA 2020.2工具操作演示。打开IntelliJ IDEA,选择“File > New > Project”。图2 新建Project选择Maven,Project SDK选择1.8,单击“Next”。定义样例工程名和配置样例工程存储路径,单击“Finish”完成工程创建。在pom.xml文件中添加如下配置。<dependencies> <dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-exec</artifactId> <version>1.2.1</version> </dependency> </dependencies>图3 pom文件中添加配置在工程路径的“src > main > java”文件夹上鼠标右键,选择“New > Package”,新建Package和类文件。Package根据需要定义,本示例定义为:“com.huawei.demo”,完成后回车。在包路径下新建Java Class文件,本示例定义为:UDTFSplit。编写UDTF函数代码。完整样例代码请参考样例代码。UDTF的类需要继承“org.apache.hadoop.hive.ql.udf.generic.GenericUDTF”,实现initialize,process,close三个方法。UDTF首先会调用initialize方法,此方法返回UDTF的返回行的信息,如,返回个数,类型等。初始化完成后,会调用process方法,真正处理在process函数中,在process中,每一次forward()调用产生一行。如果产生多列可以将多个列的值放在一个数组中,然后将该数组传入到forward()函数。public void process(Object[] args) throws HiveException { // TODO Auto-generated method stub if(args.length == 0){ return; } String input = args[0].toString(); if(StringUtils.isEmpty(input)){ return; } String[] test = input.split(";"); for (int i = 0; i < test.length; i++) { try { String[] result = test[i].split(":"); forward(result); } catch (Exception e) { continue; } } }最后调用close方法,对需要清理的方法进行清理。编写调试完成代码后,通过IntelliJ IDEA工具编译代码并导出Jar包。单击工具右侧的“Maven”,参考下图分别单击“clean”、“compile”对代码进行编译。编译成功后,单击“package”对代码进行打包。图4 编译打包打包成功后,生成的Jar包会放到target目录下,以备后用。本示例将会生成到:“D:\MyUDTF\target”下名为“MyUDTF-1.0-SNAPSHOT.jar”。登录OBS控制台,将生成的Jar包文件上传到OBS路径下。说明:Jar包文件上传的OBS桶所在的区域需与DLI的队列区域相同,不可跨区域执行操作。(可选)可以将Jar包文件上传到DLI的程序包管理中,方便后续统一管理。登录DLI管理控制台,单击“数据管理 > 程序包管理”。在“程序包管理”页面,单击右上角的“创建”创建程序包。在“创建程序包”对话框,配置以下参数。包类型:选择“JAR”。OBS路径:程序包所在的OBS路径。分组设置和组名称根据情况选择设置,方便后续识别和管理程序包。单击“确定”,完成创建程序包。创建DLI的UDTF函数。登录DLI管理控制台,单击“SQL编辑器”,执行引擎选择“spark”,选择已创建的SQL队列和数据库。图5 选择队列和数据库在SQL编辑区域输入下列命令创建UDTF函数,单击“执行”提交创建。CREATE FUNCTION mytestsplit AS 'com.huawei.demo.UDTFSplit' using jar 'obs://dli-test-obs01/MyUDTF-1.0-SNAPSHOT.jar';重启原有SQL队列,使得创建的UDTF函数生效。登录数据湖探索管理控制台,选择“队列管理”,在对应“SQL队列”类型作业的“操作”列,单击“重启”。在“重启队列”界面,选择“确定”完成队列重启。验证和使用创建的UDTF函数。在查询语句中使用6中创建的UDTF函数,如:select mytestsplit('abc:123\;efd:567\;utf:890');图6 执行结果(可选)删除UDTF函数。如果不再使用该Function,可执行以下语句删除UDTF函数:Drop FUNCTION mytestsplit;样例代码UDTFSplit.java完整的样例代码参考如下所示:package com.huawei.demo; import java.util.ArrayList; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.hive.ql.exec.UDFArgumentException; import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException; import org.apache.hadoop.hive.ql.metadata.HiveException; import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF; import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector; import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory; import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector; import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory; public class UDTFSplit extends GenericUDTF { @Override public void close() throws HiveException { // TODO Auto-generated method stub } @Override public void process(Object[] args) throws HiveException { // TODO Auto-generated method stub if(args.length == 0){ return; } String input = args[0].toString(); if(StringUtils.isEmpty(input)){ return; } String[] test = input.split(";"); for (int i = 0; i < test.length; i++) { try { String[] result = test[i].split(":"); forward(result); } catch (Exception e) { continue; } } } @Override public StructObjectInspector initialize(ObjectInspector[] args) throws UDFArgumentException { if (args.length != 1) { throw new UDFArgumentLengthException("ExplodeMap takes only one argument"); } if (args[0].getCategory() != ObjectInspector.Category.PRIMITIVE) { throw new UDFArgumentException("ExplodeMap takes string as a parameter"); } ArrayList<String> fieldNames = new ArrayList<String>(); ArrayList<ObjectInspector> fieldOIs = new ArrayList<ObjectInspector>(); fieldNames.add("col1"); fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector); fieldNames.add("col2"); fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector); return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs); } }
-
操作场景DLI支持用户使用Hive UDF(User Defined Function,用户定义函数)进行数据查询等操作,UDF只对单行数据产生作用,适用于一进一出的场景。约束限制在DLI Console上执行UDF相关操作时,需要使用自建的SQL队列。跨账号使用UDF时,除了创建UDF函数的用户,其他用户如果需要使用时,需要先进行授权才可使用对应的UDF函数。授权操作参考如下:登录DLI管理控制台,选择“ 数据管理 > 程序包管理”页面,选择对应的UDF Jar包,单击“操作”列中的“权限管理”,进入权限管理页面,单击右上角“授权”,勾选对应权限。自定义函数中引用static类或接口时,必须要加上“try catch”异常捕获,否则可能会造成包冲突,导致函数功能异常。环境准备在进行UDF开发前,请准备以下开发环境。表1 UDF开发环境准备项说明操作系统Windows系统,支持Windows7以上版本。安装JDKJDK使用1.8版本。安装和配置IntelliJ IDEAIntelliJ IDEA为进行应用开发的工具,版本要求使用2019.1或其他兼容版本。安装Maven开发环境的基本配置。用于项目管理,贯穿软件开发生命周期。开发流程DLI下UDF函数开发流程参考如下:图1 开发流程表2 开发流程说明序号阶段操作界面说明1新建Maven工程,配置pom文件IntelliJ IDEA参考操作步骤说明,编写UDF函数代码。2编写UDF函数代码3调试,编译代码并导出Jar包4上传Jar包到OBSOBS控制台将生成的UDF函数Jar包文件上传到OBS目录下。5创建DLI的UDF函数DLI控制台在DLI控制台的SQL作业管理界面创建使用的UDF函数。6验证和使用DLI的UDF函数DLI控制台在DLI作业中使用创建的UDF函数。操作步骤新建Maven工程,配置pom文件。以下通过IntelliJ IDEA 2020.2工具操作演示。打开IntelliJ IDEA,选择“File > New > Project”。图2 新建Project选择Maven,Project SDK选择1.8,单击“Next”。定义样例工程名和配置样例工程存储路径,单击“Finish”完成工程创建。在pom.xml文件中添加如下配置。<dependencies> <dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-exec</artifactId> <version>1.2.1</version> </dependency> </dependencies>图3 pom文件中添加配置在工程路径的“src > main > java”文件夹上鼠标右键,选择“New > Package”,新建Package和类文件。Package根据需要定义,本示例定义为:“com.huawei.demo”,完成后回车。在包路径下新建Java Class文件,本示例定义为:SumUdfDemo。编写UDF函数代码。UDF函数实现,主要注意以下几点:自定义UDF需要继承org.apache.hadoop.hive.ql.UDF。需要实现evaluate函数,evaluate函数支持重载。详细UDF函数实现,可以参考如下样例代码:package com.huawei.demo; import org.apache.hadoop.hive.ql.exec.UDF; public class SumUdfDemo extends UDF { public int evaluate(int a, int b) { return a + b; } }编写调试完成代码后,通过IntelliJ IDEA工具编译代码并导出Jar包。单击工具右侧的“Maven”,参考下图分别单击“clean”、“compile”对代码进行编译。编译成功后,单击“package”对代码进行打包。打包成功后,生成的Jar包会放到target目录下,以备后用。本示例将会生成到:“D:\DLITest\MyUDF\target”下名为“MyUDF-1.0-SNAPSHOT.jar”。登录OBS控制台,将生成的Jar包文件上传到OBS路径下。说明:Jar包文件上传的OBS桶所在的区域需与DLI的队列区域相同,不可跨区域执行操作。(可选)可以将Jar包文件上传到DLI的程序包管理中,方便后续统一管理。登录DLI管理控制台,单击“数据管理 > 程序包管理”。在“程序包管理”页面,单击右上角的“创建”创建程序包。在“创建程序包”对话框,配置以下参数。包类型:选择“JAR”。OBS路径:程序包所在的OBS路径。分组设置和组名称根据情况选择设置,方便后续识别和管理程序包。单击“确定”,完成创建程序包。创建UDF函数。登录DLI管理控制台,单击“SQL编辑器”,执行引擎选择“spark”,选择已创建的SQL队列和数据库。图4 选择队列和数据库在SQL编辑区域输入下列命令创建UDF函数,单击“执行”提交创建。CREATE FUNCTION TestSumUDF AS 'com.huawei.demo.SumUdfDemo' using jar 'obs://dli-test-obs01/MyUDF-1.0-SNAPSHOT.jar';重启原有SQL队列,使得创建的Function生效。登录数据湖探索管理控制台,选择“队列管理”,在对应“SQL队列”类型作业的“操作”列,单击“重启”。在“重启队列”界面,选择“确定”完成队列重启。使用UDF函数。在查询语句中使用6中创建的UDF函数:select TestSumUDF(1,2);图5 执行结果(可选)删除UDF函数。如果不再使用UDF函数,可执行以下语句删除该函数:Drop FUNCTION TestSumUDF;
-
在实际开发中,通过反射可以得到一个类的完整结构,包括类的构造方法、类的属性、类的方法,这就需要使用到java.lang.reflect包中的以下几个类:1、Constructor:表示类中的构造方法2、Field:表示类中的属性3、Method:表示类中的方法目录1、使用反射技术获取构造器对象并使用2、使用反射技术获取成员变量对象并使用3、使用反射技术获取方法对象并使用使用反射技术获取构造器对象并使用实验类:public class 反射机制_2实验类 { private String name; private int age; private 反射机制_2实验类() { System.out.println("无参构造器执行!"); } public 反射机制_2实验类(String name, int age) { System.out.println("有参构造器执行!"); 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 "反射机制_2实验类{" + "name='" + name + '\'' + ", age=" + age + '}'; }} 获取构造器对象:import org.junit.Test; import java.lang.reflect.Constructor; public class 反射机制_2构造器获取对象 { //1、getConstructors //获取全部的构造器,只能获取public修饰的构造器 @Test public void getConstructors(){ //获取类对象 Class c=反射机制_2实验类.class; //提取类中的全部的构造器对象(这里只能拿public修饰的构造器) Constructor[] constructors=c.getConstructors(); //遍历构造器 for (Constructor constructor : constructors) { System.out.println(constructor.getName()+"\t构造器参数个数为:"+constructor.getParameterCount()+"个"); } //运行结果:IT2.反射机制_2实验类 构造器参数个数为:2个 } //2、getDeclaredConstructors //获取全部的构造器:只要你敢写,这里就能拿到,无所谓权限是否可及 @Test public void getDeclaredConstructors(){ //获取类对象 Class c=反射机制_2实验类.class; //提取类中的全部的构造器对象 Constructor[] constructors=c.getDeclaredConstructors(); //遍历构造器 for (Constructor constructor : constructors) { System.out.println(constructor.getName()+"\t构造器参数个数为:"+constructor.getParameterCount()+"个"); } //运行结果: // IT2.反射机制_2实验类 构造器参数个数为:0个 // IT2.反射机制_2实验类 构造器参数个数为:2个 } //3、getConstructor //获取某个构造器:只能拿Public修饰的某个构造器 @Test public void getConstructor() throws Exception { //获取类对象 Class c=反射机制_2实验类.class; //定位单个构造器对象(按照参数,这里定位的是有参的构造器) Constructor cons=c.getConstructor(String.class,int.class); // Constructor cons=c.getConstructor();//如果获取无参构造器,但因为我设置的是private私有的,权限不足无法获取,便会报错 System.out.println(cons.getName()+"\t构造器参数个数为:"+cons.getParameterCount()+"个"); //运行结果:IT2.反射机制_2实验类 构造器参数个数为:2个 } //4、getDeclaredConstructor //获取某个构造器:只要你敢写,这里就能拿到,无所谓权限是否可及 //一般是用这个,什么都可以获取,并且是根据自己需要什么而获取 @Test public void getDeclaredConstructor() throws Exception { //获取类对象 Class c=反射机制_2实验类.class; //定位单个构造器对象(按照参数,这里定位的是有参的构造器) Constructor cons=c.getDeclaredConstructor(String.class,int.class); System.out.println(cons.getName()+"\t构造器参数个数为:"+cons.getParameterCount()+"个"); //运行结果:IT2.反射机制_2实验类 构造器参数个数为:2个 //获取类对象 Class c2=反射机制_2实验类.class; //定位单个构造器对象(按照参数定位无参构造器) Constructor cons2=c2.getDeclaredConstructor(); System.out.println(cons2.getName()+"\t构造器参数个数为:"+cons2.getParameterCount()+"个"); //运行结果:IT2.反射机制_2实验类 构造器参数个数为:0个 }}Class在开发中最常见的用法就是将Class类对象实例化为自定义类的对象,即可通过一个给定的字符串(类的全限定类名)实例化一个本类的对象。将Class对象实例化为本类对象时,可以通过无参构造完成,也可以通过有参构造完成。 创建对象:import org.junit.Test;import java.lang.reflect.Constructor; //反射可以破坏封装性,私有的也可以执行了public class 反射机制_2创建对象 { @Test public void getDeclaredConstructor() throws Exception { //获取类对象 Class c=反射机制_2实验类.class; //定位单个构造器对象(按照参数,这里定位的是有参的构造器) Constructor cons=c.getDeclaredConstructor(String.class,int.class); System.out.println(cons.getName()+"\t构造器参数个数为:"+cons.getParameterCount()+"个"); 反射机制_2实验类 s1= (反射机制_2实验类) cons.newInstance("狗蛋",18); System.out.println(s1); System.out.println(); //获取类对象 Class c2=反射机制_2实验类.class; //定位单个构造器对象(按照参数定位无参构造器) Constructor cons2=c2.getDeclaredConstructor(); System.out.println(cons2.getName()+"\t构造器参数个数为:"+cons2.getParameterCount()+"个"); //如果遇到了私有的构造器,可以暴力反射 cons2.setAccessible(true);//权限打开(只是这一次有效,并不是一直打开) 反射机制_2实验类 s2= (反射机制_2实验类) cons2.newInstance(); System.out.println(s2); //运行结果: //IT2.反射机制_2实验类 构造器参数个数为:2个 //有参构造器执行! //反射机制_2实验类{name='狗蛋', age=18} // //IT2.反射机制_2实验类 构造器参数个数为:0个 //无参构造器执行! //反射机制_2实验类{name='null', age=0} }}使用反射技术获取成员变量对象并使用实验类:public class 反射机制_2实验类2 { private String name; private int age; public static String schoolName; public static final String Name="遇安"; public 反射机制_2实验类2() { System.out.println("无参构造器执行!"); } public 反射机制_2实验类2(String name, int age) { System.out.println("有参构造器执行!"); 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; } public static String getSchoolName() { return schoolName; } public static void setSchoolName(String schoolName) { 反射机制_2实验类2.schoolName = schoolName; } @Override public String toString() { return "反射机制_2实验类2{" + "name='" + name + '\'' + ", age=" + age + '}'; }} 获取成员变量:在反射操作中可以获取一个类中的全部属性,但是类中的属性包括两部分,即从父类继承的属性和本类定义的属性。因此,在获取类的属性时也有以下两种不同的方式:1、获取实现的接口或父类中的公共属性:public Field [] getFields throws SecurityException2、获取本类中的全部属性:public Field [] getDeclaredFields throws Exception上述两种方法返回的都是Field数组,每一个Field对象表示类中的一个属性。如果要获取属性中的详细信息,就需要调用Field类的方法。import org.junit.Test; import java.lang.reflect.Field; public class 反射机制_2获取成员变量 { //1、获取全部的成员变量 // Field[] getDeclaredFields //获得所有的成员变量对应的Field对象,只要申明了就可以得到 @Test public void getDeclaredFields(){ //定位Class对象 Class c=反射机制_2实验类2.class; //定位全部成员变量 Field [] fields=c.getDeclaredFields(); //遍历获取,常量也会被当做成员变量 for (Field field : fields) { System.out.println(field.getName()+"的类型是:"+field.getType()); } } //2、获取某个成员变量对象 //Field getDeclaredField(String name) @Test public void getDeclaredField() throws Exception { //定位Class对象 Class c=反射机制_2实验类2.class; //根据名称定位某个成员变量 Field f=c.getDeclaredField("age"); System.out.println(f); System.out.println(f.getName()+"的类型是:"+f.getType()); }}运行结果:Test1、name的类型是:class java.lang.Stringage的类型是:intschoolName的类型是:class java.lang.StringCOUNTTRY的类型是:class java.lang.String Test2、private int IT2.反射机制_2实验类2.ageage的类型是:int 获取了成员变量有什么用呢?import org.junit.Test; import java.lang.reflect.Field; public class 反射机制_2获取成员变量 { //获取了成员变量有什么用呢? @Test public void demo() throws Exception { //反射第一步获取Class对象 Class c=反射机制_2实验类2.class; //提取某个成员变量 Field f=c.getDeclaredField("age"); f.setAccessible(true);//因为我的age成员变量是用private修饰的,所以需要暴力打开权限 //作用一:赋值 反射机制_2实验类2 s=new 反射机制_2实验类2(); f.set(s,18);//s.setAge(18); System.out.println(s); //运行结果: // 无参构造器执行! //反射机制_2实验类2{name='null', age=18}这里可以看出,成员变量被赋值成功 //作用二:取值 int age = (int) f.get(s); System.out.println(age);//18 }}使用反射技术获取方法对象并使用实验类:public class 反射机制_2实验类3 { private String name; public 反射机制_2实验类3() { } public 反射机制_2实验类3(String name) { this.name = name; } public void run(){ System.out.println("跑起来了。。"); } private void eat(){ System.out.println("累了,该吃饭了。。"); } private String eat(String name){ System.out.println("那就浅吃一下"+name+"吧"); return "针不戳"; } public static void ind(){ System.out.println("欢迎来到遇安的博客!"); } public String getName() { return name; } public void setName(String name) { this.name = name; }} 获取成员方法:import org.junit.Test; import java.lang.reflect.Method; public class 反射机制_2获取成员方法 { //获得类中的所有成员方法对象 @Test public void getDeclaredMethods(){ //获取类对象 Class c=反射机制_2实验类3.class; //提取全部方法,包括私有的 Method [] methods=c.getDeclaredMethods(); //遍历全部方法 for (Method method : methods) { System.out.println(method.getName()+"返回值类型:"+method.getReturnType()+"参数个数:"+method.getParameterCount()); } } //提取某个方法对象 @Test public void getDeclaredMethod() throws Exception { //获取类对象 Class c=反射机制_2实验类3.class; //提取单个方法对象 Method m1=c.getDeclaredMethod("eat"); Method m2=c.getDeclaredMethod("eat",String.class); //暴力打开权限 m1.setAccessible(true); m2.setAccessible(true); //触发方法的执行 反射机制_2实验类3 s=new 反射机制_2实验类3(); //注意:如果方法是没有结果返回的,那么返回的是Null Object result =m1.invoke(s); System.out.println(result); Object result2=m2.invoke(s,"海鲜大餐"); System.out.println(result2); }}运行结果:累了,该吃饭了。。null那就浅吃一下海鲜大餐原文链接:https://blog.csdn.net/qq_62731133/article/details/125089941
-
第一步:完成界面搭建要求:创建两个窗口:一个客户端,一个服务端,完成代码书写步骤:1.定义JFrame窗体中的组件:文本域,滚动条,面板,文本框,按钮 //属性 //文本域 private JTextArea jta; //滚动条 private JScrollPane jsp; //面板 private JPanel jp; //文本框 private JTextField jtf; //按钮 private JButton jb; 2.在构造方法中初始化窗口的组件: //构造方法 public ServerChatMain(){ //初始化组件 jta = new JTextArea(); //设置文本域默认不可编辑 jta.setEditable(false); //注意:需要将文本域添加到滚动条中,实现滚动效果 jsp =new JScrollPane(jta); //初始化面板 jp = new JPanel(); jtf = new JTextField(10); jb=new JButton("发送"); //注意:需要将文本框与按钮添加到面板中 jp.add(jtf); jp.add(jb); 注意:需要将滚动条与面板全部添加到窗体中,继承了窗体的属性,这里this就是窗体 this.add(jsp, BorderLayout.CENTER);//BorderLayout--边框布局 this.add(jp,BorderLayout.SOUTH);4.设置设置”标题“,大小,位置,关闭,是否可见 //注意:需要设置”标题“,大小,位置,关闭,是否可见 this.setTitle("QQ聊天服务端"); this.setSize(400,300); this.setLocation(700,300); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//窗体关闭,程序就退出 this.setVisible(true);//设置窗体可见 }这样我们就完成了服务端的QQ聊天界面窗口搭建:注意:JTextArea文本域是不可以书写的客户端与服务端代码类似,这里就不一一展示了书写完毕代码,运行效果如下图:第二步:TPC通信的思路与步骤❄️使用网络编程完成数据点的传输(TCP,UDP协议)TCP协议TCP 是面向连接的运输层协议。应用程序在使用 TCP 协议之前,必须先建立 TCP 连接。在传送数据完毕后,必须释放已经建立的 TCP 连接每一条 TCP 连接只能有两个端点,每一条 TCP 连接只能是点对点的(一对一)TCP 提供可靠交付的服务。通过 TCP 连接传送的数据,无差错、不丢失、不重复,并且按序到达TCP 提供全双工通信。TCP 允许通信双方的应用进程在任何时候都能发送数据。TCP 连接的两端都设有发送缓存和接受缓存,用来临时存放双向通信的数据面向字节流。TCP 中的“流”指的是流入到进程或从进程流出的字节序列TCP 服务端 具体步骤(客户端类似)具体步骤: 1.创建一个服务端的套接字 2.等待客户端连接 3.获取socket通道的输入流(输入六是实现读取数据的,一行一行读取)BufferedReader->readLine(); 4.获取socket 通道的输出流(输出流实现写出数据,也是写一行换一行,刷新)BufferedWriter->newLine(); 5.关闭socket 通道TCP通信步骤代码实现: try { //1.创建一个服务端的套接字 ServerSocket serverSocket = new ServerSocket(8888); //2.等待客户端连接 Socket socket = serverSocket.accept(); //3.获取socket通道的输入流(输入六是实现读取数据的,一行一行读取)BufferedReader->readLine(); //InputStream in = socket.getInputStream(); BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream())); //4.获取socket 通道的输出流(输出流实现写出数据,也是写一行换一行,刷新)BufferedWriter->newLine(); //当用户点击发送按钮的时候写出数据 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); //循环读取数据并拼接到文本域 String line = null; while((line = br.readLine())!=null){ //将读取的数据拼接到文本域中显示 jta.append(line+System.lineSeparator()); } //5.关闭socket 通道 serverSocket.close(); }catch (IOException e) { e.printStackTrace(); } 点击发送按钮实现数据的传输 @Override public void actionPerformed(ActionEvent actionEvent) { System.out.println("发送按钮被点击了"); }步骤:1.获取文本框中发送的内容2.拼接需要发送的数据内容3.自己也要显示4.发送5.清空文本框内容@Override public void actionPerformed(ActionEvent actionEvent) { //System.out.println("发送按钮被点击了"); //1.获取文本框中发送的内容 String text = jtf.getText(); //2.拼接需要发送的数据内容 text = "服务端对客户端说:"+text; //3.自己也要显示 jta.append(text); //4.发送 try { bw.write(text); bw.newLine();//换行刷新 bw.flush(); //5.清空文本框内容 jtf.setText(""); } catch (IOException e) { e.printStackTrace(); } } 第三步:实现回车键发送数据(客户端类似)首先要实现一个接口public class ClientChatMain extends JFrame implements ActionListener, KeyListener {@Override public void keyPressed(KeyEvent e) { //回车键 // System.out.println(e); //发送数据到socket 同道中 if(e.getKeyCode()==KeyEvent.VK_ENTER) { sendDataToSocket(); }全部代码:服务端:package com.ithmm.chat; import javax.swing.*;import java.awt.*;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.io.*;import java.net.ServerSocket;import java.net.Socket;import java.util.Properties; //说明:如果一个类,需要有界面的显示,那么这个类就需要继承JFrame,此时,该类可以被成为一个窗体类/*步骤: 1.定义JFrame窗体中的组件 2.在构造方法中初始化窗口的组件 3.使用网络编程完成数据的链接(TPC,UDP 协议) 4.实现"发送”按钮的监听点击事件 5.实现“回车键”发送数据 */public class ServerChatMain extends JFrame implements ActionListener, KeyListener { public static void main(String[] args) { //调用构造方法 new ServerChatMain(); } //属性 //文本域 private JTextArea jta; //滚动条 private JScrollPane jsp; //面板 private JPanel jp; //文本框 private JTextField jtf; //按钮 private JButton jb; //输出流(成员变量) private BufferedWriter bw = null; //服务端的端口号 //private static int serverPort; //使用static静态方法读取外部京台文件 //static代码块特点:1.在类加载的时候自动执行 //特点2:一个类会被加载一次,因此静态代码块在程序中仅会被执行一次 /*static{ Properties prop = new Properties(); try { //加载 prop.load(new FileReader("chat.properties")); //给属性赋值 Integer.parseInt(prop.getProperty("serverPort")); } catch (IOException e) { e.printStackTrace(); } }*/ //构造方法 public ServerChatMain(){ //初始化组件 jta = new JTextArea(); //设置文本域默认不可编辑 jta.setEditable(false); //注意:需要将文本域添加到滚动条中,实现滚动效果 jsp =new JScrollPane(jta); //初始化面板 jp = new JPanel(); jtf = new JTextField(10); jb=new JButton("发送"); //注意:需要将文本框与按钮添加到面板中 jp.add(jtf); jp.add(jb); //注意:需要将滚动条与面板全部添加到窗体中,继承了窗体的属性,这里this就是窗体 this.add(jsp, BorderLayout.CENTER);//BorderLayout--边框布局 this.add(jp,BorderLayout.SOUTH); //注意:需要设置”标题“,大小,位置,关闭,是否可见 this.setTitle("QQ聊天服务端"); this.setSize(400,300); this.setLocation(700,300); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//窗体关闭,程序就退出 this.setVisible(true);//设置窗体可见 /**************** TCP 服务端 Start *********************/ //给发送按钮绑定一个监听点击事件 jb.addActionListener(this); //给文本框绑定一个键盘点击事件 jtf.addKeyListener(this); try { //1.创建一个服务端的套接字 ServerSocket serverSocket = new ServerSocket(8888); //2.等待客户端连接 Socket socket = serverSocket.accept(); //3.获取socket通道的输入流(输入六是实现读取数据的,一行一行读取)BufferedReader->readLine(); //InputStream in = socket.getInputStream(); BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream())); //4.获取socket 通道的输出流(输出流实现写出数据,也是写一行换一行,刷新)BufferedWriter->newLine(); //当用户点击发送按钮的时候写出数据 bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); //循环读取数据并拼接到文本域 String line = null; while((line = br.readLine())!=null){ //将读取的数据拼接到文本域中显示 jta.append(line+System.lineSeparator()); } //5.关闭socket 通道 serverSocket.close(); }catch (IOException e) { e.printStackTrace(); } /**************** TCP 服务端 end *********************/ } @Override public void actionPerformed(ActionEvent actionEvent) { //System.out.println("发送按钮被点击了"); sendDataToSocket(); } //行为 @Override public void keyPressed(KeyEvent e) { //回车键 // System.out.println(e); //发送数据到socket 同道中 if(e.getKeyCode()==KeyEvent.VK_ENTER) { sendDataToSocket(); } } //定义一个方法,实现将数据发送到socket通道中 private void sendDataToSocket(){ //1.获取文本框中发送的内容 String text = jtf.getText(); //2.拼接需要发送的数据内容 text = "服务端对客户端说:"+text; //3.自己也要显示 jta.append(text+System.lineSeparator()); //4.发送 try { bw.write(text); bw.newLine();//换行刷新 bw.flush(); //5.清空文本框内容 jtf.setText(""); } catch (IOException e) { e.printStackTrace(); } } @Override public void keyTyped(KeyEvent keyEvent) { } @Override public void keyReleased(KeyEvent keyEvent) { } } 客户端: import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JPanel;import javax.swing.JScrollPane;import javax.swing.JTextArea;import javax.swing.JTextField;import java.awt.BorderLayout;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.net.Socket;import java.util.Properties; //说明:如果一个类,需要有界面的显示,那么这个类就需要继承JFrame,此时,该类可以被成为一个窗体类/*步骤: 1.定义JFrame窗体中的组件 2.在构造方法中初始化窗口的组件 */ public class ClientChatMain extends JFrame implements ActionListener, KeyListener { public static void main(String[] args) { //调用构造方法 new ClientChatMain(); } //属性 //文本域 private JTextArea jta; //滚动条 private JScrollPane jsp; //面板 private JPanel jp; //文本框 private JTextField jtf; //按钮 private JButton jb; //s输出流 private BufferedWriter bw =null; //客户端的IP地址 // private static String clientIp; //客户端的port端口号 // private static int clientPort; //静态代码块加载外部配置文件 /* static{ Properties prop = new Properties(); try { prop.load(new FileReader("chat.properties")); clientIp = prop.getProperty("clientIp"); clientPort =Integer.parseInt( prop.getProperty("clientPort")); } catch (IOException e) { e.printStackTrace(); } }*/ //构造方法 public ClientChatMain(){ //初始化组件 jta = new JTextArea(); //设置文本域默认不可编辑 jta.setEditable(false); //注意:需要将文本域添加到滚动条中,实现滚动效果 jsp =new JScrollPane(jta); //初始化面板 jp = new JPanel(); jtf = new JTextField(10); jb=new JButton("发送"); //注意:需要将文本框与按钮添加到面板中 jp.add(jtf); jp.add(jb); //注意:需要将滚动条与面板全部添加到窗体中,继承了窗体的属性,这里this就是窗体 this.add(jsp, BorderLayout.CENTER);//BorderLayout--边框布局 this.add(jp,BorderLayout.SOUTH); //注意:需要设置”标题“,大小,位置,关闭,是否可见 this.setTitle("QQ聊天客户端"); this.setSize(400,300); this.setLocation(700,300); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//窗体关闭,程序就退出 this.setVisible(true);//设置窗体可见 /**************** TCP 客户端 Start *********************/ //给发送按钮绑定一个监听点击事件 jb.addActionListener(this); //给文本框绑定一个键盘键 jtf.addKeyListener(this); try { //1.创建一个客户端的套接字(尝试连接) Socket socket = new Socket("127.0.0.1",8888); //2.获取socket通道的输入流 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); //3.获取socket 通道的输出流 bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); //循环读取数据,并拼接到文本域 String line = null; while((line = br.readLine())!=null){ jta.append(line + System.lineSeparator()); } //4.关闭socket 通道 socket.close(); }catch (Exception e){ e.printStackTrace(); } /**************** TCP 客户端 end *********************/ } @Override public void actionPerformed(ActionEvent e) { sendDataToSocket(); } //行为 @Override public void keyPressed(KeyEvent e) { //回车键 if(e.getKeyCode() == KeyEvent.VK_ENTER){ //发送数据 sendDataToSocket(); } } private void sendDataToSocket(){ //1.获取文本框需要发送内容 String text = jtf.getText(); //2.拼接内容 text = "客户端对服务端说"+text; //3.自己显示 jta.append(text+System.lineSeparator()); try { //4.发送 bw.write(text); bw.newLine();//换行加刷新 bw.flush(); bw.write(text); //5.清空 jtf.setText(""); } catch (IOException e) { e.printStackTrace(); } } @Override public void keyTyped(KeyEvent e) { } @Override public void keyReleased(KeyEvent e) { } } 接口类:public interface addActistener {}public interface addActionListener {原文链接:https://blog.csdn.net/m0_68089732/article/details/124897209
-
首先,介绍一下我自己,从普通二本毕业,到上海工作,不知不觉,已经两年时间了。说实话,自己还是一个菜鸟,总是在迷茫,从未去努力。刚毕业的时候,想着自己还年轻,菜就菜吧,工作一年就好了,毕竟工作的环境几乎都是比自己大,或者和自己同龄的年轻人,说不上当一天和尚撞一天钟吧,偶尔会去学习一些东西,但是努力确实谈不上。很多时候,也能发现自己和同事的差距,但是很少去思考为什么。在此还是要感叹一句C+V大法确实牛逼啊。目前,在这家公司已经呆了两年的时间了,期间也从未想过换工作,因为公司的氛围我还是蛮喜欢的,同事们都很友好,很少加班,即使加班了也可以换调休,在这里确实也学到了很多东西,但是我觉得差的很远。平时的工作怎么说呢,让我写代码还行,但是让我干别的,不不不,我真不会。我真的很害怕经理又让我建表,然后把我怼一顿,建的是个什么玩意(有些夸张,经理还是蛮好的,教了我很多东西);让我写一些项目里没有样例的代码,写完之后,把我叫过去,指着我的代码,删掉,然后敲一遍给我看,简直是折磨我啊(我还是挺佩服经理的,在我和他的多次博弈中,我好像没赢过)。我对我的总结就是,干啥啥不行,CV第一名。其实,公司用的技术还是蛮新的,springboot微服务+hibernate+vue,至少在现在还算是比较新的技术,我也很庆幸用的不是jsp(并没有看不起的意思,谁还没用过jsp是不是,手动狗头),哈哈哈。之前也有想过写博客,也确实写了两篇,但是那时候确实还是很太菜了,所以后来文章被我删掉了,因为当我回头去看的时候,我想说:写的是个什么玩意啊,连si都不如。。。我想可能有很多人和我一样吧,摸鱼的日子确实还是可以的,但是总觉得少了点什么,没有核心竞争力是件可怕的事情,短时间还看不出来,时间长了,你总是要被时代所抛弃的。以后的日子里,争取每周两篇博客,忙的话至少也要一篇,有关于自己的心得和一些技术的(大家千万别喷我)。这篇文章就当成是我csdn的开山之作吧,我并没有想要赞和评论的意思,但是那也算是对我的一种鼓励和认可吧(狗头奉上),谢谢大家原文链接:https://blog.csdn.net/qq_42281590/article/details/106183225
-
课程质量真的是好得没话说,我看过很多前端的课程,但从没有哪家课程能将前端的知识体系划分的如此全面细致,还能保证每一个知识点还都能讲得如此透彻,在讲知识点的基础上还能开篇幅去讲思想,更是难得。比如下面的函数式编程,这种编程范式我之前从来都没使用过,更不知道柯里化、函数组合为何物。直到在拉钩大前端课程中,每一个知识点的学习,都让我有种重获新生的感觉,仿佛以前学习的东西都白学了,只知道简单的用法,不了解核心原理,更不会用高级特性。现在每学习完一个模块,就期待着解锁下一个模块,迫不及待地想去知道下一个模块可以让自己get到哪些技能。
-
-
>摘要:几乎所有涉及应用数据交互的场景都可以通过DCM来改善应用结构,提升开发与计算效率。 本文分享自华为云社区《[DCM:中间件家族迎来新成员](https://bbs.huaweicloud.com/blogs/354299?utm_source=csdn&utm_medium=bbs-ex&utm_campaign=other&utm_content=content)》,作者: 石臻臻的杂货铺。 # DCM是什么 现代应用无时无刻不在与数据打交道,数据计算无处不在,报表统计、数据分析、业务处理不一而足。当前数据处理的主要手段仍然是以关系数据库为代表的相关技术,虽然使用高级语言(如Java)硬编码也能实现各类计算,但远不如数据库(SQL)方便,数据库在当代数据处理中仍然发挥举足轻重的作用。 不过,随着信息技术的发展,存储与计算分离、微服务、前置计算、边缘计算等架构与概念的兴起,过于沉重、封闭的数据库在应对这些场景时越来越显得捉襟见肘。数据库要求数据入库才能计算,但面对丰富的多样数据源时,数据入库不仅效率低资源消耗大,实时性也无法保障,而有的数据只是临时使用却要入库持久化就更得不偿失了。另外对于微服务、边缘计算等需要将计算能力前置到应用端的场景,数据库也很难嵌入使用。 在这样的背景下,如果有一种不依赖数据库、具备开放计算能力、能够与应用嵌入集成使用的数据计算处理技术,那么这些问题就都能够很好地解决,这就是数据计算中间件(Data Computing Middleware,简称DCM)。DCM的应用场景非常广泛,可以说无处不在,在优化应用开发、微服务实现、存储过程替代、数据库解耦、ETL辅助、多样性数据源计算、BI数据准备等等多方面都能发挥重要的作用,几乎所有涉及应用数据交互的场景都可以通过DCM来改善应用结构,提升开发与计算效率。 # DCM应用场景 ## 优化应用开发 应用中数据处理逻辑只能通过编码实现,使用原生的Java实现由于缺少必要的结构化计算类库往往比较困难,即使用新增加的Stream/Kotlin也并没有明显改善。借助ORM技术可以一定程度缓解开发困境,但仍然缺乏专业的结构化数据类型,集合运算不够方便,同时读写数据库时代码繁琐,复杂计算也难以实现。ORM的这些缺点经常导致业务逻辑的开发效率不仅没有明显提升,甚至还大幅降低。此外,这些实现方式还会导致应用结构问题。Java实现的计算逻辑必须与主应用一起部署导致紧耦合,同时由于不支持热部署开发运维也很麻烦。 如果借助DCM的敏捷计算、易集成、热切换等特性,在应用中替代Java实现数据处理逻辑,就可以很好解决上述问题,不仅开发效率提升,还可以优化应用结构,实现计算模块的解耦,同时支持热部署。  ## 多样性数据源计算 现代应用还经常面临多样性数据源问题,通过数据库处理不仅需要数据入库,效率低下,还无法保障数据的实时性。不同数据源有各自的优势,RDB计算能力较强,但IO吞吐能力弱;NoSQL的IO效率高,但计算能力很弱;而文本等文件数据完全没有计算能力,但使用非常灵活。强迫这些数据入库就会丧失这些原数据源的优势。 通过DCM的多源混算能力,不仅可以直接对RDB、文本、Excel、JSON、XML、NoSQL以及其他网络接口数据进行混合计算,保证数据与计算的实时性,而且还能同时保留各类数据源的优点,充分发挥其效力。  ## 微服务实现 当前微服务实现时仍然大量依赖Java和数据库实施数据处理,Java的缺点在于实现复杂、无法热切换;而数据库由于有“库”的限制,多源数据要入库才能计算,灵活性很低,不仅数据时效性无法保证,也无法充分发挥各类数据源的优势。 将可集成的DCM分别嵌入中台或微服务的各个环节完成数据采集整理、数据处理以及前置的数据计算任务,利用开放的计算体系可以充分发挥多数据源自身的优势,灵活性增强。多源数据处理、实时计算、热部署这些问题均能迎刃而解。  ## 存储过程替代 以往为了实现复杂计算或整理数据常常会使用存储过程,存储过程在库内计算有一定优势,但缺点也很明显。存储过程缺乏可移植性,编辑调试困难,创建和使用存储过程需要较高权限存在安全问题,为前端应用服务的存储过程还会造成数据库与应用紧耦合。 通过DCM将存储过程外置到应用中,可以实现“库外存储过程”,数据库则主要用于存储,将存储过程从数据库中解耦出来就可以很好解决存储过程带来的各类问题。  ## 报表BI数据准备 为报表提供数据准备是DCM的重要场景,以往使用数据库为报表准备数据存在实现难度高、耦合性强等问题,而报表本身计算能力不足又无法完成很多复杂计算。通过DCM的库外强计算能力就可以为报表提供一个专门的数据计算层,不仅可以解耦数据库为数据库减负,还可以弥补报表工具自身的计算能力不足。逻辑上分层后,报表开发维护都很清爽。  ## 中间表消除 有时为了加快查询效率事先将要查询的数据加工成结果表存储在数据库中,这就是中间表。另外,有些复杂计算需要保存中间结果也会存成中间表;多样数据源也要先存成中间表才能在数据库中混合计算。与存储过程类似,中间表一旦建立就可能被多个应用(模块)使用,造成应用与数据库的紧耦合,同时由于中间表无法轻易删除,数量会越积越多。中间表数量过多会引发数据库容量和性能问题,存储中间表需要空间,加工中间表则需要数据库计算资源。 通过DCM可以将中间表外置到文件系统,利用DCM实施计算,解耦数据库减轻数据库存储和计算负担。这里的关键是DCM使得文件也拥有了计算能力,所以才能将库内的中间表置于库外,原来中间表放在库内主要为了获得数据库的计算能力,现在有DCM的计算能力中间表存成什么形式就不重要了,外置到文件系统反而更优。  ## T+0查询 数据量积累到一定程度时基于生产库查询会影响交易,这时就会将大量的历史数据剥离到其他历史数据库中,进行冷热数据分离。这时如果要查询全量数据就要完成跨库查询、冷热数据路由等工作。数据库对于跨库查询尤其是跨异构库存在很多问题,不仅效率低下,还存在数据传输不稳定、可扩展性低等很多不足,无法很好实现T+0全量数据查询。 而这些问题都可以通过DCM来解决,由于具备独立且完善的计算能力,可以分别从不同的数据库取数计算,因此可以很好适应异构数据库的情况,还可以根据数据库的资源状况决定计算是在数据库还是DCM中实施,非常灵活。在计算实现上,DCM的敏捷计算能力还可以简化T+0查询中的复杂计算,提升开发效率。  ## ETL ETL需要对数据清洗转换再加载到目标端,但由于源端数据可能来源多处(文本、数据库、web)加上数据质量参差不齐,因此E和T这两个步骤会涉及大量数据计算。目前除了数据库以外,其他数据源并不太具备这样的计算能力,想要完成这些计算就要先加载到数据库再进行,这就形成了LET。大量无用的数据存储在数据库中会占用大量存储空间,极易引发容量问题。而将清洗和转换的计算工作都压给数据库又会增加数据处理时间,再叠加大量未经清洗转换的原始数据入库时间,有限的ETL时间窗口很可能不够,如果无法在规定时间完成ETL工作就会影响第二天的业务。 在ETL任务中引入DCM就可以按顺序完成清洗E、转换T、加载L,解决LET面临的各种问题。借助DCM的开放计算能力,在库外对多源数据实施清洗转换,DCM拥有强计算能力可以应对各类复杂计算,最后将整理后数据装在到目标端,实现真正的ETL。  # DCM特性 可以看到,DCM的应用场景非常广泛。那么要很好应对这些场景,一个优秀的DCM应该具备哪些特点呢? ## 兼容性(Compatible) 首先DCM需要具备很好的兼容性,可以跨平台使用,各类操作系统、云平台、应用服务器下均可以很好运行,这决定了DCM的使用范围。 此外,兼容性还意味着可以兼容多样性数据源,无论何种数据源都可以直接使用并进行混合计算,这要求DCM拥有足够强的开放性。 ## 热部署(Hot-deploy) 数据处理是一种高频且稳定性较差的场景,在业务开展过程中经常要新增修改计算任务,这就要求DCM应该具备热部署特性,修改数据处理逻辑无需重启应用(服务)就能生效。 ## 高性能(Efficient) 计算性能是数据计算场景重点关注的方面,有时会成为最主要的关注点,所谓天下武功无快不破。DCM应该能够高效处理数据,提供诸如高性能计算库、高性能存方案、并行计算等高性能保障机制。 ## 敏捷性(Agile) 敏捷性要求DCM能够快速实现数据处理逻辑,具备完备的计算能力,尤其面对复杂计算场景通过足够简单的编码就能完成数据处理,同时可以高效运行。这需要DCM提供敏捷编程机制和易于使用的开发环境等支持。 ## 扩展性(Scalable) 当计算容量无法满足需要时,DCM应该具备灵活的横向扩展能力。扩展性对当代应用十分重要,扩展能力的好坏决定了DCM的上限。 ## 集成性(Embeddable) DCM应该能够很好与应用集成嵌入使用,在应用内充当计算引擎,作为应用的一部分随应用一起打包部署。这样应用本身就获得了强计算能力,不再强依赖数据库后,可以很好应对存储与计算分离、微服务和边缘计算等场景。并且,良好的集成性还是敏捷性的另一方面体现,DCM很轻,随时随地都能嵌入与应用结合使用。 如果将DCM这几个特性的首字母组合起来,与CHEESE(奶酪)很接近(CHEASE),而DCM的作用就像夹在汉堡里的奶酪一样,如果缺少,味道和营养都会差很多。  这样能否作为理想的DCM就可以使用CHEASE的标准去考察。这里不妨看一下一些主流技术对DCM的满足情况。 # 现有技术的情况 ## SQL 数据库是使用SQL的主要阵地,数据库通常具备较强的计算能力,一些头部数据库的计算性能也很强,基本可以满足高性能(E)的需要。而且数据库过于封闭,数据要入库才能计算,无法很好满足多样性数据源场景的需要,兼容性(C)较差。 对于集成性(E),由于绝大部分数据库都是独立使用的,极少数(如SQLite)支持嵌入的数据库往往功能和性能都达不到要求,因此数据库几乎不满足集成性的要求。 而SQL作为专用的集合计算语言,实现简单计算很方便,但复杂计算用SQL表达很繁琐,经常要嵌套多层,实际业务中经常能看到几千行的“长”SQL,不仅难写,维护也不方便,所以SQL不太符合敏捷性(A)的要求。 与数据库类似的Hadoop相关技术也存在同样的问题,封闭性导致兼容性差、敏捷性不足、基本不具备集成性等缺点,虽然在扩展性方面表现要优于数据库,但总体并不符合DCM的要求。Spark的表现要略好,但Scala不支持热部署,实现复杂计算也不够方便,而Spark SQL仍然存在SQL的那些问题。这些技术都过于沉重,很难满足DCM在敏捷性、集成性、热部署等方面的需要。 ## Java Java作为原生的编程语言可以很好跨平台运行,也可以通过编码完成多数据源计算任务,因此兼容性(C)很好。而且对于大部分都采用Java开发的应用来说,集成性(E)也不在话下。 但Java的缺点也很明显,作为编译型语言无法实现热部署(H)。由于缺少必要的结构化计算类库完成简单的分组汇总也要几十行代码,就别提复杂计算了。虽然现在微服务架构中也经常使用Java硬编码完成数据处理,但其实计算实现要比SQL复杂得多,没办法,计算前置就不能再用数据库,难写也得挺着,因此敏捷性(A)极其不足。虽然在Java8以后引入了Stream,但计算能力并没有实质改善(Kotlin也存在类似的问题)。 使用Java虽然理论上也能实现各类高性能算法,但是如果只是为某个应用/项目服务,要实现这些高性能算法封装投入就太大了,因此从实际应用角度来看,Java并不具备高性能(E)特性。扩展性(S)也存在同样的问题。因此综合来看,Java很难作为优秀的DCM技术使用。 ## Python Python作为大火的一类计算技术不得不提一下。Python的兼容性(C)较强,无论是跨平台还是对接多数据源都能支持。尤其是丰富的数据处理包让Python的适用范围极广。 Python在结构化数据处理相比于Java等技术有相当的优势,但却难说很完善,尤其在处理有序分组等复杂计算时会很绕,Python在敏捷性(A)上略有所欠缺。 不仅如此,Pandas的性能(E)也往往达不到要求,尤其针对大数据量计算方面,这跟算法的实现效率有很大关系,敏捷语法可以很方便地实现高性能算法,反之就很困难。同样,在扩展性(S)方面,Python也不尽如人意,本质上来讲作为编程语言的Python要拥有良好的扩展性需要投入大量资源开发完成,这点与Java是一样的。 Python最大的问题是集成性(E),很难与现有应用集成在一起使用。虽然可以通过诸如sidecar模式进行服务间调用,但本质上与DCM要求与应用结合嵌入在一起(同一个进程)相去甚远。Python的主要应用场景并非像Java一样做企业级应用开发,各有用途,勉强不来。归根到底,专业的事儿还需要专业的工具来做。 # 专业数据计算中间件SPL 开源集算器SPL是专业的数据计算中间件,具备不依赖数据库的完备计算能力,同时开放的计算能力可以混合计算多样性数据,同时解释执行的SPL天然支持热部署,良好的集成性可以很方便嵌入应用中,让应用拥有强计算能力,充分发挥DCM的效力。 ## 兼容性 SPL采用Java开发,跨平台能力与Java一致,可以很好运行在各类操作系统、云平台下。而在多数据源支持方面,SPL具备开放的计算能力,可以对接多种数据源,RDB、NoSQL、CSV、Excel、JSON/XML、Hadoop、RESTful、Webservice都可以直接对接并进行混合计算,不需要入库,数据实时性和计算实时性都可以很好保障。  多源计算支持很好解决了原来数据库无法跨源计算、无法计算外部数据的问题,再加上SPL完备的计算能力和相对SQL更简洁的语法,对于应用来说就获得了与数据库相当(超过)的计算能力。 除了原生计算语法,SPL还提供了SQL支持(相当SQL92标准),可以使用SQL查询文本、Excel、NoSQL等非RDB数据源,这样就极大方便了熟悉SQL的应用开发人员。  DCM只有在开放计算体系的支持下才能拥有足够强的兼容性,才能适应更多的应用场景。 ## 热部署 SPL采用解释执行机制,天然支持热部署。这样对于一些稳定性差经常需要新增、修改计算逻辑的业务(如报表、微服务)非常友好。  ## 高性能 在性能方面,SPL提供了诸多高性能算法与高性能存储机制。在前面提到的DCM消除中间表和ETL场景中,数据往往要落地成文件存储在数据库外,这时采用SPL的文件格式存储可以获得比文本等开放格式高很多的性能。 SPL提供了两种存储类型:集文件和组表。集文件采用了压缩技术(占用空间更小读取更快),存储了数据类型(无需解析数据类型读取更快),支持可追加数据的倍增分段机制,利用分段策略很容易实现并行计算,保证计算性能。组表支持列式存储,在参与计算的列数(字段)较少时会有巨大优势。组表上还实现了minmax索引,同时支持倍增分段,这样不仅能享受到列存的优势,也更容易并行提升计算性能。 SPL还支持各种高性能算法。比如常见的TopN运算,在SPL中TopN被理解为聚合运算,这样可以将高复杂度的排序转换成低复杂度的聚合运算,而且很还能扩展应用范围。  这里的语句中没有排序字样,也不会产生大排序的动作,在全集还是分组中计算TopN的语法基本一致,而且都会有较高的性能,类似的算法在SPL中还有很多。 SPL也很容易实施并行计算,发挥多CPU的优势。SPL有很多计算函数都提供并行机制,如文件读取、过滤、排序只要增加一个@m选项就可以自动实施并行计算,简单方便。同时也可以显示编写并行程序,通过多线程并行提升计算性能。  ## 敏捷性 SPL提供了原生的计算语法和简洁易用的IDE环境,在IDE中不仅可以很方便编码调试,过程计算的每步计算结果都可以实时查看,网格式编码代码天然整齐,通过格子名称引用中间计算结果无需定义变量,简单方便。  同时,基于SPL丰富的计算类库实施结构化数据计算更方便,分组汇总、循环、过滤、集合运算、有序计算等应有尽有。  SPL尤其擅长复杂计算,原来SQL要嵌套很多层的计算使用SPL却可以很方便实现。比如根据股票记录计算某只股票最长连续上涨多少天?SPL就比SQL简单很多。  上面SQL嵌套了3层,读起来都很绕就别提写了;下面的SPL完全按照自然思维、简单3行就能实现,高下立判。 良好的敏捷性不仅能提升开发效率,很多高性能算法通过SPL可以很方便实现。算法不仅要能想出来,还要能实现,最好实现还简单,SPL提供了这种可能。 ## 扩展性 对于计算性能要求较高的场景,SPL还可以部署单独的计算服务,同时支持多机分布式集群,支持负载均衡和容错机制,当计算资源达到上限时可以通过横向扩容增加算力,具备良好的扩展性。 在分布式计算中,用户可根据数据和计算任务的特点灵活定制数据分布及冗余方案,有效减少节点间数据传输量,以获得更高性能,实现可控数据分布。 SPL采用无中心集群设计,集群没有永久的中心主控节点,允许程序员用代码控制参与计算的节点,从而有效避免单点失效。同时SPL会根据每个节点空闲程度(线程数量)决定是否分配任务,实现负担和资源的有效平衡。 在容错方面,SPL提供内外存两种数据容错机制,外存冗余式容错和内存备胎式容错。支持计算容错,节点故障时自动将该节点计算任务迁移掉其他节点继续完成。 ## 集成性 作为DCM与应用结合方面,SPL提供了标准JDBC/ODBC/RESTful接口,应用可以像调用存储过程一样请求SPL计算结果。  逻辑上SPL作为DCM介于应用和数据源之间实施数据处理,对上提供计算服务,对下屏蔽多样性数据源差异,充分彰显了DCM的重要作用。 JDBC调用SPL 代码示例: ``` Class.forName("com.esproc.jdbc.InternalDriver"); Connection conn =DriverManager.getConnection("jdbc:esproc:local://"); CallableStatement st = conn.prepareCall("{call splscript(?, ?)}"); st.setObject(1, 3000); st.setObject(2, 5000); ResultSet result=st.execute(); ``` 综合起来,从DCM的6个特性(CHEASE)来看,SPL在各方面能力综合起来十分均衡,整体远优于其他技术,是DCM的理想选择。  # SPL资料 - SPL官网 - SPL下载 - [SPL源代码](https://github.com/SPLWare/esProc)
-
学习一门新的编程语言当熟练了Java之后,再去学习新的编程语言,比如Python,这个时候不仅能够很快的学习好Python,Java语言能力也在迅速提高因为语言是相通的,当学习Python的时候,会带着和Java相比较的心去学,这个时候,不仅学习了Python,也加深了对Java的理解尝试独立完成一个项目独立完整地完成一个项目,可以更全面的了解项目的构成重温经典书籍意识到操作系统,计算机网络,编译原理,数据结构与算法,数据库知识的重要性动物书:O’Reilly出版的系列书犀牛书蝴蝶书图灵书: 人民邮电大学出版社黑皮书: 机械工业出版社异步图书清华大学出版社《设计模式》认真研读源码查看源码一方面可以了解看的东西是如何实现的,用到的算法,数据结构学习代码的架构使用Google进行搜索使用谷歌可以更加快速的解决开发时遇到的问题还能翻查Github,Stack Overflow上的博客使用英文文档最主流,最新鲜,最正确的技术文章都是英文当开始阅读英文技术文档之后:明显感觉学的东西都是很多书上没有的知识点更加细节也更加系统编程不应该死记硬背,要善于查阅技术文档国外的技术文档写的清晰又详细,都有上手特别容易的QuickStart有最全面的API,而且很多新特性也能先人一步用起来Stack OverflowRedditGithub深入学习技术疯狂追求技术上的细节追求更深处的实现细节,理解语言的思想以及应用场景下的解决方案,并养成对方案问为什么的准备注重培养自己的技术能力,阅读并参与到一些开源项目中,进入自己喜欢的技术细节领域编码时先思考再写:每个需求的思考占据70%编码的时间只要20% - 30%追求问题的完美解决方案,着重培养自己的工程能力,短时间之内理解新技术并投入使用理解语言只是解决问题的工具,思考编程语言对场景的适用性,学习和接纳新的编程语言并投入使用遇到问题,学会分析问题源头并寻找最合适的解决方法,学会阶段性的自我总结注重工程能力的培养,开始注重利用技术开发高稳定可用的完整产品,注重代码的结构,设计和规范链接:https://bbs.huaweicloud.com/blogs/354470
-
## 活动介绍 开源之夏是由“开源软件供应链点亮计划”发起并长期支持的一项暑期开源活动,旨在鼓励在校学生积极参与开源软件的开发维护,促进优秀开源软件社区的蓬勃发展,培养和发掘更多优秀的开发者。 学生可在本活动中自主选择感兴趣的项目任务进行申请,并在中选后获得该开源项目资深维护者(社区导师)亲自指导的机会,完成项目并贡献给社区后,参与学生还将获得开源之夏活动奖金和结项证书。 ## 项目介绍 本期开源之夏活动,openEuler Compiler SIG发布了4个课题,内容涉及编译器语言前端、动态编译性能优化、lib库和兼容性等。随着硬件和软件的协同设计变得更加重要,编译技术正处在LLVM之父Chris Lattner所说“编译器的黄金时代”,尤其在国内,以编译技术为代表的基础软件技术的发展越来越受重视,国之重器。欢迎各位同学积极参与申请报名,与华为资深编译专家畅游编译海洋,共享编译知识盛宴。 ### 项目一:源码中矩阵乘识别 **项目导师**:Eric_Ch96(chendewen3@huawei.com) **项目描述**:基于C、C++语言和开源的LLVM框架,在Clang前端的抽象语法树(Abstract Syntax Tree,AST)识别出典型的矩阵乘操作,并将矩阵信息以Metadata的形式添加在LLVM IR上。 **产出要求**: - 自定义包含矩阵乘信息的metadata,矩阵信息如矩阵大小、是否转置等重要信息。 - 以个人仓库形式提交代码,实现基于Clang AST,识别出典型的矩阵乘操作,并将识别信息添加至LLVM IR,并添加端到端测试用例。 **技术要求**: - 熟练掌握C、C++语言 - 对编译器前端有一定了解,熟悉前端的流程,尤其是AST - 熟悉开源Clang、LLVM的整体框架,对源码有一定的理解 - 熟练使用Linux ### 项目二:在BGMProvider中使用Java实现国密相关算法以及国密证书解析 **项目导师**:xiezhaokun(xiezhaokun@huawei.com) **项目描述**:BGMProvider是为毕昇JDK生态提供国密TLS协议Java实现,它包括 jca、jsse、tomcat-adaptor等模块。目前BGMProvider jca模块实现的国密相关算法依赖于bouncycastle。为了将来更好地扩展,需要将BGMProvider和bouncycastle解耦,并且在BGMProvider上实现国密相关算法。 **产出要求**: - 实现SM2/SM3/SM4/HmacSM3/SM3WithSM2国密算法以及相关功能测试用例。 - BGMProvider与bouncycastle解耦,去除与bouncycastle相关类的依赖。 **技术要求**: - 熟练掌握Java语言 - 熟悉JDK Service Provider机制,熟悉TLS协议 ### 项目三:毕昇编译器应用迁移兼容性问题的解决 **项目导师**:eastb233(xiezhiheng@huawei.com) **项目描述**:为了构建毕昇编译器生态,毕昇编译器已融入openEuler yum源,并持续迁移各个领域的应用,在迁移过程中需要解决和其他编译器的兼容性问题。 **产出要求**: - 按问题描述实现功能,并输出相关测试用例。 - 支持高扩展性,如:以尽量少的代码修改量实现添加更多的屏蔽/替换选项。 **技术要求**: - 掌握编译器的使用,如GCC、LLVM的使用 - 熟悉编译器源码的架构和调试方法 - 掌握编程语言C、C++ ### 项目四:毕昇Fortran编译器内联动态库函数str_copy **项目导师**:peixin-qiao(qiaopeixin@huawei.com) **项目描述**:毕昇Fortran编译器是一款基于classic flang的高性能Fortran编译器,支持Fortran编程语言的编译和运行,提供强大的数值计算和数据处理能力,在科学计算领域应用前景广阔。str_copy是一个实现字符串拷贝功能的动态库函数,本项目是对该动态库函数进行内联,预期提高编译器字符串拷贝的性能。 **产出要求**: - 以patch的方法提交代码,实现str_copy内联。 - 场景有性能提升。 **技术要求**: - 掌握C语言 - 了解汇编的同学优先 ## 申请资格 - 本活动面向年满 18 周岁在校学生。 - 暑期即将毕业的学生,只要在申请时学生证处在有效期内,就可以提交申请。 - 海外学生可提供录取通知书、学生卡、在读证明等文件用于证明学生身份。 ## 活动日程  ## Compiler SIG介绍 Compiler SIG 专注于编译器领域技术交流探讨和分享,包括 GCC/LLVM/OpenJDK 以及其他的程序优化技术,聚集编译技术领域的学者、专家、学术等同行,共同推进编译相关技术的发展。 Compiler SIG 每双周周二上午10:00进行线上例会,也会定期举办线下沙龙,以议题驱动方式进行编译器领域学习交流分享。我们希望加入 SIG 群组的每一位开发者都能在编译器领域有所成长和收获。 扫码添加 SIG 小助手微信,邀请你进 Compiler SIG 微信交流群。  原文转载自毕昇编译-[开源之夏 2022 | 与Compiler SIG一起在编译技术的海洋徜徉](https://mp.weixin.qq.com/s/QZyzjsyZBRWoHZjWntmm_g )
-
PriorityQueue常用方法:下面具体通过Integer(它实现了Comparable,所以有比较能力,PriorityQueue能选出优先级高的元素)演示一下:public class Main { public static void main(String[] args) { Queue<Integer> queue = new PriorityQueue<>(); //入队列 queue.add(1); queue.add(999); queue.add(45); queue.add(3); queue.add(66); //看看队首元素 System.out.println("对首元素" + queue.element()); //出队列 queue.remove(); //看看对首元素、 System.out.println("对首元素" + queue.element()); //查看队列是否为空 System.out.println(queue.isEmpty()); //输出当前队列大小 System.out.println(queue.size()); }}
-
1、>> 带符号右移正数,在高位补0负数,在高位补12、>>> 无符号右移无论正数负数都在高位补0
-
AR502H容器安装java应用失败提示以下内容:
-
链接:https://bbs.huaweicloud.com/blogs/350656什么是集合概念对象的容器,实现了对对象常用的操作。和数组的区别数组长度固定,集合长度不固定。数组可以存储基本类型和引用类型,集合只能存储引用类型。导包import java.util.*;Collection体系Collection 父接口特点:代表一组任意类型的对象,无序、无下标、不能重复。创建集合: Collection collection = new ArrayList();常用方法添加元素 collection.add();删除元素collection.remove();collection.clear();遍历元素(重点)1. 使用增强for(因为无下标)for(Object object : collection){ }2. 使用迭代器java //haNext(); 有没有下一个元素 //next(); 获取下一个元素 //remove(); 删除当前元素 Iterator it = collection.iterator(); while(it.hasNext()){ String object = (String)it.next(); //强制 // 可以使用it.remove(); 进行移除元素 // collection.remove(); 不能用collection其他方法 会报并发修改异常 }判断 collection.contains(); collection.isEmpty();List 子接口特点:有序、有下标、元素可重复创建集合对象 List list = new ArrayList<>( );常用方法添加元素 list.add( ); 会对基本类型进行自动装箱删除元素 可以用索引 list.remove(0)当删除数字与索引矛盾时 对数字强转list.remove((Object) 10) 或 list.remove(new Integer(10))遍历1. 使用for遍历java for(int i = 0; i < lise.size(); i++){ System.out.print(list.get(i)); }2. 使用增强forfor(Object list: collection){ }3. 使用迭代器java Iterator it = collection.iterator(); while(it.hasNext()){ String object = (String)it.next(); //强转 // 可以使用it.remove(); 进行移除元素 // collection.remove(); 不能用collection其他方法 会报并发修改异常 }4. 使用列表迭代器 (注意和迭代器区别)java ListIterator li = list.listIterator(); while(li.hasNext()){ System.out.println(li.nextIndex() + ":" + li.next()); //从前往后遍历 } while(li.hasPrevious()){ System.out.println(li.previousIndex() + ":" + li.previous()); //从后往前遍历 }获取 list.indexOf( );返回子集合 sublist(x, y); 左闭右开List subList = list.subList(1, 3); 返回索引 1、2List实现类ArrayList 【重点】 - 数组结构实现,必须要连续空间,查询快、增删慢Vector - 数组结构实现,查询快、增删慢LinkedList - 双向链表结构实现,无需连续空间,增删快,查询慢ArrayList创建集合 ArrayList arrayList = new ArrayList<>();添加元素 arrayList.add();删除元素 arrayList.remove(new Student("name", 10));这里重写了 equals(this == obj) 方法java public boolean equals(Object obj){ //1 判断是不是同一个对象 if(this == obj){ return true; } //2 判断是否为空 if(obj == null){ return false; } //3 判断是否是Student类型 if(obj instanceof Student){ Student == (Student)obj; //4 比较属性 if(this.name.equals(s.getName()) && this.age == s.getAge()){ return true; } } //5 不满足条件返回false return false; }遍历元素【重点】1. 使用迭代器java Iterator it = arrayList.iterator(); while(it.hasNext()){ Student s = (Student)it.next(); //强转 }2. 列表迭代器java ListIterator li = arrayList.listIterator(); while(li.hasNext()){ Student s = (Student)li.next(); //从前往后遍历 } while(li.hasPrevious()){ Student s = (Student)li.previous();//从后往前遍历 }判断arrayList.contains(); 和 arrayList.isEmpty();查找arrayList.indexof();Vector创建集合 Vector vector = new Vector<>();增加、删除、判断同上遍历中枚举器遍历Enumeration en = vector.elements(); while(en.hasMoreElements()){ String o = (String)en.nextElement(); System.out.println(o); }LinkedList创建链表集合LinkedList li = new LinkedList<>();常用方法与List一致泛型本质是参数化类型,把类型作为参数传递常见形式有泛型类、泛型接口、泛型方法语法 T成为类型占位符,表示一种引用类型,可以写多个逗号隔开好处 1. 提高代码重用性 2. 防止类型转换异常,提高代码安全性泛型类// 写一个泛型类 public class MyGeneric<T>{ //使用泛型T //1 创建变量 T t; //2 泛型作为方法的参数 public void show(T t){ sout(t); } //3 泛型作为方法的返回值 public T getT(){ return t; } }// 使用泛型类 public class TestGeneric{ public static void main(String[] args){ //使用泛型类创建对象 // 注意: 1. 泛型只能使用引用类型 // 2. 不用泛型类型对象之间不能相互赋值 MyGeneric<String> myGeneric = new MyGeneric<String>(); myGeneric.t = "hello"; myGeneric.show("hello world!"); String string = myGeneric.getT(); MyGeneric<Integer> myGeneric2 = new MyGeneric<Integer>(); myGeneric2.t = 100; myGeneric2.show(200); Integer integer = myGeneric2.getT(); } }泛型接口语法:接口名注意:不能泛型静态常量泛型方法语法: 返回值类型public class MyGenericMethod{ //泛型方法 public <T> T show(T t){ sout("泛型方法" + t); return t; } } //调用 MyGenericMethod myGenericMethod = new MyGenericMethod(); myGenericMethod.show("字符串");// 自动类型为字符串 myGenericMethod.show(200);// integer类型 myGenericMethod.show(3.14);// double类型泛型集合概念:参数化类型、类型安全的集合,强制集合元素的类型必须一致特点:编译时即可检查,而非运行时抛出异常访问时,不必类型转换(拆箱)不同泛型之间应用不能相互赋值,泛型不存在多态Set集合特点:无序、无下标、元素不可重复方法:全部继承自Collection中的方法增、删、遍历、判断与collection一致HashSet 【重点】存储结构:哈希表(数组+链表+红黑树)存储过程(重复依据)根据hashCode计算保存的位置,如果位置为空,直接保存,若不为空,进行第二步再执行equals方法,如果equals为true,则认为是重复,否则形成链表特点基于HashCode计算元素存放位置 - 利用31这个质数,减少散列冲突 - 31提高执行效率 31 * i = (i << 5) - i 转为移位操作 - 当存入元素的哈希码相同时,会调用equals进行确认,如果结果为true,则拒绝后者存入新建集合 HashSet<String> hashSet = new HashSet<String>();添加元素 hashSet.add( );删除元素 hashSet.remove( );遍历操作1. 增强for for( type type : hashSet)2. 迭代器 Iterator<String> it = hashSet.iterator( );判断 hashSet.contains( ); hashSet.isEmpty();TreeSet特点基于排列顺序实现元素不重复实现SortedSet接口,对集合元素自动排序元素对象的类型必须实现Comparable接口,指定排序规则通过CompareTo方法确定是否为重复元素存储结构:红黑树创建集合 TreeSet<String> treeSet = new TreeSet<>()添加元素 treeSet.add();删除元素 treeSet.remove();遍历 1. 增强for 2. 迭代器判断 treeSet.contains();补充:TreeSet集合的使用Comparator 实现定制比较(比较器)Comparable 可比较的// 重写compare @override public int compare(Person o1, Person o2){ int n1 = o1.getAge()-o2.getAge(); int n2 = o1.getName().comareTo(o2.getName()); return n1 == 0 ? n2 : n1; }MapMap接口的特点1. 用于存储任意键值对(key - value) 2. 键:无序、无下标、不允许重复(唯一) 3. 值:无序、无下标、允许重复方法:1. V put(K key, V value) 将对象存到集合中,关联键值 2. Object get(Object key) 根据键获得对应的值 3. Set<K> 返回所有的Key 4. Collection<V> values() 返回包含所有值的Collection集合 5. Set<Map.Entry<K, V>> 键值匹配的Set集合Map接口的使用//创建Map集合 Map<String, String> map = new HashMap<>(); // 1. 添加元素 map.put("cn", "中国"); map.put("uk", "英国"); map.put("cn", "zhongguo"); // 会替换第一个 // 2. 删除 map.remove("uk"); // 3. 遍历 // 3.1 使用KeySet() //Set<String> keyset = map.keySet(); // 所有Key的set集合 for(String key : map.keyset){ sout(key + "---" + map.get(key)); } // 3.2 使用entrySet() //Set<Map.Entry<String, String>> entries = map.entrySet(); for(Map.Entry<String, String> entry : map.entries){ sout(entry.getKey() + "---" + entry.getValue(); }HashMap 【重点】存储结构:哈希表(数组+链表+红黑树)使用key可使hashcode和equals作为重复增、删、遍历、判断与上述一致原码分析总结:HashMap刚创建时,table是null,节省空间,当添加第一个元素时,table容量调整为16当元素个数大于阈值(16*0.75 = 12)时,会进行扩容,扩容后的大小为原来的两倍,目的是减少调整元素的个数当每个链表长度 >8 ,并且数组元素个数 ≥64时,会调整成红黑Hashtable线程安全,运行效率慢;不允许null作为key或是valuePropertieshashtable的子类,要求key和value都是string,通常用于配置文件的读取TreeMap实现了SortedMap接口(是map的子接口),可以对key自动排序Collection工具类概念:集合工具类,定义了除了存取以外的集合常用方法直接二分查找int i = Collections.binarySearch(list, x); 成功返回索引其他方法 : copy复制、reverse反转、shuffle打乱补充:// list转成数组 Integer[] arr = list.toArray(new Integer[10]); sout(arr.length); sout(Array.toString(arr)); // 数组转成集合 // 此时为受限集合,不能 添加和删除! String[] name = {"张三","李四","王五"}; List<String> list2 = Arrays.asList(names); // 把基本类型数组转为集合时,需要修改为包装类 Integer[] nums = {100, 200, 300, 400, 500}; List<Integer> list3 = Arrays.asList(nums);
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签