图书借阅记录统计是图书馆管理系统的核心功能,需要基于借阅记录数据计算各类统计指标,比如单本图书的借阅次数、用户的平均借阅时长、月度借阅总量等。Java可以借助集合操作、文件读写、数据库查询等能力完成不同复杂度的统计需求。

基础数据模型定义
首先需要定义借阅记录的基础实体类,所有统计数据都基于该实体类的实例展开,实体类包含借阅记录的核心字段:
public class BorrowRecord {
// 记录ID
private String recordId;
// 图书ID
private String bookId;
// 图书名称
private String bookName;
// 借阅用户ID
private String userId;
// 借阅时间
private long borrowTime;
// 归还时间,未归还时为-1
private long returnTime;
public BorrowRecord(String recordId, String bookId, String bookName, String userId, long borrowTime, long returnTime) {
this.recordId = recordId;
this.bookId = bookId;
this.bookName = bookName;
this.userId = userId;
this.borrowTime = borrowTime;
this.returnTime = returnTime;
}
// getter和setter方法
public String getRecordId() {
return recordId;
}
public void setRecordId(String recordId) {
this.recordId = recordId;
}
public String getBookId() {
return bookId;
}
public void setBookId(String bookId) {
this.bookId = bookId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public long getBorrowTime() {
return borrowTime;
}
public void setBorrowTime(long borrowTime) {
this.borrowTime = borrowTime;
}
public long getReturnTime() {
return returnTime;
}
public void setReturnTime(long returnTime) {
this.returnTime = returnTime;
}
// 计算借阅时长,单位:天
public long getBorrowDays() {
if (returnTime == -1) {
return -1;
}
return (returnTime - borrowTime) / (1000 * 60 * 60 * 24);
}
}
基于内存集合的统计实现
当借阅记录数据量较小,且不需要持久化存储时,可以使用Java集合类在内存中完成统计,适合小型本地工具或者测试场景。
统计单本图书的借阅次数
通过遍历借阅记录列表,使用Map统计每本图书的借阅次数:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BorrowStatByCollection {
public static void main(String[] args) {
// 模拟借阅记录列表
List<BorrowRecord> recordList = new ArrayList<>();
recordList.add(new BorrowRecord("1", "b001", "Java编程思想", "u001", 1620000000000L, 1620500000000L));
recordList.add(new BorrowRecord("2", "b001", "Java编程思想", "u002", 1621000000000L, 1621500000000L));
recordList.add(new BorrowRecord("3", "b002", "算法导论", "u001", 1622000000000L, -1L));
recordList.add(new BorrowRecord("4", "b003", "深入理解计算机系统", "u003", 1623000000000L, 1623500000000L));
// 统计每本图书的借阅次数
Map<String, Integer> bookBorrowCount = new HashMap<>();
for (BorrowRecord record : recordList) {
String bookId = record.getBookId();
bookBorrowCount.put(bookId, bookBorrowCount.getOrDefault(bookId, 0) + 1);
}
// 输出统计结果
System.out.println("图书借阅次数统计:");
for (Map.Entry<String, Integer> entry : bookBorrowCount.entrySet()) {
System.out.println("图书ID:" + entry.getKey() + ",借阅次数:" + entry.getValue());
}
}
}
统计用户平均借阅时长
先筛选出已归还的记录,再计算所有用户的平均借阅天数:
import java.util.ArrayList;
import java.util.List;
public class AvgBorrowDaysStat {
public static void main(String[] args) {
// 模拟借阅记录列表
List<BorrowRecord> recordList = new ArrayList<>();
recordList.add(new BorrowRecord("1", "b001", "Java编程思想", "u001", 1620000000000L, 1620500000000L));
recordList.add(new BorrowRecord("2", "b001", "Java编程思想", "u002", 1621000000000L, 1621500000000L));
recordList.add(new BorrowRecord("3", "b002", "算法导论", "u001", 1622000000000L, 1622500000000L));
recordList.add(new BorrowRecord("4", "b003", "深入理解计算机系统", "u003", 1623000000000L, 1623500000000L));
int totalDays = 0;
int returnedCount = 0;
for (BorrowRecord record : recordList) {
long days = record.getBorrowDays();
if (days != -1) {
totalDays += days;
returnedCount++;
}
}
double avgDays = returnedCount == 0 ? 0 : (double) totalDays / returnedCount;
System.out.println("已归还记录的平均借阅时长:" + avgDays + "天");
}
}
基于文件读写的统计实现
当需要将借阅记录持久化到本地文件,且数据量中等时,可以通过读写文本文件完成统计,适合单机版管理系统。
借阅记录文件存储格式
约定借阅记录每行存储一条,字段用逗号分隔,格式为:recordId,bookId,bookName,userId,borrowTime,returnTime,示例如下:
1,b001,Java编程思想,u001,1620000000000,1620500000000 2,b001,Java编程思想,u002,1621000000000,1621500000000 3,b002,算法导论,u001,1622000000000,-1 4,b003,深入理解计算机系统,u003,1623000000000,1623500000000
文件读取与统计实现
先读取文件内容解析为借阅记录列表,再执行统计逻辑:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BorrowStatByFile {
// 从文件读取借阅记录
private static List<BorrowRecord> readRecordsFromFile(String filePath) throws IOException {
List<BorrowRecord> recordList = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = br.readLine()) != null) {
String[] fields = line.split(",");
if (fields.length == 6) {
String recordId = fields[0];
String bookId = fields[1];
String bookName = fields[2];
String userId = fields[3];
long borrowTime = Long.parseLong(fields[4]);
long returnTime = Long.parseLong(fields[5]);
recordList.add(new BorrowRecord(recordId, bookId, bookName, userId, borrowTime, returnTime));
}
}
}
return recordList;
}
public static void main(String[] args) {
String filePath = "borrow_records.txt";
try {
List<BorrowRecord> recordList = readRecordsFromFile(filePath);
// 统计热门图书(借阅次数前3)
Map<String, Integer> bookCount = new HashMap<>();
for (BorrowRecord record : recordList) {
String bookName = record.getBookName();
bookCount.put(bookName, bookCount.getOrDefault(bookName, 0) + 1);
}
// 简单排序输出前3
System.out.println("热门图书借阅次数TOP3:");
bookCount.entrySet().stream()
.sorted((e1, e2) -> e2.getValue() - e1.getValue())
.limit(3)
.forEach(entry -> System.out.println("图书名称:" + entry.getKey() + ",借阅次数:" + entry.getValue()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
基于数据库的统计实现
当系统数据量较大,需要多端访问时,通常使用数据库存储借阅记录,Java通过JDBC或者持久层框架执行SQL完成统计,效率更高。
数据库表结构设计
借阅记录表borrow_record的结构如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
| record_id | varchar(32) | 记录ID,主键 |
| book_id | varchar(32) | 图书ID |
| book_name | varchar(100) | 图书名称 |
| user_id | varchar(32) | 用户ID |
| borrow_time | bigint | 借阅时间(时间戳) |
| return_time | bigint | 归还时间,-1表示未归还 |
JDBC统计实现示例
通过执行SQL语句直接完成统计,减少内存计算的压力:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class BorrowStatByDatabase {
private static final String URL = "jdbc:mysql://127.0.0.1:3306/library_db?useSSL=false&serverTimezone=UTC";
private static final String USER = "root";
private static final String PASSWORD = "123456";
public static void main(String[] args) {
try {
// 加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 建立连接
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
Statement stmt = conn.createStatement();
// 统计每本图书的借阅次数
String countSql = "SELECT book_name, COUNT(*) AS borrow_count FROM borrow_record GROUP BY book_id, book_name ORDER BY borrow_count DESC";
ResultSet countRs = stmt.executeQuery(countSql);
System.out.println("图书借阅次数统计:");
while (countRs.next()) {
String bookName = countRs.getString("book_name");
int count = countRs.getInt("borrow_count");
System.out.println("图书名称:" + bookName + ",借阅次数:" + count);
}
// 统计平均借阅时长
String avgSql = "SELECT AVG((return_time - borrow_time) / (1000 * 60 * 60 * 24)) AS avg_days FROM borrow_record WHERE return_time != -1";
ResultSet avgRs = stmt.executeQuery(avgSql);
if (avgRs.next()) {
double avgDays = avgRs.getDouble("avg_days");
System.out.println("已归还记录的平均借阅时长:" + String.format("%.2f", avgDays) + "天");
}
// 关闭资源
avgRs.close();
countRs.close();
stmt.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
不同实现方案的适用场景
- 内存集合方案:适合数据量小、无需持久化的本地工具或测试场景,实现简单,响应速度快。
- 文件读写方案:适合单机版系统,数据量中等,需要本地持久化存储的场景,实现成本较低。
- 数据库方案:适合多用户、大数据量的正式线上系统,统计效率高,支持复杂查询和并发访问。
开发者可以根据实际项目的规模和需求,选择合适的实现方案,也可以将多种方案结合使用,比如本地缓存热门统计数据,减少数据库查询压力。