2023-9-8 阿里健康2024校招java-体检及泛医疗部门一面

1 自我介绍

2 java基础

2.1 java集合类体系的了解

推荐文章:Java 集合框架体系总览

2.2 java的并发包体系

2.3 java的list和map的数据结构主要有什么不同呢?

答:

2.4 只有8个元素的一个HashMap,它会用到红黑树嘛

答:不会,因为转换为红黑树要满足两个条件,一个hashMap的数组长度大于64,一个是某一个哈希槽的链表长度大于8

2.5.1 为什么要同时满足这两个条件

答:

为了提高HashMap的性能,同时平衡存储效率和查询效率,JDK的设计者引入了红黑树与链表的结合使用。

  1. 当哈希槽的链表长度大于8时:长链表对于查找效率是非常不利的,因为链表查找的时间复杂度是O(n)。当链表过长时,查找所需的时间会增加。为了提高查找效率,当链表长度超过一个阈值(在这里是8)时,链表就会转换为红黑树。红黑树作为一种自平衡的二叉查找树,其查找时间复杂度可以维持在O(log n),大大提高了查找效率。

  2. 当HashMap的数组长度大于64时数组长度太小的时候,增加红黑树结构可能并不会提高性能,甚至可能引入额外的开销(性能比扩容低,而且这里的扩容相当于预先扩容,下一次扩容的时间可能会被推迟,此外扩容后的元素被均摊到新的哈希槽中,查找和比较的次数可能跟红黑树差不多)。而且,在数组长度较小的情况下,扩容(双倍增长)可能是一个更好的选择,因为它可以更均匀地分配键值对,从而减少单个哈希槽的链表长度。只有在数组长度足够大的情况下(这里的阈值是64),转换为红黑树才真正有意义。

这两个条件确保了HashMap在不同大小和填充度的情况下都能提供良好的性能。红黑树和链表的这种混合使用是一个对存储和查询效率的折衷。

2.5.2 数组长度太小的时候,增加红黑树结构可能并不会提高性能,甚至可能引入额外的开销.为什么呢

数组长度太小的时候,增加红黑树结构可能并不会提高性能,甚至可能引入额外的开销。原因如下:

  1. 开销:红黑树的结构比链表复杂。每个节点不仅需要存储数据和指针,还需要存储颜色和额外的指针。这意味着更多的内存使用,以及在创建和维护红黑树时更多的计算开销。

  2. 插入和删除性能:在红黑树中,每次插入和删除都可能需要一系列的旋转和颜色更改以保持树的平衡。而对于链表,插入和删除操作相对较为简单和快速。

  3. 转换的开销:将链表转换为红黑树本身就有一定的开销。如果数组长度太小,很可能在不久的将来会进行扩容操作,这可能会导致重新哈希和数据的再次分配。在这种情况下,频繁地转换链表和红黑树可能是无效的。

  4. 查找性能:当链表长度较短时,尽管链表查找的时间复杂度是O(n),但由于常数因子较小和内存局部性的原因,其实际的查找性能可能仍然是可接受的。红黑树虽然提供了O(log n)的查找时间复杂度,但当n较小时,log n的增益可能被树的额外开销所抵消。

综上所述,当数组长度较小或链表长度较短时,使用链表可能更为高效。红黑树的引入是为了处理链表长度过长的情况,以提高查找性能,而不是为了小数组或短链表的情况。

2.6 链表的头插法和尾插法的区别

答:

1 头插的时间复杂度是O(1),尾插的时间复杂度是O(n)

2 头插法的插入位置和插入的顺序是相反的。

3 在进行扩容的时候,头插法会造成死循环的问题

2.6.1 讲讲造成死循环的原因

2.7 关于AQS,你能说一下对他的了解嘛?

答:

(1)实现类:CountDownLatch,cyclicBarrier,ReentrantLock

(2)AQS的组件:等待队列,条件队列,一组状态:signal,condition,cancelled,一组操作:acquire,TryAcquire,release和tryRelease方法

