用jfreechart生成心电图

先上成功的图片

在这里插入图片描述

上代码

1.导入包

  implementation 'org.jfree:jfreechart:1.5.4'implementation 'org.jfree:jcommon:1.0.24'

2.实现代码

对数据进行滤波
转换单位
package com.shinrun.infrastructure.util;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;public class ECGDataConverter {/*** 将原始数据转换为mV单位的数据* @param rawData 原始数据数组* @param scaleFactor 比例因子(20000对应1mV)* @return 转换后的数据数组(单位mV)*/public static double[] convertToMillivolts(double[] rawData, double scaleFactor) {double[] convertedData = new double[rawData.length];for (int i = 0; i < rawData.length; i++) {convertedData[i] = rawData[i] / scaleFactor;}return convertedData;}/*** 将原始数据转换为微伏(uV)单位的数据* @param rawData 原始数据数组* @param scaleFactor 比例因子(20000对应1mV)* @return 转换后的数据数组(单位uV)*/public static double[] convertToMicrovolts(double[] rawData, double scaleFactor) {double[] convertedData = new double[rawData.length];for (int i = 0; i < rawData.length; i++) {convertedData[i] = (rawData[i] / scaleFactor) * 1000; // 转换为uV}return convertedData;}/*** 移动平均滤波器 - 平滑高频噪声* @param data 原始ECG数据(uV)* @param windowSize 窗口大小(建议3-7)* @return 滤波后的数据*/public static double[] movingAverageFilter(double[] data, int windowSize) {if (windowSize < 1 || windowSize > data.length) {throw new IllegalArgumentException("Invalid window size");}double[] filtered = new double[data.length];int halfWindow = windowSize / 2;for (int i = 0; i < data.length; i++) {int start = Math.max(0, i - halfWindow);int end = Math.min(data.length - 1, i + halfWindow);double sum = 0;int count = 0;for (int j = start; j <= end; j++) {sum += data[j];count++;}filtered[i] = sum / count;}return filtered;}/*** 中值滤波器 - 有效去除脉冲噪声* @param data 原始ECG数据(uV)* @param windowSize 窗口大小(建议3-5)* @return 滤波后的数据*/public static double[] medianFilter(double[] data, int windowSize) {if (windowSize < 1 || windowSize > data.length || windowSize % 2 == 0) {throw new IllegalArgumentException("Window size must be odd and positive");}double[] filtered = new double[data.length];int halfWindow = windowSize / 2;double[] window = new double[windowSize];for (int i = 0; i < data.length; i++) {int start = Math.max(0, i - halfWindow);int end = Math.min(data.length - 1, i + halfWindow);// 填充窗口int index = 0;for (int j = start; j <= end; j++) {window[index++] = data[j];}// 对窗口数据进行排序Arrays.sort(window, 0, index);// 取中值filtered[i] = window[index / 2];}return filtered;}/*** 基于阈值的异常值过滤* @param data 原始ECG数据(uV)* @param thresholdMultiplier 阈值乘数(建议2.5-3.5)* @return 过滤后的数据*/public static double[] thresholdFilter(double[] data, double thresholdMultiplier) {// 计算数据的均值和标准差double mean = 0;for (double v : data) {mean += v;}mean /= data.length;double stdDev = 0;for (double v : data) {stdDev += Math.pow(v - mean, 2);}stdDev = Math.sqrt(stdDev / data.length);double threshold = thresholdMultiplier * stdDev;double[] filtered = new double[data.length];for (int i = 0; i < data.length; i++) {if (Math.abs(data[i] - mean) > threshold) {// 如果是异常值,用前后值的平均值替代double prev = i > 0 ? filtered[i-1] : mean;double next = i < data.length-1 ? data[i+1] : mean;filtered[i] = (prev + next) / 2;} else {filtered[i] = data[i];}}return filtered;}/*** 组合滤波器 - 先中值滤波再移动平均* @param data 原始ECG数据(uV)* @param medianWindow 中值滤波窗口大小(建议3)* @param averageWindow 移动平均窗口大小(建议5)* @return 滤波后的数据*/public static double[] combinedFilter(double[] data, int medianWindow, int averageWindow) {double[] medianFiltered = medianFilter(data, medianWindow);return movingAverageFilter(medianFiltered, averageWindow);}/*** 原始数据强过滤(处理ADC值)* @param rawData 原始ADC数据* @param maxAllowed 允许的最大ADC绝对值* @return 过滤后的原始ADC数据*/public static double[] filterRawData(double[] rawData, double maxAllowed) {if (rawData == null) {return new double[0];}double[] filtered = Arrays.copyOf(rawData, rawData.length);int windowSize = 7; // 使用前后各3个点(共7点窗口)for (int i = 0; i < filtered.length; i++) {if (Math.abs(filtered[i]) > maxAllowed) {// 获取周围正常值的滑动窗口中值filtered[i] = getSlidingWindowReplacement(filtered, i, windowSize, maxAllowed);}}return filtered;}private static double getSlidingWindowReplacement(double[] data, int centerIdx,int windowSize, double maxAllowed) {int halfWindow = windowSize / 2;List<Double> validValues = new ArrayList<>();// 收集窗口内有效值for (int i = -halfWindow; i <= halfWindow; i++) {int actualIdx = centerIdx + i;if (actualIdx >= 0 && actualIdx < data.length &&Math.abs(data[actualIdx]) <= maxAllowed) {validValues.add(data[actualIdx]);}}// 处理策略(按优先级):if (!validValues.isEmpty()) {// 1. 有有效值则取中位数Collections.sort(validValues);return validValues.get(validValues.size() / 2);} else {// 2. 无有效值则取前一个正常值(向前搜索)for (int i = centerIdx - 1; i >= 0; i--) {if (Math.abs(data[i]) <= maxAllowed) {return data[i];}}// 3. 极端情况返回0return 0.0;}}}
图片生成 方法
package com.shinrun.infrastructure.util;import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.ui.RectangleInsets;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;import java.awt.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;/*** 医疗级心电图生成工具类(10段垂直排列)* 实现了标准心电图纸网格和比例尺*/
public class ECGChartGenerator {// ============= 默认参数配置 =============private static final int DEFAULT_SAMPLE_RATE = 500; // 默认采样率500Hzprivate static final int SEGMENT_COUNT = 10; // 固定10段心电图private static final int DEFAULT_WIDTH = 800; // 图片默认宽度private static final int SEGMENT_HEIGHT = 150; // 每段心电图高度private static final int TOTAL_HEIGHT = SEGMENT_COUNT * SEGMENT_HEIGHT; // 总高度// 心电图纸网格参数(医疗标准)private static final int SMALL_GRID_MM = 1; // 小方格1mmprivate static final int LARGE_GRID_MM = 5; // 大方格5mm(5个小方格)private static final int GRID_SIZE_PX = 12; // 1mm对应的像素数(方法二)// 心电图显示参数private static final double DEFAULT_MM_PER_MV = 10.0; // 标准灵敏度:10mm/mVprivate static final double DEFAULT_PAPER_SPEED_MM_PER_SEC = 25.0; // 标准走纸速度:25mm/sprivate static final int DEFAULT_HEIGHT = 600; // 图片默认高度/*** 生成无标签的心电图纸网格*/public static String generateECGGrid() throws IOException {// 创建空数据集(不需要实际数据)XYSeriesCollection dataset = new XYSeriesCollection();// 创建图表JFreeChart chart = ChartFactory.createXYLineChart(null, "", "", dataset);// 自定义网格样式customizeGridChart(chart);return convertChartToBase64(chart, DEFAULT_WIDTH, DEFAULT_HEIGHT);}/*** 自定义网格图表样式*/private static void customizeGridChart(JFreeChart chart) {XYPlot plot = chart.getXYPlot();// 1. 背景设置plot.setBackgroundPaint(Color.WHITE);chart.setBackgroundPaint(Color.WHITE);// 2. 网格线设置(医疗标准)plot.setDomainGridlinePaint(new Color(255, 200, 200)); // 小网格线颜色plot.setRangeGridlinePaint(new Color(255, 200, 200));plot.setDomainGridlineStroke(new BasicStroke(0.5f));   // 小网格线宽度plot.setRangeGridlineStroke(new BasicStroke(0.5f));// 计算网格大小(像素)double smallGridPx = SMALL_GRID_MM * GRID_SIZE_PX;double largeGridPx = LARGE_GRID_MM * GRID_SIZE_PX;// 3. X轴(时间轴)设置 - 隐藏所有标签和刻度NumberAxis xAxis = (NumberAxis) plot.getDomainAxis();xAxis.setAxisLineVisible(false);xAxis.setTickMarksVisible(false);xAxis.setTickLabelsVisible(false);xAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());xAxis.setTickUnit(new org.jfree.chart.axis.NumberTickUnit(largeGridPx));// 4. Y轴(幅度轴)设置 - 隐藏所有标签和刻度NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();yAxis.setAxisLineVisible(false);yAxis.setTickMarksVisible(false);yAxis.setTickLabelsVisible(false);yAxis.setTickUnit(new org.jfree.chart.axis.NumberTickUnit(largeGridPx));// 5. 移除所有渲染器(因为我们只需要网格)plot.setRenderer(null);// 6. 调整图表边距plot.setAxisOffset(new RectangleInsets(5, 5, 5, 5));// 7. 确保没有图例chart.removeLegend();// 8. 设置合适的范围以显示网格xAxis.setRange(0, DEFAULT_WIDTH / GRID_SIZE_PX);yAxis.setRange(0, DEFAULT_HEIGHT / GRID_SIZE_PX);}/*** 使用默认参数生成10段ECG Base64图片*/public static String generateSegmentedECGImage(double[] ecgData) throws IOException {return generateSegmentedECGImage(ecgData, DEFAULT_SAMPLE_RATE, DEFAULT_MM_PER_MV, DEFAULT_PAPER_SPEED_MM_PER_SEC);}/*** 自定义参数生成10段ECG Base64图片*/public static String generateSegmentedECGImage(double[] ecgData, int sampleRate, double mmPerMv, double paperSpeedMmPerSec) throws IOException {// 计算每段数据点数int pointsPerSegment = ecgData.length / SEGMENT_COUNT;XYSeriesCollection dataset = new XYSeriesCollection();// 创建10个数据段for (int seg = 0; seg < SEGMENT_COUNT; seg++) {int start = seg * pointsPerSegment;int end = (seg == SEGMENT_COUNT - 1) ? ecgData.length : start + pointsPerSegment;XYSeries series = new XYSeries("ECG Segment " + (seg + 1));// 垂直偏移量(使各段分开显示)double yOffset = -seg * 2.0; // 每段下移2mVfor (int i = start; i < end; i++) {// 计算时间并转换为毫米(基于走纸速度)double timeSec = (i - start) / (double) sampleRate;double xPosMm = timeSec * paperSpeedMmPerSec;// 转换为像素double xPixel = xPosMm * GRID_SIZE_PX;// 转换ECG值到mV(输入是uV)double valueMv = ecgData[i] / 1000.0;// 应用垂直比例尺(mm/mV)double yPosMm = valueMv * mmPerMv;// 转换为像素并加上偏移量double yPixel = yPosMm * GRID_SIZE_PX + (yOffset * mmPerMv * GRID_SIZE_PX);series.add(xPixel, yPixel);}dataset.addSeries(series);}// 创建图表JFreeChart chart = ChartFactory.createXYLineChart(null, "时间 (mm)", "幅度 (mm)", dataset);// 自定义心电图样式customizeMedicalECGChart(chart, mmPerMv, paperSpeedMmPerSec);return convertChartToBase64(chart, DEFAULT_WIDTH, TOTAL_HEIGHT);}/*** 自定义心电图图表样式(医疗标准)*/private static void customizeMedicalECGChart(JFreeChart chart, double mmPerMv, double paperSpeedMmPerSec) {XYPlot plot = chart.getXYPlot();// 1. 背景设置plot.setBackgroundPaint(Color.WHITE);chart.setBackgroundPaint(Color.WHITE);// 2. 网格线设置(医疗标准)plot.setDomainGridlinePaint(new Color(255, 200, 200)); // 小网格线颜色plot.setRangeGridlinePaint(new Color(255, 200, 200));plot.setDomainGridlineStroke(new BasicStroke(0.5f));   // 小网格线宽度plot.setRangeGridlineStroke(new BasicStroke(0.5f));// 计算网格大小(像素)double smallGridPx = SMALL_GRID_MM * GRID_SIZE_PX;double largeGridPx = LARGE_GRID_MM * GRID_SIZE_PX;// 3. X轴(时间轴)设置NumberAxis xAxis = (NumberAxis) plot.getDomainAxis();xAxis.setAxisLinePaint(Color.BLACK);xAxis.setTickMarkPaint(Color.BLACK);xAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());xAxis.setTickUnit(new org.jfree.chart.axis.NumberTickUnit(largeGridPx));// 新版JFreeChart不再支持setMinorTickMarkPaint,改用以下方式设置次刻度xAxis.setMinorTickCount(4);xAxis.setMinorTickMarksVisible(true);// 4. Y轴(幅度轴)设置NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();yAxis.setAxisLinePaint(Color.BLACK);yAxis.setTickMarkPaint(Color.BLACK);yAxis.setTickUnit(new org.jfree.chart.axis.NumberTickUnit(largeGridPx));yAxis.setMinorTickCount(4);yAxis.setMinorTickMarksVisible(true);// 5. 心电图曲线渲染设置XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();renderer.setSeriesPaint(0, Color.BLACK);renderer.setSeriesStroke(0, new BasicStroke(1.5f));// 为所有段应用相同的渲染器for (int i = 0; i < SEGMENT_COUNT; i++) {plot.setRenderer(i, renderer);renderer.setSeriesPaint(i, Color.BLACK);renderer.setSeriesStroke(i, new BasicStroke(1.5f));renderer.setSeriesShapesVisible(i, false);}// 6. 调整图表边距plot.setAxisOffset(new RectangleInsets(5, 5, 5, 5));// 7. 移除图例chart.removeLegend();// 8. 添加标题显示参数chart.setTitle("心电图 - " + mmPerMv + "mm/mV, 走纸速度 " + paperSpeedMmPerSec + "mm/s");}/*** 使用默认参数生成10段ECG Base64图片(无刻度值)*/public static String generateSegmentedECGImageNoLabels(double[] ecgData) throws IOException {return generateSegmentedECGImageNoLabels(ecgData, DEFAULT_SAMPLE_RATE,DEFAULT_MM_PER_MV, DEFAULT_PAPER_SPEED_MM_PER_SEC);}/*** 自定义参数生成10段ECG Base64图片(无刻度值)*/public static String generateSegmentedECGImageNoLabels(double[] ecgData, int sampleRate,double mmPerMv, double paperSpeedMmPerSec) throws IOException {// 计算每段数据点数int pointsPerSegment = ecgData.length / SEGMENT_COUNT;XYSeriesCollection dataset = new XYSeriesCollection();// 创建10个数据段for (int seg = 0; seg < SEGMENT_COUNT; seg++) {int start = seg * pointsPerSegment;int end = (seg == SEGMENT_COUNT - 1) ? ecgData.length : start + pointsPerSegment;XYSeries series = new XYSeries("ECG Segment " + (seg + 1));// 垂直偏移量(使各段分开显示)double yOffset = -seg * 2.0; // 每段下移2mVfor (int i = start; i < end; i++) {// 计算时间并转换为毫米(基于走纸速度)double timeSec = (i - start) / (double) sampleRate;double xPosMm = timeSec * paperSpeedMmPerSec;// 转换为像素double xPixel = xPosMm * GRID_SIZE_PX;// 转换ECG值到mV(输入是uV)double valueMv = ecgData[i] / 1000.0;// 应用垂直比例尺(mm/mV)double yPosMm = valueMv * mmPerMv;// 转换为像素并加上偏移量double yPixel = yPosMm * GRID_SIZE_PX + (yOffset * mmPerMv * GRID_SIZE_PX);series.add(xPixel, yPixel);}dataset.addSeries(series);}// 创建图表JFreeChart chart = ChartFactory.createXYLineChart(null, "", "", dataset); // 空标题和空轴标签// 自定义心电图样式(无刻度值)customizeMedicalECGChartNoLabels(chart, mmPerMv, paperSpeedMmPerSec);return convertChartToBase64(chart, DEFAULT_WIDTH, TOTAL_HEIGHT);}/*** 自定义心电图图表样式(医疗标准,无刻度值)*/private static void customizeMedicalECGChartNoLabels(JFreeChart chart, double mmPerMv, double paperSpeedMmPerSec) {XYPlot plot = chart.getXYPlot();// 1. 背景设置plot.setBackgroundPaint(Color.WHITE);chart.setBackgroundPaint(Color.WHITE);// 2. 网格线设置(医疗标准)plot.setDomainGridlinePaint(new Color(255, 200, 200)); // 小网格线颜色plot.setRangeGridlinePaint(new Color(255, 200, 200));plot.setDomainGridlineStroke(new BasicStroke(0.5f));   // 小网格线宽度plot.setRangeGridlineStroke(new BasicStroke(0.5f));// 计算网格大小(像素)double smallGridPx = SMALL_GRID_MM * GRID_SIZE_PX;double largeGridPx = LARGE_GRID_MM * GRID_SIZE_PX;// 3. X轴(时间轴)设置 - 隐藏刻度值NumberAxis xAxis = (NumberAxis) plot.getDomainAxis();xAxis.setAxisLineVisible(false);          // 隐藏轴线xAxis.setTickMarksVisible(false);         // 隐藏刻度线xAxis.setTickLabelsVisible(false);        // 隐藏刻度标签xAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());xAxis.setTickUnit(new org.jfree.chart.axis.NumberTickUnit(largeGridPx));// 4. Y轴(幅度轴)设置 - 隐藏刻度值NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();yAxis.setAxisLineVisible(false);          // 隐藏轴线yAxis.setTickMarksVisible(false);         // 隐藏刻度线yAxis.setTickLabelsVisible(false);        // 隐藏刻度标签yAxis.setTickUnit(new org.jfree.chart.axis.NumberTickUnit(largeGridPx));// 5. 心电图曲线渲染设置XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();renderer.setSeriesPaint(0, Color.BLACK);renderer.setSeriesStroke(0, new BasicStroke(0.8f));// 为所有段应用相同的渲染器for (int i = 0; i < SEGMENT_COUNT; i++) {plot.setRenderer(i, renderer);renderer.setSeriesPaint(i, Color.BLACK);renderer.setSeriesStroke(i, new BasicStroke(0.8f));renderer.setSeriesShapesVisible(i, false);}// 6. 调整图表边距plot.setAxisOffset(new RectangleInsets(5, 5, 5, 5));// 7. 移除图例chart.removeLegend();// 8. 移除标题chart.setTitle("心电图 - " + mmPerMv + "mm/mV, 走纸速度 " + paperSpeedMmPerSec + "mm/s");}/*** 将图表转换为Base64编码的PNG图片*/private static String convertChartToBase64(JFreeChart chart, int width, int height) throws IOException {try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {ChartUtils.writeChartAsPNG(outputStream, chart, width, height);return "data:image/png;base64," + Base64.getEncoder().encodeToString(outputStream.toByteArray());}}
}
讲解使用

我的心电图数据是来着于第三方平台的(是由机器将心电数据文件上传到第三方系统,第三方系统给我返回的double数组)

首先是进行 滤波,数据会有很大的外界因素影响,波动会很大,
再将数据转换成mv
再画图,进行调试

  public String generateEcg(double[] rawData ) {// 转换参数double scaleFactor = 20.0;String oss="";// 方法1: 使用默认参数处理double[] processedECG = ECGDataConverter.filterRawData(rawData,4000);//转换成mvdouble[] doubles = ECGDataConverter.convertToMillivolts(processedECG, scaleFactor);// 生成时间轴try {String string = ECGChartGenerator.generateSegmentedECGImageNoLabels(doubles);oss= uploadFile(string, "image/ecg");} catch (IOException e) {throw new RuntimeException(e);}return oss;}
关键点
double[] processedECG = ECGDataConverter.filterRawData(rawData,4000);

这里 滤波参数我给的4000 其实就是 将超过4000的值给过滤掉,第三方给的数据 人的平均心跳峰值不超过4000,超过4000的那就不对需要过滤掉,这个根据你实际的数据进行修改

    double scaleFactor = 20.0;//转换成mvdouble[] doubles = ECGDataConverter.convertToMillivolts(processedECG, scaleFactor);

这里传个参数scaleFactor ,其实就是对数据进行除一下,这里由于第三方给的数据不太规范,不除以一下的话画出来不太好看
一般数据除到是百位数就行了

如果上面两点没有做好的话效果可能如下
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

微软Build 2025:Copilot Studio升级,解锁多智能体协作未来

微软Build 2025大会圆满落幕&#xff0c;作为年度科技盛会&#xff0c;它一直是开发与AI技术突破性创新的重要展示平台。对于工程师、创作者和领域专家来说&#xff0c;这是了解微软生态未来动向的关键时刻。今年&#xff0c;Microsoft Copilot Studio推出了一系列新功能&#…

LabVIEW杂草识别与精准喷洒

基于LabVIEW构建了一套集成机器视觉、智能决策与精准控制的农业杂草识别系统。通过高分辨率视觉传感器采集作物图像&#xff0c;利用 LabVIEW 的 NI Vision 模块实现图像颜色匹配与特征分析&#xff0c;结合 Arduino 兼容的工业级控制硬件&#xff0c;实现杂草定位与除草剂精准…

使用 Akamai 分布式云与 CDN 保障视频供稿传输安全

作者简介&#xff1a;David Eisenbacher 是 EZDRM 公司的首席执行官兼联合创始人&#xff0c;该公司是首家提供 "DRM 即服务" 的企业。作为 CEO&#xff0c;David 始终秉持为企业确立的使命&#xff1a;为视频服务商提供简洁有效的数字版权管理方案&#xff0c;助力其…

javascript 实战案例 二级联动下拉选框

本案例完全使用原生javascript实现&#xff0c;使用时只需填充platform_list二维数组即可&#xff0c;platform_list填充规则如下&#xff1a; [‘一级选项1’,‘二级选项11’,‘二级选项12’,‘二级选项13’,‘二级选项14’,…], [‘一级选项2’,‘二级选项21’,‘二级选项22’…

Elasticsearch集群最大分片数设置详解:从问题到解决方案

目录 前言 1 问题背景&#xff1a;重启后设置失效 2 核心概念解析 2.1 什么是分片(Shard)&#xff1f; 2.2 cluster.max_shards_per_node的作用 2.3 默认值是多少&#xff1f; 3 参数设置的两种方式 3.2 持久性设置(persistent) 3.2 临时设置(transient) 4 问题解决方…

Redis Sorted Set 深度解析:从原理到实战应用

Redis Sorted Set 深度解析&#xff1a;从原理到实战应用 在 Redis 丰富的数据结构家族中&#xff0c;Sorted Set&#xff08;有序集合&#xff09;凭借独特的设计和强大的功能&#xff0c;成为处理有序数据场景的得力工具。无论是构建实时排行榜&#xff0c;还是实现基于时间的…

Java并发编程:读写锁与普通互斥锁的深度对比

在Java并发编程中&#xff0c;锁是实现线程安全的重要工具。其中&#xff0c;普通互斥锁&#xff08;如synchronized和ReentrantLock&#xff09;和读写锁&#xff08;ReentrantReadWriteLock&#xff09;是两种常用的同步机制。本文将从多个维度深入分析它们的区别、适用场景及…

《云原生安全攻防》-- K8s网络策略:通过NetworkPolicy实现微隔离

默认情况下&#xff0c;K8s集群的网络是没有任何限制的&#xff0c;所有的Pod之间都可以相互访问。这就意味着&#xff0c;一旦攻击者入侵了某个Pod&#xff0c;就能够访问到集群中任意Pod&#xff0c;存在比较大的安全风险。 在本节课程中&#xff0c;我们将详细介绍如何通过N…

Log4j2、Fastjson特征流量分析

文章目录 一、Log4j2流量特征分析1. 漏洞原理简述2. 核心流量特征&#xff08;1&#xff09;请求特征&#xff08;2&#xff09;响应特征&#xff08;3&#xff09;日志特征 3.检测与防御建议 二、fastjson流量特征分析1.漏洞原理简述2.核心流量特征&#xff08;1&#xff09;请…

Java编程之建造者模式

建造者模式&#xff08;Builder Pattern&#xff09;是一种创建型设计模式&#xff0c;它将一个复杂对象的构建与表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。这种模式允许你分步骤构建一个复杂对象&#xff0c;并且可以在构建过程中进行不同的配置。 模式的核…

Spring AI之RAG入门

目录 1. 什么是RAG 2. RAG典型应用场景 3. RAG核心流程 3.1. 检索阶段 3.2. 生成阶段 4. 使用Spring AI实现RAG 4.1. 创建项目 4.2. 配置application.yml 4.3. 安装ElasticSearch和Kibana 4.3.1. 安装并启动ElasticSearch 4.3.2. 验证ElasticSearch是否启动成功 …

mysql数据库实现分库分表,读写分离中间件sharding-sphere

一 概述 1.1 sharding-sphere 作用&#xff1a; 定位关系型数据库的中间件&#xff0c;合理在分布式环境下使用关系型数据库操作&#xff0c;目前有三个产品 1.sharding-jdbc&#xff0c;sharding-proxy 1.2 sharding-proxy实现读写分离的api版本 4.x版本 5.x版本 1.3 说明…

运维视角下的广告系统之理解广告索引级联

广告索引中为什么要级联 这里的“级联”一般指的是多层索引结构&#xff0c;也叫级联索引&#xff08;Cascade Index 或 Multi-level Index&#xff09;。 在广告系统的索引中&#xff0c;级联设计有重要作用&#xff0c;主要原因如下&#xff1a; 1. 多维特征筛选的需求 广…

2025年5月24日系统架构设计师考试题目回顾

当前仅仅是个人用于记录&#xff0c;还未做详细分析&#xff0c;待更新… 综合知识 设 x,y 满足约束条件&#xff1a;x-1>0, x-y<0, x-y-x<0, 则 y/x 的最大值是()。 A. 3 B. 2 C. 4 D. 1 申请软件著作权登记时应当向中国版本保护中心提交软件的鉴别材料&#xff…

3D-激光SLAM笔记

目录 定位方案 编译tbb ros2humble安装 命令 colcon commond not found 栅格地图生成&#xff1a; evo画轨迹曲线 安装gtsam4.0.2 安装ceres-solver1.14.0 定位方案 1 方案一&#xff1a;改动最多 fasterlio 建图&#xff0c;加闭环优化&#xff0c;参考fast-lio增加关…

贪心算法应用:分数背包问题详解

贪心算法与分数背包问题 贪心算法&#xff08;Greedy Algorithm&#xff09;是算法设计中一种重要的思想&#xff0c;它在许多经典问题中展现出独特的优势。本文将用2万字篇幅&#xff0c;深入剖析贪心算法在分数背包问题中的应用&#xff0c;从基础原理到Java实现细节&#x…

PyTorch——非线性激活(5)

非线性激活函数的作用是让神经网络能够理解更复杂的模式和规律。如果没有非线性激活函数&#xff0c;神经网络就只能进行简单的加法和乘法运算&#xff0c;没法处理复杂的问题。 非线性变化的目的就是给我们的网络当中引入一些非线性特征 Relu 激活函数 Relu处理图像 # 导入必…

iOS 电子书听书功能的实现

在 iOS 应用中实现电子书听书&#xff08;文本转语音&#xff09;功能&#xff0c;可以通过系统提供的 AVFoundation 框架实现。以下是详细实现步骤和代码示例&#xff1a; 核心步骤&#xff1a; 导入框架创建语音合成器配置语音参数实现播放控制处理后台播放添加进度跟踪 完整…

ES中must与filter的区别

在 Elasticsearch 的布尔查询&#xff08;bool query&#xff09;中&#xff0c;must 和 filter 是两个核心子句&#xff0c;它们的核心区别在于 是否影响相关性评分&#xff0c;这直接决定了它们在查询性能、使用场景和结果排序上的差异。以下是详细对比&#xff1a; 一、核心…

vscode实时预览编辑markdown

vscode实时预览编辑markdown 点击vsode界面&#xff0c;实现快捷键如下&#xff1a; 按下快捷键 CtrlShiftV&#xff08;Windows/Linux&#xff09;或 CommandShiftV&#xff08;Mac&#xff09;即可在侧边栏打开 Markdown 预览。 效果如下&#xff1a;