Java案例如何清理冗余数据?

wen java案例 66

本文目录导读:

Java案例如何清理冗余数据?

  1. 清理内存中的冗余数据(Java集合处理)
  2. 清理数据库中的冗余数据(JDBC / JPA 操作)
  3. 清理冗余文件 / 外部存储
  4. 综合最佳实践建议

在Java开发中,清理冗余数据(如重复记录、过期数据、无效数据)通常涉及内存中的数据清理数据库中的数据清理,下面我将从这两个方面,结合具体的代码案例和常用技巧进行说明。


清理内存中的冗余数据(Java集合处理)

使用 Set 去重

Set 数据结构天然不允许重复元素,是清理内存中重复对象的首选。

案例:清理 List 中的重复对象

import java.util.*;
public class DuplicateRemovalExample {
    public static void main(String[] args) {
        List<String> listWithDuplicates = Arrays.asList("A", "B", "A", "C", "B", "D");
        // 方法1:借助 HashSet 去重
        Set<String> set = new HashSet<>(listWithDuplicates);
        List<String> uniqueList = new ArrayList<>(set);
        System.out.println("去重后: " + uniqueList); // 输出: [A, B, C, D] (顺序不保证)
        // 方法2:使用 LinkedHashSet 保持插入顺序
        Set<String> linkedSet = new LinkedHashSet<>(listWithDuplicates);
        List<String> orderedUniqueList = new ArrayList<>(linkedSet);
        System.out.println("有序去重: " + orderedUniqueList); // 输出: [A, B, C, D]
        // 方法3:Java 8 Stream API
        List<String> streamUnique = listWithDuplicates.stream()
                .distinct()
                .collect(Collectors.toList());
        System.out.println("Stream去重: " + streamUnique);
    }
}

自定义对象去重(重写 equals 和 hashCode)

对于自定义对象,必须正确重写 equals()hashCode(),否则 Set 无法识别重复。

class User {
    private int id;
    private String name;
    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return id == user.id && Objects.equals(name, user.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
    // getters...
}
// 使用
List<User> users = new ArrayList<>();
users.add(new User(1, "Alice"));
users.add(new User(2, "Bob"));
users.add(new User(1, "Alice")); // 重复
users.add(new User(3, "Charlie"));
List<User> uniqueUsers = new ArrayList<>(new HashSet<>(users));
System.out.println("去重后人数: " + uniqueUsers.size()); // 3

根据特定字段去重

有时我们不希望整个对象相同才算重复,而是根据某个字段(如ID)去重。

List<User> users = ...;
Map<Integer, User> uniqueMap = new LinkedHashMap<>();
for (User u : users) {
    uniqueMap.putIfAbsent(u.getId(), u); // 按ID去重,保留首次出现
}
List<User> uniqueByField = new ArrayList<>(uniqueMap.values());
// 使用 Stream API
List<User> streamUnique = users.stream()
        .filter(distinctByKey(User::getId)) // 需要自定义 Predicate
        .collect(Collectors.toList());
// 辅助方法
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    Set<Object> seen = ConcurrentHashMap.newKeySet();
    return t -> seen.add(keyExtractor.apply(t));
}

清理数据库中的冗余数据(JDBC / JPA 操作)

使用 SQL 直接清理

最直接、高效的方式是执行删除 SQL。

案例:删除重复记录(保留ID最小的那一条)

-- 删除 email 重复的记录,只保留每组中 id 最小的那一行
DELETE FROM users 
WHERE id NOT IN (
    SELECT MIN(id) FROM users GROUP BY email
);

在 JDBC 中执行

String sql = "DELETE FROM users WHERE id NOT IN (SELECT MIN(id) FROM users GROUP BY email)";
try (Statement stmt = connection.createStatement()) {
    int deletedRows = stmt.executeUpdate(sql);
    System.out.println("删除了 " + deletedRows + " 条重复记录");
}

使用 JPA / Hibernate 清理

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    @Modifying
    @Transactional
    @Query(value = "DELETE FROM users WHERE id NOT IN (SELECT MIN(id) FROM users GROUP BY email)", nativeQuery = true)
    int deleteDuplicateByEmail();
}

清理过期数据(基于时间戳)

-- 删除7天前的日志
DELETE FROM logs WHERE created_at < NOW() - INTERVAL '7 days';
String deleteSql = "DELETE FROM logs WHERE created_at < :cutoff";
int deleted = jdbcTemplate.update(deleteSql, 
    Map.of("cutoff", LocalDateTime.now().minusDays(7)));

清理冗余文件 / 外部存储

如果冗余数据是文件系统中的临时文件、日志等:

import java.io.IOException;
import java.nio.file.*;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.stream.Stream;
public class FileCleaner {
    public static void cleanOldFiles(String dirPath, int daysToKeep) throws IOException {
        Path dir = Paths.get(dirPath);
        LocalDateTime cutoff = LocalDateTime.now().minusDays(daysToKeep);
        try (Stream<Path> files = Files.list(dir)) {
            files.filter(Files::isRegularFile)
                 .filter(path -> {
                     try {
                         LocalDateTime lastModified = Files.getLastModifiedTime(path).toInstant()
                                 .atZone(java.time.ZoneId.systemDefault()).toLocalDateTime();
                         return lastModified.isBefore(cutoff);
                     } catch (IOException e) {
                         return false;
                     }
                 })
                 .forEach(path -> {
                     try {
                         Files.delete(path);
                         System.out.println("已删除过期文件: " + path);
                     } catch (IOException e) {
                         System.err.println("删除失败: " + path);
                     }
                 });
        }
    }
    public static void main(String[] args) throws IOException {
        cleanOldFiles("/tmp/logs", 30); // 删除30天前的文件
    }
}

综合最佳实践建议

  1. 优先用 SQL 层面清理:数据库直接执行 DELETE 比将数据加载到 Java 中再处理快得多。

  2. 分页删除避免锁表:如果冗余数据量非常大,使用分页删除或限制每次删除的行数:

    DELETE FROM table WHERE condition LIMIT 1000; -- MySQL

    或在 Java 中循环执行小批量删除。

  3. 建立定时任务:使用 Spring @Scheduled 或 Quartz 定时清理:

    @Scheduled(cron = "0 0 3 * * ?") // 每天凌晨3点执行
    public void scheduleCleanup() {
        // 调用清理逻辑
    }
  4. 对清理操作记录日志:记录删除数量、时间、影响行数,方便回溯问题。

  5. 先备份再清理:在清理重要数据前,建议将数据备份或软删除(如增加 deleted_at 字段)。


场景 推荐方案
内存中 List 去重 Set / Stream.distinct() / Map.putIfAbsent()
集合根据字段去重 ConcurrentHashMap.newKeySet() + 自定义 Predicate
数据库重复记录 SQL DELETE ... NOT IN (SELECT MIN(...))
数据库过期数据 SQL WHERE created_at < 时间
文件系统清理 NIO Files.list() + Files.delete()
大规模数据清理 分页删除 + 定时任务 + 日志与告警

掌握这些案例,你就可以应对绝大多数 Java 项目中的冗余数据清理需求,如果有具体的业务场景(如清理缓存、清理 Kafka 队列),也可以进一步结合相应的 API 来处理。

抱歉,评论功能暂时关闭!