共享单车之数据分析

文章目录

  • 第1关:统计共享单车每天的平均使用时间
  • 第2关:统计共享单车在指定地点的每天平均次数
  • 第3关:统计共享单车指定车辆每次使用的空闲平均时间
  • 第4关:统计指定时间共享单车使用次数
  • 第5关:统计共享单车线路流量


第1关:统计共享单车每天的平均使用时间

任务描述
本关任务:使用Hbase的MapReduce对已经存在 Hbase 的共享单车运行数据进行分析,统计共享单车每天的平均使用时间,其中共享单车运行数据在Hbase的t_shared_bicycle表中(表结构可在编程要求中进行查看)。

相关知识
为了完成本关任务,你需要掌握:

如何配置Hbase的MapReduce类;
如何使用Hbase的MapReduce进行数据分析。
如何配置Hbase的MapReduce类
MapReduce是运行在Job上的一个并行计算框架,分为Map节点和Reduce节点。

Hbase提供了org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil的initTableMapperJob和initTableReducerJob两个方法来完成MapReduce的配置。

initTableMapperJob 方法:

/**
*在提交TableMap作业之前使用它。 它会适当地设置
* 工作。
*
* @param table要读取的表名。
* @param scan具有列,时间范围等的扫描实例。
* @param mapper要使用的mapper类。
* @param outputKeyClass输出键的类。
* @param outputValueClass输出值的类。
* @param job当前要调整的工作。 确保传递的作业是
*携带所有必要的HBase配置。
* @throws IOException设置细节失败。
*/
public static void initTableMapperJob(String table, Scan scan,
Class<? extends TableMapper> mapper, Class<?> outputKeyClass,
Class<?> outputValueClass, Job job)
throws IOException
/ **
initTableReducerJob 方法:

/**
*在提交TableReduce作业之前使用它。 它会
*适当设置JobConf。
*
* @param table输出表。
* @param reducer要使用的reducer类。
* @param job当前要调整的工作。
* @throws IOException确定区域计数失败时。
*/
public static void initTableReducerJob(String table,
Class<? extends TableReducer> reducer, Job job)
throws IOException

如何使用Hbase的MapReduce进行数据分析
下面我们以统计每个城市的酒店个数的例子来介绍MapReduce的Map节点和Reduce节点:

Map节点执行类需要继承抽象类TableMapper,实现其map方法,结构如下:

public static class MyMapper extends TableMapper<Text, DoubleWritable> {
@Override
protected void map(ImmutableBytesWritable rowKey, Result result, Context context) {

}

}
在 map 方法中可从输入表(原数据表)得到行数据,最后向 Reduce 节点 输出键值对(key/value) 。

String cityId = Bytes.toString(result.getValue(“cityInfo”.getBytes(), “cityId”.getBytes()));
DoubleWritable i = new DoubleWritable(1);
context.write(new Text(cityId),i);
下面介绍Reduce节点,Reduce节点执行类需要继承抽象类TableReducer,实现其reduce方法:

public static class MyTableReducer extends TableReducer<Text, DoubleWritable, ImmutableBytesWritable> {
@Override
public void reduce(Text key, Iterable values, Context context) {

}
}
在reduce方法里会接收map 方法里相同key 的集合,最后把结果存到输出到表里。

double sum = 0;
for (DoubleWritable num:values){
sum += num.get();
}
Put put = new Put(Bytes.toBytes(key.toString()));
put.addColumn(“total_infos”.getBytes(),“total”.getBytes(),Bytes.toBytes(String.valueOf(sum)));
context.write(null,put);//initTableReducerJob 设置了表名所以在这里无需设置了
编程要求
在右侧代码窗口完成代码编写:

MapReduce类已经配置好,只需完成MapReduce的数据分析;

在map方法中,获取输入表t_shared_bicycle的相关信息,计算出使用时间=结束时间 - 开始时间,并把使用时间和开始时间的日期传给reduce

在reduce方法中通过使用时间和开始时间的日期计算共享单车每天平均使用时间,并把每天平均使用时间,四舍五入保留两位有效数字,存入到列族为info,字段为avgTime,ROWKEY 为avgTime的表里。

t_shared_bicycle表结构如下:

列族名称 字段 对应的文件的描述 ROWKEY (格式为:骑行id)
info beginTime 开始时间 trip_id
info endTime 结束时间 trip_id
info bicycleId 车辆id trip_id
info departure 出发地 trip_id
info destination 目的地 trip_id
info city 所在城市 trip_id
info start_longitude 开始经度 trip_id
info stop_longitude 结束经度 trip_id
info start_latitude 开始纬度 trip_id
info stop_latitude 结束纬度 trip_id
测试说明
平台会对你编写的代码进行测试,若是与预期输出相同,则算通关。

开始你的任务吧,祝你成功!
示例代码如下:

