java面试-场景题

一、集合

1. java中如何给一个超大的一个亿左右的list数据去重?

我当时的回答是使用HashSet或Stream流的distinct语法。但是面试官好像更注重内存的消耗问题。

  • 使用HashSet:
    HashSet 是一个不允许有重复元素的集合。你可以将List中的元素添加到HashSet中,然后再将HashSet转换回List(如果你需要的话)。但请注意,这种方法只适用于可以安全地在HashSet中存储的元素(即实现了正确的hashCode()和equals()方法的对象)。
java 复制代码
List<YourType> originalList = ... // 你的原始列表
Set<YourType> set = new HashSet<>(originalList);
List<YourType> uniqueList = new ArrayList<>(set);

但是,如果List中的元素非常大(例如,每个元素都是一个复杂的对象),那么将整个List添加到HashSet中可能会消耗大量内存。

  • 流式处理(Stream API):
    如果你使用的是Java 8或更高版本,你可以使用Stream API进行去重。但是,流式处理可能不适用于非常大的数据集,因为它需要在内存中构建中间结果。
java 复制代码
List<YourType> uniqueList = originalList.stream().distinct().collect(Collectors.toList());

与HashSet方法类似,这种方法也依赖于元素的hashCode()和equals()方法的正确实现。

java 复制代码
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class MyObject {
    private int id; // 假设对象有一个唯一的ID属性

    // 构造函数、getter、setter等...
}

public class ListDeduplicationWithBitmapForObjects {

    // 假设我们有一个函数可以将对象映射到唯一的整数ID
    public static int getIdFromObject(MyObject obj) {
        return obj.getId(); // 示例:直接返回对象的ID属性
    }

    public static List<MyObject> deduplicateListWithBitmap(List<MyObject> list) {
        // 假设我们知道可能的最大ID是maxValue(实际情况中需要根据数据确定)
        int maxValue = ...; // 例如,如果你的ID范围是0到100000000,则maxValue = 100000000

        // 创建一个BitSet,大小为maxValue+1
        BitSet bitSet = new BitSet(maxValue + 1);

        // 遍历原始List,并将对象的ID设置为true在BitSet中
        for (MyObject obj : list) {
            int id = getIdFromObject(obj);
            if (id >= 0 && id <= maxValue) { // 确保ID在有效范围内
                bitSet.set(id);
            }
        }

        // 创建一个新的List来存储去重后的对象(如果需要的话)
        List<MyObject> deduplicatedList = new ArrayList<>();

        // 如果你需要保留去重后的对象列表,你可能需要额外的数据结构来存储对象与ID的映射
        // 这里只是一个简单的示例,我们假设你可以从ID直接获取到对象(这通常不现实)

        // 遍历BitSet(如果需要的话,可以将ID转换回对象并添加到deduplicatedList中)
        // 但在这个简单的示例中,我们仅打印去重后的ID
        for (int i = bitSet.nextSetBit(0); i >= 0; i = bitSet.nextSetBit(i + 1)) {
            // MyObject obj = getObjectFromId(i); // 假设你有这样的函数可以从ID获取对象
            System.out.println(i); // 或者打印出对象的ID
            // deduplicatedList.add(obj); // 如果需要的话,将对象添加到列表中
        }

        // 注意:这个示例没有返回去重后的对象列表,因为它取决于你如何存储和检索对象
        // 根据你的具体需求,你可能需要实现getObjectFromId函数或其他逻辑来恢复对象

        return null; // 或者返回一个空的deduplicatedList,取决于你的需求
    }

    public static void main(String[] args) {
        // 示例:创建一个包含重复对象的List
        List<MyObject> list = new ArrayList<>();
        // ... 添加对象到list中 ...

        // 去重(并可能打印结果,取决于你的实现)
        deduplicateListWithBitmap(list);
    }
}

内存占用比较:

在内存消耗有限制的场景中,使用BitSet来去重确实可以比使用HashSet更加节省内存,特别是在处理大量整数或可以映射到整数的对象时。但是,节省的内存量取决于具体的数据集和整数ID的分布情况。

以下是使用BitSet与HashSet去重时内存消耗的对比:

BitSet:

BitSet使用位(bit)来表示每个可能的元素是否出现过。因此,如果你知道可能的ID范围是0到maxValue,那么BitSet将使用(maxValue + 1) / 8个字节(因为一个字节有8位)。

BitSet不会为那些未使用的ID分配内存,因此它的内存使用是固定的,并且基于ID范围的上限。

HashSet:

HashSet使用哈希表来存储对象,每个对象都映射到一个哈希桶(bucket)。哈希表的大小通常是基于负载因子(load factor)和预期的元素数量来动态调整的。

对于整数,如果直接使用HashSet,那么每个Integer对象本身就需要一定的内存(大约16到24字节,取决于JVM和JVM设置)。此外,哈希表本身也需要额外的内存来存储桶数组和链表或红黑树(用于解决哈希冲突)。

对于对象,HashSet会存储对象的引用,所以实际的内存消耗还取决于对象的大小。

内存占用的减少量:

如果你的ID范围是连续的,并且你知道这个范围,那么BitSet的内存消耗将是固定的,并且通常远低于HashSet。

假设maxValue是100,000,000,BitSet将需要大约12.5MB((100,000,000 + 1) / 8 / 1024 / 1024)。而使用HashSet存储这么多Integer对象将需要显著更多的内存。

如果对象本身很大,那么HashSet的内存消耗将更高。

注意事项:

BitSet只适用于可以映射到整数ID的对象,并且这些ID的范围是已知的且相对较小的。

BitSet不支持直接存储对象,因此如果你需要保留去重后的对象列表,你需要额外的数据结构(如HashMap)来存储对象与ID之间的映射。

HashSet提供了更通用的去重功能,可以处理任何类型的对象,而不仅仅是整数或可以映射到整数的对象。

在选择使用哪种方法时,请考虑你的具体需求,包括内存限制、数据类型、对象大小以及是否需要保留去重后的对象列表等因素。

相关推荐
a5876913 分钟前
消息队列(MQ)初级入门:详解RabbitMQ与Kafka
java·分布式·microsoft·面试·kafka·rabbitmq
千里码aicood25 分钟前
【springboot+vue】党员党建活动管理平台(源码+文档+调试+基础修改+答疑)
java·数据库·spring boot
Chan1629 分钟前
【智能协同云图库】基于统一接口架构构建多维度分析功能、结合 ECharts 可视化与权限校验实现用户 / 管理员图库统计、通过 SQL 优化与流式处理提升数据
java·spring boot·后端·sql·spring·intellij-idea·echarts
先做个垃圾出来………34 分钟前
差分数组(Difference Array)
java·数据结构·算法
BillKu1 小时前
Java核心概念详解:JVM、JRE、JDK、Java SE、Java EE (Jakarta EE)
java·jvm·jdk·java ee·jre·java se·jakarta ee
小林coding1 小时前
再也不怕面试了!程序员 AI 面试练习神器终于上线了
前端·后端·面试
wjm0410062 小时前
ios面试八股文
ios·面试
刘婉晴2 小时前
【Java】NIO 简单介绍
java·nio
渣哥2 小时前
聊聊我和 ArrayList、LinkedList、Vector 的“一地鸡毛”
java
知其然亦知其所以然2 小时前
面试官微笑发问:第100万页怎么查?我差点当场沉默…
后端·mysql·面试