2.7.1 ReentrantLock的实现逻辑

3 分布式基础

3.1 CAP和BASE理论

3.1.1 CAP

CAP 理论是由 Eric Brewer 在 2000 年提出的,用来描述在分布式系统中不能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三个基本需求。

  • Consistency (一致性): 所有的节点在同一时间点应该看到相同的数据。

  • Availability (可用性): 确保每个请求都能够收到一个响应,无论响应是成功或失败。

  • Partition Tolerance (分区容错性): 系统应该即使在数据分区之间的网络出现问题时也能够正常运行。

在 CAP 理论中,这三个属性是不能同时达到的。在任何分布式系统中,你只能保证其中的两个。通常的选择是:

  • CA: 在没有网络分区的情况下,选择一致性和可用性。

  • CP: 在网络分区出现时,选择一致性和分区容错性。这意味着某些节点可能会不可用,但仍然保持数据一致性。

  • AP: 在网络分区出现时,选择可用性和分区容错性。这可能导致数据不一致。

3.1.2 BASE

由于 CAP 理论的局限性,BASE 理论作为其补充被提出。BASE 是基本可用(Basically Available)、软状态(Soft state)和最终一致性(Eventually consistent)的缩写。

  • Basically Available (基本可用): 系统始终提供可用性,但可能不是最新的数据。

  • Soft State (软状态): 系统的状态可能会因为没有输入改变。

  • Eventually Consistent (最终一致性): 系统可能会在没有输入的情况下变得一致,即经过一个未定义的时间后,数据将达到一致性。

BASE 和 CAP 是分布式系统设计中的两种不同方法。CAP 注重强一致性,而 BASE 更加灵活,注重可用性和一致性的权衡。BASE 适用于高可用性的场景,如互联网应用,其中短时间的数据不一致是可以接受的。

4 rpc框架

4.1 rpc框架的组件

4.2 你实现了什么特别的功能嘛

答:泛化机制

4.3 既然泛化机制这么好用,为什么大家不都采用它呢?这样不是可以节省大量的本地sdk存根更新操作嘛

泛化调用或泛化机制是一种允许消费者调用远程服务,而不需要知道服务的具体实现或具体的方法签名的方式。在某些上下文中,例如RPC框架,它可以非常有用。但这种方法也有其局限性和缺点。下面是泛化机制的一些优缺点:

4.3.1 优点:

  1. 灵活性:由于不需要事先知道服务的实现细节,泛化调用可以在不更改客户端代码的情况下调用各种服务。
  2. 减少本地依赖:不需要针对每个服务都有本地SDK或存根,这可以降低客户端的复杂性和维护成本。

4.3.2 缺点:

  1. 类型安全性问题:使用泛化调用,您失去了编译时的类型检查,这可能导致运行时错误,而这些错误在使用强类型的本地SDK或存根时是可以避免的。
  2. 可读性和可维护性:对于那些不熟悉泛化机制的开发者来说,使用泛化调用的代码可能不如使用明确的API调用直观和容易理解。
  3. 性能:泛化调用可能需要额外的序列化/反序列化步骤或其他处理步骤,这可能会对性能产生一些影响。
  4. 文档和工具支持:泛化调用可能不被所有的工具和库完全支持,这可能导致一些与生成文档、测试、或其他开发工作流相关的挑战。
  5. 紧密耦合的问题:即使您使用泛化调用,您仍然需要知道远程服务的某些细节(例如期望的参数结构),这可能导致耦合问题。

总之,泛化机制确实提供了某些优势,但也带来了一些挑战。是否选择使用它取决于具体的应用场景和需求。对于需要频繁更新SDK或接口的系统,或者对于需要大量与不同服务进行交互的系统,泛化调用可能是一个有吸引力的选项。但对于那些强调类型安全性、性能和可维护性的系统,使用具体的本地SDK或存根可能是更好的选择。

4.4 你说的问题是指程序有可能报错呢,还是说被入侵