package com.educoder.bigData.sharedbicycle;
import java.io.IOException;
import java.text.ParseException;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Scanner;
import java.math.RoundingMode;
import java.math.BigDecimal;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.BytesWritable;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.util.Tool;
import com.educoder.bigData.util.HBaseUtil;
/*** 统计共享单车每天的平均使用时间*/
public class AveragetTimeMapReduce extends Configured implements Tool {public static final byte[] family = "info".getBytes();public static class MyMapper extends TableMapper<Text, BytesWritable> {protected void map(ImmutableBytesWritable rowKey, Result result, Context context)throws IOException, InterruptedException {/********** Begin *********/long beginTime = Long.parseLong(Bytes.toString(result.getValue(family, "beginTime".getBytes())));long endTime = Long.parseLong(Bytes.toString(result.getValue(family, "endTime".getBytes())));// 转化为yyyy-MM-ddString format = DateFormatUtils.format(beginTime, "yyyy-MM-dd", Locale.CHINA);// 计算时间long useTime = endTime - beginTime;// 拼装value ,包含日期 + 使用时间BytesWritable bytesWritable = new BytesWritable(Bytes.toBytes(format + "_" + useTime));context.write(new Text("avgTime"), bytesWritable);/********** End *********/}}public static class MyTableReducer extends TableReducer<Text, BytesWritable, ImmutableBytesWritable> {@Overridepublic void reduce(Text key, Iterable<BytesWritable> values, Context context)throws IOException, InterruptedException {/********** Begin *********/double sum = 0;int length = 0;Map<String, Long> map = new HashMap<String, Long>();for (BytesWritable price : values) {byte[] copyBytes = price.copyBytes();String string = Bytes.toString(copyBytes);String[] split = string.split("_");if (map.containsKey(split[0])) {Long integer = map.get(split[0]) + Long.parseLong(split[1]);map.put(split[0], integer);} else {map.put(split[0], Long.parseLong(split[1]));}}// 统计map value平均值Collection<Long> values2 = map.values();for (Long i : values2) {length++;sum += i;}BigDecimal decimal = new BigDecimal(sum / length /1000);BigDecimal setScale = decimal.setScale(2, RoundingMode.HALF_DOWN);Put put = new Put(Bytes.toBytes(key.toString()));put.addColumn(family, "avgTime".getBytes(), Bytes.toBytes(setScale.toString()));context.write(null, put);// initTableReducerJob 设置了 表名所以在这里无需设置了/********** End *********/}}public int run(String[] args) throws Exception {// 配置JobConfiguration conf = HBaseUtil.conf;// Scanner sc = new Scanner(System.in);// String arg1 = sc.next();// String arg2 = sc.next();String arg1 = "t_shared_bicycle";String arg2 = "t_bicycle_avgtime";try {HBaseUtil.createTable(arg2, new String[] { "info" });} catch (Exception e) {// 创建表失败e.printStackTrace();}Job job = configureJob(conf, new String[] { arg1, arg2 });return job.waitForCompletion(true) ? 0 : 1;}private Job configureJob(Configuration conf, String[] args) throws IOException {String tablename = args[0];String targetTable = args[1];Job job = new Job(conf, tablename);Scan scan = new Scan();scan.setCaching(300);scan.setCacheBlocks(false);// 在mapreduce程序中千万不要设置允许缓存// 初始化Mapreduce程序TableMapReduceUtil.initTableMapperJob(tablename, scan, MyMapper.class, Text.class, BytesWritable.class, job);// 初始化ReduceTableMapReduceUtil.initTableReducerJob(targetTable, // output tableMyTableReducer.class, // reducer classjob);job.setNumReduceTasks(1);return job;}
}

在这里插入图片描述

第2关:统计共享单车在指定地点的每天平均次数

任务描述
本关任务:使用Hbase的MapReduce对已经存在 Hbase 的共享单车运行数据进行分析,统计共享单车每天在指定地点的平均次数,其中共享单车运行数据在Hbase的t_shared_bicycle表中(表结构可在编程要求中进行查看)。

相关知识
为了完成本关任务,你需要掌握:

如何配置Hbase的MapReduce类;
如何使用Hbase的MapReduce进行数据分析;
如何使用过滤器过滤读取到的数据。
如何配置Hbase的MapReduce类
MapReduce是运行在Job上的一个并行计算框架,分为Map节点和Reduce节点。

Hbase提供了org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil的initTableMapperJob和initTableReducerJob两个方法来完成MapReduce的配置。

initTableMapperJob方法:

/**
*在提交TableMap作业之前使用它。 它会适当地设置
* 工作。
*
* @param table要读取的表名。
* @param scan具有列,时间范围等的扫描实例。
* @param mapper要使用的mapper类。
* @param outputKeyClass输出键的类。
* @param outputValueClass输出值的类。
* @param job当前要调整的工作。 确保传递的作业是
*携带所有必要的HBase配置。
* @throws IOException设置细节失败。
*/
public static void initTableMapperJob(String table, Scan scan,
Class<? extends TableMapper> mapper, Class<?> outputKeyClass,
Class<?> outputValueClass, Job job)
throws IOException
/ **
initTableReducerJob方法:

/**
*在提交TableReduce作业之前使用它。 它会
*适当设置JobConf。
*
* @param table输出表。
* @param reducer要使用的reducer类。
* @param job当前要调整的工作。
* @throws IOException确定区域计数失败时。
*/
public static void initTableReducerJob(String table,
Class<? extends TableReducer> reducer, Job job)
throws IOException

如何使用Hbase的MapReduce进行数据分析
下面我们以统计每个城市的酒店个数的例子来介绍MapReduce的Map节点和Reduce节点:

Map节点执行类需要继承抽象类TableMapper,实现其map方法,结构如下:

public static class MyMapper extends TableMapper<Text, DoubleWritable> {
@Override
protected void map(ImmutableBytesWritable rowKey, Result result, Context context) {

}

}
在 map 方法中可从输入表(原数据表)得到行数据,最后向 Reduce 节点 输出键值对(key/value) 。

String cityId = Bytes.toString(result.getValue(“cityInfo”.getBytes(), “cityId”.getBytes()));
DoubleWritable i = new DoubleWritable(1);
context.write(new Text(cityId),i);
下面介绍Reduce节点,Reduce节点执行类需要继承抽象类TableReducer,实现其reduce方法:

public static class MyTableReducer extends TableReducer<Text, DoubleWritable, ImmutableBytesWritable> {
@Override
public void reduce(Text key, Iterable values, Context context) {

}
}
在reduce方法里会 接收map 方法里 相同key 的集合,最后把结果存到输出到表里。

double sum = 0;
for (DoubleWritable num:values){
sum += num.get();
}
Put put = new Put(Bytes.toBytes(key.toString()));
put.addColumn(“total_infos”.getBytes(),“total”.getBytes(),Bytes.toBytes(String.valueOf(sum)));
context.write(null,put);//initTableReducerJob 设置了表名所以在这里无需设置了
如何使用过滤器过滤读取到的数据
请查看 HBase高级特性:过滤器系列 。

编程要求
在右侧代码窗口完成代码编写:

MapReduce类需要进行配置,请在configureJob方法里配置表数据过滤器,过滤条件为:只获取目的地包含韩庄村,出发地为河北省保定市雄县的数据。
在map方法中,获取输入表t_shared_bicycle的相关信息,通过开始时间获取当天日期,并传入到reduce
在reduce方法中通过当天日期计算共享单车每天平均次数,并把每天次数,四舍五入保留两位有效数字,存入到列族为info,字段为avgNum,ROWKEY 为河北省保定市雄县-韩庄村的表里。
t_shared_bicycle表结构如下:

列族名称 字段 对应的文件的描述 ROWKEY (格式为:骑行id)
info beginTime 开始时间 trip_id
info endTime 结束时间 trip_id
info bicycleId 车辆id trip_id
info departure 出发地 trip_id
info destination 目的地 trip_id
info city 所在城市 trip_id
info start_longitude 开始经度 trip_id
info stop_longitude 结束经度 trip_id
info start_latitude 开始纬度 trip_id
info stop_latitude 结束纬度 trip_id
测试说明
平台会对你编写的代码进行测试,若是与预期输出相同,则算通关。

开始你的任务吧,祝你成功!
示例代码如下:

package com.educoder.bigData.sharedbicycle;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Scanner;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.filter.BinaryComparator;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
import org.apache.hadoop.hbase.filter.SubstringComparator;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.BytesWritable;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.util.Tool;
import com.educoder.bigData.util.HBaseUtil;
/*** 共享单车每天在韩庄村的平均空闲时间*/
public class AverageVehicleMapReduce extends Configured implements Tool {public static final byte[] family = "info".getBytes();public static class MyMapper extends TableMapper<Text, BytesWritable> {protected void map(ImmutableBytesWritable rowKey, Result result, Context context)throws IOException, InterruptedException {/********** Begin *********/// 时间String beginTime = Bytes.toString(result.getValue(family, "beginTime".getBytes()));// 转化为yyyy-MM-ddString format = DateFormatUtils.format(Long.parseLong(beginTime), "yyyy-MM-dd", Locale.CHINA);BytesWritable bytesWritable = new BytesWritable(Bytes.toBytes(format));context.write(new Text("河北省保定市雄县-韩庄村"), bytesWritable);/********** End *********/}}public static class MyTableReducer extends TableReducer<Text, BytesWritable, ImmutableBytesWritable> {@Overridepublic void reduce(Text key, Iterable<BytesWritable> values, Context context)throws IOException, InterruptedException {/********** Begin *********/double sum = 0;int length = 0;Map<String, Integer> map = new HashMap<String, Integer>();for (BytesWritable price : values) {byte[] copyBytes = price.copyBytes();String string = Bytes.toString(copyBytes);if (map.containsKey(string)) {Integer integer = map.get(string) + 1;map.put(string, integer);} else {map.put(string, new Integer(1));}}// 统计map value平均值Collection<Integer> values2 = map.values();for (Integer i : values2) {length++;sum += i;}BigDecimal decimal = new BigDecimal(sum / length);BigDecimal setScale = decimal.setScale(2, RoundingMode.HALF_DOWN);Put put = new Put(Bytes.toBytes(key.toString()));put.addColumn(family, "avgNum".getBytes(), Bytes.toBytes(setScale.toString()));context.write(null, put);// initTableReducerJob 设置了 表名所以在这里无需设置了/********** End *********/}}public int run(String[] args) throws Exception {// 配置JobConfiguration conf = HBaseUtil.conf;//Scanner sc = new Scanner(System.in);//String arg1 = sc.next();//String arg2 = sc.next();String arg1 = "t_shared_bicycle";String arg2 = "t_bicycle_avgnum";try {HBaseUtil.createTable(arg2, new String[] { "info" });} catch (Exception e) {// 创建表失败e.printStackTrace();}Job job = configureJob(conf, new String[] { arg1, arg2 });return job.waitForCompletion(true) ? 0 : 1;}private Job configureJob(Configuration conf, String[] args) throws IOException {String tablename = args[0];String targetTable = args[1];Job job = new Job(conf, tablename);Scan scan = new Scan();scan.setCaching(300);scan.setCacheBlocks(false);// 在mapreduce程序中千万不要设置允许缓存/********** Begin *********///设置过滤ArrayList<Filter> listForFilters = new ArrayList<Filter>();Filter destinationFilter = new SingleColumnValueFilter(Bytes.toBytes("info"), Bytes.toBytes("destination"),CompareOperator.EQUAL, new SubstringComparator("韩庄村"));Filter departure = new SingleColumnValueFilter(Bytes.toBytes("info"), Bytes.toBytes("departure"),CompareOperator.EQUAL, Bytes.toBytes("河北省保定市雄县"));listForFilters.add(departure);listForFilters.add(destinationFilter);scan.setCaching(300);scan.setCacheBlocks(false);// 在mapreduce程序中千万不要设置允许缓存Filter filters = new FilterList(listForFilters);scan.setFilter(filters);/********** End *********/// 初始化Mapreduce程序TableMapReduceUtil.initTableMapperJob(tablename, scan, MyMapper.class, Text.class, BytesWritable.class, job);// 初始化ReduceTableMapReduceUtil.initTableReducerJob(targetTable, // output tableMyTableReducer.class, // reducer classjob);job.setNumReduceTasks(1);return job;}
}

在这里插入图片描述

第3关:统计共享单车指定车辆每次使用的空闲平均时间

任务描述
本关任务:使用Hbase的MapReduce对已经存在Hbase 的共享单车运行数据进行分析,统计共享单车指定车辆每次使用的平均空闲时间,其中共享单车运行数据在Hbase的t_shared_bicycle表中(表结构可在编程要求中进行查看)。

相关知识
为了完成本关任务,你需要掌握:

如何配置Hbase的MapReduce类;
如何使用Hbase的MapReduce进行数据分析。
如何配置Hbase的MapReduce类
MapReduce是运行在Job上的一个并行计算框架,分为Map节点和Reduce节点。

Hbase提供了org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil的initTableMapperJob和initTableReducerJob两个方法来完成MapReduce的配置。

initTableMapperJob 方法:

/**
*在提交TableMap作业之前使用它。 它会适当地设置
* 工作。
*
* @param table要读取的表名。
* @param scan具有列,时间范围等的扫描实例。
* @param mapper要使用的mapper类。
* @param outputKeyClass输出键的类。
* @param outputValueClass输出值的类。
* @param job当前要调整的工作。 确保传递的作业是
*携带所有必要的HBase配置。
* @throws IOException设置细节失败。
*/
public static void initTableMapperJob(String table, Scan scan,
Class<? extends TableMapper> mapper, Class<?> outputKeyClass,
Class<?> outputValueClass, Job job)
throws IOException
/ **
initTableReducerJob方法:

/**
*在提交TableReduce作业之前使用它。 它会
*适当设置JobConf。
*
* @param table输出表。
* @param reducer要使用的reducer类。
* @param job当前要调整的工作。
* @throws IOException确定区域计数失败时。
*/
public static void initTableReducerJob(String table,
Class<? extends TableReducer> reducer, Job job)
throws IOException

如何使用Hbase的MapReduce进行数据分析
下面我们以统计每个城市的酒店个数的例子来介绍MapReduce的Map节点和Reduce节点:

Map节点执行类需要继承抽象类TableMapper,实现其map方法,结构如下:

public static class MyMapper extends TableMapper<Text, DoubleWritable> {
@Override
protected void map(ImmutableBytesWritable rowKey, Result result, Context context) {

}

}
在 map 方法中可从输入表(原数据表)得到行数据,最后向 Reduce 节点 输出键值对(key/value) 。

String cityId = Bytes.toString(result.getValue(“cityInfo”.getBytes(), “cityId”.getBytes()));
DoubleWritable i = new DoubleWritable(1);
context.write(new Text(cityId),i);
下面介绍Reduce节点,Reduce节点执行类需要继承抽象类TableReducer,实现其reduce方法:

public static class MyTableReducer extends TableReducer<Text, DoubleWritable, ImmutableBytesWritable> {
@Override
public void reduce(Text key, Iterable values, Context context) {

}
}
在reduce方法里会接收map 方法里 相同key 的集合,最后把结果存到输出到表里。

double sum = 0;
for (DoubleWritable num:values){
sum += num.get();
}
Put put = new Put(Bytes.toBytes(key.toString()));
put.addColumn(“total_infos”.getBytes(),“total”.getBytes(),Bytes.toBytes(String.valueOf(sum)));
context.write(null,put);//initTableReducerJob 设置了表名所以在这里无需设置了
编程要求
在右侧代码窗口完成代码编写:

MapReduce类需要进行配置,请在configureJob方法里配置表数据过滤器,过滤条件为:只获取车辆id为5996的数据。

在map方法中,获取输入表t_shared_bicycle的相关信息,获取开始时间和结束时间,并传入到reduce

在reduce方法中通过开始时间和结束时间计算每次使用的平均空闲时间,并把空闲时间单位转化为小时,四舍五入保留两位有效数字,存入到列族为info,字段为freeTime,ROWKEY 为5996的表里。

t_shared_bicycle表结构如下:

列族名称 字段 对应的文件的描述 ROWKEY (格式为:骑行id)
info beginTime 开始时间 trip_id
info endTime 结束时间 trip_id
info bicycleId 车辆id trip_id
info departure 出发地 trip_id
info destination 目的地 trip_id
info city 所在城市 trip_id
info start_longitude 开始经度 trip_id
info stop_longitude 结束经度 trip_id
info start_latitude 开始纬度 trip_id
info stop_latitude 结束纬度 trip_id
测试说明
平台会对你编写的代码进行测试,若是与预期输出相同,则算通关。

开始你的任务吧,祝你成功!
示例代码如下:

package com.educoder.bigData.sharedbicycle;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.BytesWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.util.Tool;
import com.educoder.bigData.util.HBaseUtil;
/*** * 统计5996共享单车每次使用的空闲平均时间*/
public class FreeTimeMapReduce extends Configured implements Tool {public static final byte[] family = "info".getBytes();public static class MyMapper extends TableMapper<Text, BytesWritable> {protected void map(ImmutableBytesWritable rowKey, Result result, Context context)throws IOException, InterruptedException {// 时间long beginTime = Long.parseLong(Bytes.toString(result.getValue(family, "beginTime".getBytes())));long endTime = Long.parseLong(Bytes.toString(result.getValue(family, "endTime".getBytes())));// 拼装value ,包含开始时间和结束时间BytesWritable bytesWritable = new BytesWritable(Bytes.toBytes(beginTime + "_" + endTime));context.write(new Text("5996"), bytesWritable);}}public static class MyTableReducer extends TableReducer<Text, BytesWritable, ImmutableBytesWritable> {@Overridepublic void reduce(Text key, Iterable<BytesWritable> values, Context context)throws IOException, InterruptedException {long freeTime = 0;long beginTime = 0;int length = 0;for (BytesWritable time : values) {byte[] copyBytes = time.copyBytes();String timeLong = Bytes.toString(copyBytes);String[] split = timeLong.split("_");if(beginTime == 0) {beginTime = Long.parseLong(split[0]);continue;}else {//空闲时间 = 本次开始时间 - 上一次结束时间 freeTime = freeTime + beginTime -  Long.parseLong(split[1]);//重新设置开始时间beginTime = Long.parseLong(split[0]);length ++;}}Put put = new Put(Bytes.toBytes(key.toString()));BigDecimal decimal = new BigDecimal(freeTime / length /1000 /60 /60);BigDecimal setScale = decimal.setScale(2, RoundingMode.HALF_DOWN);put.addColumn(family, "freeTime".getBytes(), Bytes.toBytes(setScale.toString()));context.write(null, put);// initTableReducerJob 设置了 表名所以在这里无需设置了}}public int run(String[] args) throws Exception {// 配置JobConfiguration conf = HBaseUtil.conf;// Scanner sc = new Scanner(System.in);// String arg1 = sc.next();// String arg2 = sc.next();String arg1 = "t_shared_bicycle";String arg2 = "t_bicycle_freetime";try {HBaseUtil.createTable(arg2, new String[] { "info" });} catch (Exception e) {// 创建表失败e.printStackTrace();}Job job = configureJob(conf, new String[] { arg1, arg2 });return job.waitForCompletion(true) ? 0 : 1;}private Job configureJob(Configuration conf, String[] args) throws IOException {String tablename = args[0];String targetTable = args[1];Job job = new Job(conf, tablename);Scan scan = new Scan();scan.setCaching(300);scan.setCacheBlocks(false);// 在mapreduce程序中千万不要设置允许缓存Filter filter = new SingleColumnValueFilter(Bytes.toBytes("info"), Bytes.toBytes("bicycleId"),CompareOperator.EQUAL, Bytes.toBytes("5996"));scan.setFilter(filter);// 初始化Mapreduce程序TableMapReduceUtil.initTableMapperJob(tablename, scan, MyMapper.class, Text.class, BytesWritable.class, job);// 初始化ReduceTableMapReduceUtil.initTableReducerJob(targetTable, // output tableMyTableReducer.class, // reducer classjob);job.setNumReduceTasks(1);return job;}
}

在这里插入图片描述

第4关:统计指定时间共享单车使用次数

任务描述
本关任务:使用Hbase的MapReduce对已经存在Hbase的共享单车运行数据进行分析,统计共享单车指定时间的使用次数,其中共享单车运行数据在Hbase的t_shared_bicycle表中(表结构可在编程要求中进行查看)。

相关知识
为了完成本关任务,你需要掌握:

如何配置Hbase的MapReduce类;
如何使用Hbase的MapReduce进行数据分析。
如何配置Hbase的MapReduce类
MapReduce是运行在Job上的一个并行计算框架,分为Map节点和Reduce节点。

Hbase提供了org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil的initTableMapperJob和initTableReducerJob两个方法来完成MapReduce的配置。

initTableMapperJob方法:

/**
*在提交TableMap作业之前使用它。 它会适当地设置
* 工作。
*
* @param table要读取的表名。
* @param scan具有列,时间范围等的扫描实例。
* @param mapper要使用的mapper类。
* @param outputKeyClass输出键的类。
* @param outputValueClass输出值的类。
* @param job当前要调整的工作。 确保传递的作业是
*携带所有必要的HBase配置。
* @throws IOException设置细节失败。
*/
public static void initTableMapperJob(String table, Scan scan,
Class<? extends TableMapper> mapper, Class<?> outputKeyClass,
Class<?> outputValueClass, Job job)
throws IOException
/ **
initTableReducerJob方法:

/**
*在提交TableReduce作业之前使用它。 它会
*适当设置JobConf。
*
* @param table输出表。
* @param reducer要使用的reducer类。
* @param job当前要调整的工作。
* @throws IOException确定区域计数失败时。
*/
public static void initTableReducerJob(String table,
Class<? extends TableReducer> reducer, Job job)
throws IOException

如何使用Hbase的MapReduce进行数据分析
下面我们以统计每个城市的酒店个数的例子来介绍MapReduce的Map节点和Reduce节点:

Map节点执行类需要继承抽象类TableMapper,实现其map方法,结构如下:

public static class MyMapper extends TableMapper<Text, DoubleWritable> {
@Override
protected void map(ImmutableBytesWritable rowKey, Result result, Context context) {

}

}
在 map 方法中可从输入表(原数据表)得到行数据 ,最后向 Reduce 节点 输出键值对(key/value) 。

String cityId = Bytes.toString(result.getValue(“cityInfo”.getBytes(), “cityId”.getBytes()));
DoubleWritable i = new DoubleWritable(1);
context.write(new Text(cityId),i);
下面介绍Reduce节点,Reduce节点执行类需要继承抽象类TableReducer,实现其reduce方法:

public static class MyTableReducer extends TableReducer<Text, DoubleWritable, ImmutableBytesWritable> {
@Override
public void reduce(Text key, Iterable values, Context context) {

}
}
在reduce方法里会接收map 方法里 相同key 的集合,最后把结果存到输出到表里。

double sum = 0;
for (DoubleWritable num:values){
sum += num.get();
}
Put put = new Put(Bytes.toBytes(key.toString()));
put.addColumn(“total_infos”.getBytes(),“total”.getBytes(),Bytes.toBytes(String.valueOf(sum)));
context.write(null,put);//initTableReducerJob 设置了表名所以在这里无需设置了
编程要求
在右侧代码窗口Begin-End处完成代码编写:

MapReduce类需要进行配置,请在configureJob方法里配置表数据过滤器,过滤条件为:只获取开始时间大于等于2017-08-01,结束时间小于等于2017-09-01的数据。
在map方法中,获取输入表t_shared_bicycle的相关信息,次数设为1,并传入到reduce
在reduce方法中通过次数计算共享单车使用总次数,并把总次数存入到列族为info,字段为usageRate,ROWKEY 为departure的表里。
t_shared_bicycle表结构如下:

列族名称 字段 对应的文件的描述 ROWKEY (格式为:骑行id)
info beginTime 开始时间 trip_id
info endTime 结束时间 trip_id
info bicycleId 车辆id trip_id
info departure 出发地 trip_id
info destination 目的地 trip_id
info city 所在城市 trip_id
info start_longitude 开始经度 trip_id
info stop_longitude 结束经度 trip_id
info start_latitude 开始纬度 trip_id
info stop_latitude 结束纬度 trip_id
beginTime 和 endTime 在 Hbase 中以时间戳的形式存储

测试说明
平台会对你编写的代码进行测试,若是与预期输出相同,则算通关。

开始你的任务吧,祝你成功!
示例代码如下:

package com.educoder.bigData.sharedbicycle;
import java.io.IOException;
import java.util.ArrayList;
import org.apache.commons.lang3.time.FastDateFormat;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.util.Tool;
import com.educoder.bigData.util.HBaseUtil;
/*** 共享单车使用次数统计*/
public class UsageRateMapReduce extends Configured implements Tool {public static final byte[] family = "info".getBytes();public static class MyMapper extends TableMapper<Text, IntWritable> {protected void map(ImmutableBytesWritable rowKey, Result result, Context context)throws IOException, InterruptedException {/********** Begin *********/// 次数IntWritable doubleWritable = new IntWritable(1);context.write(new Text("departure"), doubleWritable);/********** End *********/}}public static class MyTableReducer extends TableReducer<Text, IntWritable, ImmutableBytesWritable> {@Overridepublic void reduce(Text key, Iterable<IntWritable> values, Context context)throws IOException, InterruptedException {/********** Begin *********/        int totalNum = 0;for (IntWritable num : values) {int d = num.get();totalNum += d;}Put put = new Put(Bytes.toBytes(key.toString()));put.addColumn(family, "usageRate".getBytes(), Bytes.toBytes(String.valueOf(totalNum)));context.write(null, put);// initTableReducerJob 设置了 表名所以在这里无需设置了/********** End *********/}}public int run(String[] args) throws Exception {// 配置JobConfiguration conf = HBaseUtil.conf;// Scanner sc = new Scanner(System.in);// String arg1 = sc.next();// String arg2 = sc.next();String arg1 = "t_shared_bicycle";String arg2 = "t_bicycle_usagerate";try {HBaseUtil.createTable(arg2, new String[] { "info" });} catch (Exception e) {// 创建表失败e.printStackTrace();}Job job = configureJob(conf, new String[] { arg1, arg2 });return job.waitForCompletion(true) ? 0 : 1;}private Job configureJob(Configuration conf, String[] args) throws IOException {String tablename = args[0];String targetTable = args[1];Job job = new Job(conf, tablename);ArrayList<Filter> listForFilters = new ArrayList<Filter>();FastDateFormat instance = FastDateFormat.getInstance("yyyy-MM-dd");Scan scan = new Scan();scan.setCaching(300);scan.setCacheBlocks(false);// 在mapreduce程序中千万不要设置允许缓存/********** Begin *********/try {Filter destinationFilter = new SingleColumnValueFilter(Bytes.toBytes("info"), Bytes.toBytes("beginTime"),CompareOperator.GREATER_OR_EQUAL, Bytes.toBytes(String.valueOf(instance.parse("2017-08-01").getTime())));Filter departure = new SingleColumnValueFilter(Bytes.toBytes("info"), Bytes.toBytes("endTime"),CompareOperator.LESS_OR_EQUAL, Bytes.toBytes(String.valueOf(instance.parse("2017-09-01").getTime())));listForFilters.add(departure);listForFilters.add(destinationFilter);}catch (Exception e) {e.printStackTrace();return null;}Filter filters = new FilterList(listForFilters);scan.setFilter(filters);/********** End *********/// 初始化Mapreduce程序TableMapReduceUtil.initTableMapperJob(tablename, scan, MyMapper.class, Text.class, IntWritable.class, job);// 初始化ReduceTableMapReduceUtil.initTableReducerJob(targetTable, // output tableMyTableReducer.class, // reducer classjob);job.setNumReduceTasks(1);return job;}
}

在这里插入图片描述

第5关:统计共享单车线路流量

任务描述
本关任务:使用Hbase的MapReduce对已经存在Hbase的共享单车运行数据进行分析,统计共享单车线路次数,其中共享单车运行数据在Hbase的t_shared_bicycle表中(表结构可在编程要求中进行查看)。

相关知识
为了完成本关任务,你需要掌握:

如何配置Hbase的MapReduce类;
如何使用Hbase的MapReduce进行数据分析。
如何配置Hbase的MapReduce类
MapReduce是运行在Job上的一个并行计算框架,分为Map节点和Reduce节点。

Hbase提供了org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil的initTableMapperJob和initTableReducerJob两个方法来完成MapReduce的配置。

initTableMapperJob 方法:

/**
*在提交TableMap作业之前使用它。 它会适当地设置
* 工作。
*
* @param table要读取的表名。
* @param scan具有列,时间范围等的扫描实例。
* @param mapper要使用的mapper类。
* @param outputKeyClass输出键的类。
* @param outputValueClass输出值的类。
* @param job当前要调整的工作。 确保传递的作业是
*携带所有必要的HBase配置。
* @throws IOException设置细节失败。
*/
public static void initTableMapperJob(String table, Scan scan,
Class<? extends TableMapper> mapper, Class<?> outputKeyClass,
Class<?> outputValueClass, Job job)
throws IOException
/ **
initTableReducerJob方法:

/**
*在提交TableReduce作业之前使用它。 它会
*适当设置JobConf。
*
* @param table输出表。
* @param reducer要使用的reducer类。
* @param job当前要调整的工作。
* @throws IOException确定区域计数失败时。
*/
public static void initTableReducerJob(String table,
Class<? extends TableReducer> reducer, Job job)
throws IOException

如何使用Hbase的MapReduce进行数据分析
下面我们以统计每个城市的酒店个数的例子来介绍MapReduce的Map节点和Reduce节点:

Map节点执行类需要继承抽象类TableMapper,实现其map方法,结构如下:

public static class MyMapper extends TableMapper<Text, DoubleWritable> {
@Override
protected void map(ImmutableBytesWritable rowKey, Result result, Context context) {

}

}
在 map 方法中可从输入表(原数据表)得到行数据 ,最后向 Reduce 节点 输出键值对(key/value) 。

String cityId = Bytes.toString(result.getValue(“cityInfo”.getBytes(), “cityId”.getBytes()));
DoubleWritable i = new DoubleWritable(1);
context.write(new Text(cityId),i);
下面介绍Reduce节点,Reduce节点执行类需要继承抽象类TableReducer,实现其reduce方法:

public static class MyTableReducer extends TableReducer<Text, DoubleWritable, ImmutableBytesWritable> {
@Override
public void reduce(Text key, Iterable values, Context context) {

}
}
在reduce方法里会接收map 方法里 相同key 的集合,最后把结果存到输出到表里。

double sum = 0;
for (DoubleWritable num:values){
sum += num.get();
}
Put put = new Put(Bytes.toBytes(key.toString()));
put.addColumn(“total_infos”.getBytes(),“total”.getBytes(),Bytes.toBytes(String.valueOf(sum)));
context.write(null,put);//initTableReducerJob 设置了表名所以在这里无需设置了
编程要求
在右侧代码窗口完成代码编写:

MapReduce类已经配置好,不需要再进行配置
在map方法中,获取输入表t_shared_bicycle的相关信息,设置1为线路次数,把开始经度、结束经度、开始维度、结束维度、出发地、目的地、线路次数传入到reduce
在reduce方法中通过线路次数计算共享单车每个路线的使用次数,存入到列族为info,字段为lineTotal的表里,ROWKEY 格式为:开始经度-结束经度_开始维度-结束维度_出发地—目的地。
平台会输出前五的单车线路流量进行评测
t_shared_bicycle表结构如下:

列族名称 字段 对应的文件的描述 ROWKEY (格式为:骑行id)
info beginTime 开始时间 trip_id
info endTime 结束时间 trip_id
info bicycleId 车辆id trip_id
info departure 出发地 trip_id
info destination 目的地 trip_id
info city 所在城市 trip_id
info start_longitude 开始经度 trip_id
info stop_longitude 结束经度 trip_id
info start_latitude 开始纬度 trip_id
info stop_latitude 结束纬度 trip_id
测试说明
平台会对你编写的代码进行测试,若是与预期输出相同,则算通关。

开始你的任务吧,祝你成功!
示例代码如下:

package com.educoder.bigData.sharedbicycle;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.util.Tool;
import com.educoder.bigData.util.HBaseUtil;
/*** 共享单车线路流量统计*/
public class LineTotalMapReduce extends Configured implements Tool {public static final byte[] family = "info".getBytes();public static class MyMapper extends TableMapper<Text, IntWritable> {protected void map(ImmutableBytesWritable rowKey, Result result, Context context)throws IOException, InterruptedException {/********** Begin *********/        // 开始经纬度String start_latitude = Bytes.toString(result.getValue(family, "start_latitude".getBytes()));String start_longitude = Bytes.toString(result.getValue(family, "start_longitude".getBytes()));// 结束经纬度String stop_latitude = Bytes.toString(result.getValue(family, "stop_latitude".getBytes()));String stop_longitude = Bytes.toString(result.getValue(family, "stop_longitude".getBytes()));// 出发地String departure = Bytes.toString(result.getValue(family, "departure".getBytes()));// 目的地String destination = Bytes.toString(result.getValue(family, "destination".getBytes()));// 拼装value IntWritable doubleWritable = new IntWritable(1);context.write(new Text(start_latitude + "-" + start_longitude + "_" + stop_latitude + "-" + stop_longitude+ "_" + departure + "-" + destination), doubleWritable);/********** End *********/        }}public static class MyTableReducer extends TableReducer<Text, IntWritable, ImmutableBytesWritable> {@Overridepublic void reduce(Text key, Iterable<IntWritable> values, Context context)throws IOException, InterruptedException {/********** Begin *********/        int totalNum = 0;for (IntWritable num : values) {int d = num.get();totalNum += d;}Put put = new Put(Bytes.toBytes(key.toString() + totalNum ));put.addColumn(family, "lineTotal".getBytes(), Bytes.toBytes(String.valueOf(totalNum)));context.write(null, put);// initTableReducerJob 设置了 表名所以在这里无需设置了/********** End *********/}}public int run(String[] args) throws Exception {// 配置JobConfiguration conf = HBaseUtil.conf;// Scanner sc = new Scanner(System.in);// String arg1 = sc.next();// String arg2 = sc.next();String arg1 = "t_shared_bicycle";String arg2 = "t_bicycle_linetotal";try {HBaseUtil.createTable(arg2, new String[] { "info" });} catch (Exception e) {// 创建表失败e.printStackTrace();}Job job = configureJob(conf, new String[] { arg1, arg2 });return job.waitForCompletion(true) ? 0 : 1;}private Job configureJob(Configuration conf, String[] args) throws IOException {String tablename = args[0];String targetTable = args[1];Job job = new Job(conf, tablename);Scan scan = new Scan();scan.setCaching(300);scan.setCacheBlocks(false);// 在mapreduce程序中千万不要设置允许缓存// 初始化Mapreduce程序TableMapReduceUtil.initTableMapperJob(tablename, scan, MyMapper.class, Text.class, IntWritable.class, job);// 初始化ReduceTableMapReduceUtil.initTableReducerJob(targetTable, // output tableMyTableReducer.class, // reducer classjob);job.setNumReduceTasks(1);return job;}
}

在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/308043.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GLTF编辑器-位移贴图实现破碎的路面

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 位移贴图是一种可以用于增加模型细节和形状的贴图。它能够在渲染时针…

【无标题】《巴黎图书馆》,又发现一本书

我喜愛看的书(https://img-blog.csdnimg.cn/8cd84d33e6724f09a46831f75abe6464.jpg)在这里插入图片描述

帆软FineBi V6版本经验总结

帆软FineBi V6版本经验总结 BI分析出现背景 ​ 现在是一个大数据的时代&#xff0c;每时每刻都有海量的明细数据出现。这时大数据时代用户思维是&#xff1a;1、数据的爆炸式增长&#xff0c;人们比起明细数据&#xff0c;更在意样本的整体特征、相互关系。2、基于明细的“小…

详解C语言单链表接口函数

准备工作 创建一个头文件&#xff08;SList.h&#xff09;,两个源文件&#xff08;SList.c和test.c&#xff09; SList.h:用于包含库函数的头文件&#xff0c;链表节点结构体声明&#xff0c;接口函数的声明等【另外两个源文件要包含SList.h这个头文件&#xff0c;才能使用其…

《HelloGitHub》第 93 期

兴趣是最好的老师&#xff0c;HelloGitHub 让你对编程感兴趣&#xff01; 简介 HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。 这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等&#xff0c;涵盖多种编程语言 Python、Java、Go、C/C、Swift...让你在短时间内…

Selenium在vue框架下求生存

vue框架下面&#xff0c;没有id、没有name&#xff0c;vue帮开发做了很多脏活累活&#xff0c;却委屈了写页面自动化测试的人&#xff08;当然&#xff0c;也给爬信息的也带来了一定的难处&#xff09;。这里只能靠总结&#xff0c;用一些歪门邪道&#xff1a; 一、跟开发商量…

字符串转换tuple对象

给定“前导空格分隔的元组字符串”&#xff0c;还原成合法的python元组tuple对象。 (笔记模板由python脚本于2023年12月29日 19:29:03创建&#xff0c;本篇笔记适合熟悉Python元组tuple的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org…

mfc100u.dll文件丢失了要怎么解决?修复mfc100u.dll详细指南

mfc100u.dll文件丢失了要怎么解决?首先让我们扒一扒什么是 mfc100u.dll。这玩意儿是 Microsoft Visual Studio 2010 的一部分&#xff0c;它就像一款程序生活中不可或缺的零件&#xff0c;没了它&#xff0c;程序肯定跑不起来。想想看&#xff0c;没有一个重要的零件&#xff…

【Azure 架构师学习笔记】- Azure Databricks (4) - 使用Azure Key Vault 管理ADB Secret

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Databricks】系列。 接上文 【Azure 架构师学习笔记】- Azure Databricks (3) - 再次认识DataBricks 前言 Azure Databricks有access token&#xff0c;是具有ADB内部最高权限的token。在云环境中这些高级别权限的sec…

【Python基础011】Python中字符串的具体用法

1、创建 字符串的创建共有3种方式 1&#xff09;单/双引号创建短字符串 s hello worlds “hello world” 2&#xff09;三个单/双引号创建短字符串 三个单/双引号一般用于多行文本的注释&#xff0c;如果将这样的字符串用print打印&#xff0c;或者赋给一个变量&#xff0c;…

leetcode贪心算法题总结(二)

本节目录 1.最长回文串2.增减字符串匹配3.分发饼干4.最优除法5.跳跃游戏II6.跳跃游戏7.加油站8.单调递增的数字9.坏了的计算器 1.最长回文串 最长回文串 class Solution { public:int longestPalindrome(string s) {//计数一&#xff1a;用数组模拟哈希表int hash[127] {0}…

自定义拖拽表单的优势表现在哪里?

利用自定义拖拽表单&#xff0c;可以让办公更轻松&#xff0c;效率更高&#xff0c;是理想的提质增效办公工具。哪里有自定义拖拽表单&#xff1f;它又有什么样的优势特点&#xff1f;流辰信息是专业研发低代码技术平台的服务商&#xff0c;经验足、研发实力强、服务好&#xf…