答:报错

4.5 你还实现了哪些其他的模块?

答:我实现了一个自定义协议,解决了粘包和拆包的问题,介绍了一下有哪些头部字段,为什么要这么设置

自定义协议

4.6 你的diy协议和netty本身提供的协议解决的http,有什么区别?

答:一般粘包和拆包的问题是由应用层协议解决的,netty本身是没有自定义协议,但是他是支持HTTP等多种应用层协议的,以http为例,http解决拆包和粘包问题的方案是使用分隔符加上content-length字段解决拆包和粘包问题的,http多出来的分隔符是为了区分http请求头和请求体,因为http的请求头不是固定长度的,但是我的diy协议固定了头部有四个字段,每个字段占4B,头部总长度为16个字节,这样的话,每次只需要读取固定的16字节就行,读取速度会比http快,但是因为没有做扩展,所以不如http灵活,因为http的头部字段可选,还可以增加自定义头部。我觉得如果我的协议是应用于一个特定的场景和程序,而且不会发生什么改变,我这种协议可以用上去,解析速度会比较快

4.6.1 netty协议本身提供的解决粘包和拆包的实现类源码,给我展示一下

Netty提供了多种解决粘包和拆包问题的编解码器。以下是其中一些常用的解决方案:

  1. LineBasedFrameDecoder:这是一个解决基于行的粘包/拆包的解码器,它会按行(换行符为边界)来切分接收到的ByteBuf

  2. FixedLengthFrameDecoder:固定长度的解码器,按照指定的长度来切分接收到的ByteBuf

  3. DelimiterBasedFrameDecoder:基于分隔符的解码器,可以自定义分隔符来切分接收到的ByteBuf

  4. LengthFieldBasedFrameDecoder:这可能是最常用的一个,尤其是处理自定义协议时。该解码器基于消息的长度来切分ByteBuf

为了给你一个直观的了解,我可以为你展示DelimiterBasedFrameDecoder的一部分核心代码。但请注意,由于版权问题,我不能直接复制全部的源代码。以下是一个简化版的核心逻辑:

public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {

    private final ByteBuf delimiter;
    private final int maxFrameLength;

    // ... 其他字段和构造方法

    @Override
    protected final void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {
        Object decoded = decode(ctx, buffer);
        if (decoded != null) {
            out.add(decoded);
        }
    }

    protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) {
        // 找到第一个匹配的分隔符的索引
        int minIndex = Integer.MAX_VALUE;
        for (ByteBuf delim : delimiters) {
            int frameLength = indexOf(buffer, delim);
            if (frameLength >= 0 && frameLength < minIndex) {
                minIndex = frameLength;
            }
        }

        if (minIndex == Integer.MAX_VALUE) {
            // 未找到分隔符,返回null
            return null;
        }

        // 找到了分隔符,根据分隔符切分消息
        ByteBuf frame = buffer.readRetainedSlice(minIndex);
        buffer.skipBytes(delimiter.capacity());  // 跳过分隔符
        return frame;
    }

    // ... 其他相关的方法和帮助函数
}

这是一个简化的示例,主要用于说明DelimiterBasedFrameDecoder的基本逻辑。在实际的Netty代码中,还有很多其他的细节和优化。如果你想深入了解,建议直接查看Netty的源代码。

4.6.2 FixedLengthFrameDecoder和LengthFieldBasedFrameDecoder的区别,FixedLengthFrameDecoder是指首部数据长度固定吗

让我们来详细讨论一下 FixedLengthFrameDecoderLengthFieldBasedFrameDecoder 之间的差异。

  1. FixedLengthFrameDecoder:

    FixedLengthFrameDecoder 是一个简单的解码器,用于处理固定长度的消息。它不关心消息的内容,仅按预定的固定长度来分割消息。比如,如果你知道每个消息都是10个字节长,无论内容是什么,你可以使用这个解码器。

    例如,如果你设置长度为5,那么以下输入数据:

    ABCDEFGHIJ
    

    会被解码成两条消息:

    ABCDE
    FGHIJ
    
  2. LengthFieldBasedFrameDecoder:

    LengthFieldBasedFrameDecoder 是一个更复杂、更灵活的解码器,用于处理那些前面带有表示内容长度的字段的消息。这种解码器主要用于自定义协议,其中消息的某一部分(通常是消息的开头)包含了接下来的消息主体的长度。

    举个例子,如果你的协议规定每个消息的前2个字节都是无符号的short,表示接下来消息主体的长度,那么以下输入数据:

    00 05 ABCDEFGHIJ
    

    会被解码成一条消息:

    ABCDE
    

    由于长度字段告诉我们消息是5个字节长,所以 FGHIJ 将被视为下一个消息的开始部分。

总结:

  • FixedLengthFrameDecoder 是为固定长度的消息设计的,而不关心消息的内容。

  • LengthFieldBasedFrameDecoder 是为带有表示长度的字段的消息设计的,通常用于自定义协议。

你问到的“FixedLengthFrameDecoder是指首部数据长度固定吗?”:其实是反过来的。FixedLengthFrameDecoder 指的是整个消息的长度是固定的。而首部数据长度固定的情况更适合 LengthFieldBasedFrameDecoder

5 你对ES有了解吗?

5.1 什么是倒排索引,为什么这么叫?

倒排索引(Inverted Index)是一种为快速全文搜索而设计的数据结构。它被广泛应用于搜索引擎,其中 Elasticsearch(简称 ES)是一个主要的使用者。

什么是倒排索引?

传统的索引(例如书的索引或数据库的B-Tree索引)是“正向”的:它们从“文档到词汇项”的映射。换句话说,你查找文档,然后列出文档中的词汇项。

相反,倒排索引从“词汇项到文档”的映射。这意味着,对于索引中的每一个唯一的词汇项,都有一个相关的文档列表,这些文档包含该词汇项。

例如,考虑以下简单文档集:

1. Apple is tasty.
2. Banana is tasty.
3. Apple and banana are fruits.

一个简单的倒排索引可能是:

Apple -> 1, 3
Banana -> 2, 3
tasty -> 1, 2
...

为什么叫“倒排索引”?

这个名称的由来是因为它与“正向索引”相对立。正向索引直接映射文档到其中的词汇项,而倒排索引则相反,映射词汇项到包含它们的文档。

为什么使用倒排索引?

倒排索引尤其对于全文搜索很有用,因为:

  1. 速度:它允许快速查找包含特定词汇项的文档。
  2. 空间效率:只为唯一的词汇项保存索引,并与相关的文档关联,这避免了重复。
  3. 排名和相关性:使用倒排索引,可以很容易地为搜索结果进行排名,基于词频、文档频率等。

Elasticsearch 作为一个分布式搜索和分析引擎,大量使用倒排索引来实现其高速和高效的文本搜索功能。

6 redis

6.1 你讲一下redis的跳表的实现原理吧

redis的基本数据结构有哪些,都有什么应用?

6.2 redis的listpack和quickList有了解嘛

7 数据结构-快排

8 你能来实习嘛?实习多久

答:

9 反问

9.1 你们有哪些业务构成呢?

答:处理现有的体检以及泛医疗,我们团队还有一些大模型相关的任务,可能你们来的话,这个会分配给你们做

9.2 如果实习来不了的话,是不是offer可能没了

答:如果表现特别好的话,可以保留offer

9.3 实习多久转正呢?

答:根据以往经验,他们暑假会来两个月吧

9.4 你们和阿里的自营大药房有什么区别呢

答:是我们阿里健康这个大部门的另外一个组在做,那个是电商业务

9.5 那你们的体检医疗业务体现在哪个应用上呢?

答:支付宝上的搜索栏,搜索体检,有一个小程序,这个就是我们负责的

你可能感兴趣的:(面经,java,阿里健康后端开发,2024秋招)