面试中必会的Java基础(一)

Java是面向对象编程

所以第一就是面向对象编程的特点是什么?

面向对象编程

  • 类与对象:掌握类的定义、成员变量和成员方法的声明与使用,以及如何通过类创建对象。理解对象的生命周期,包括创建、使用和销毁。
  • 封装:明白封装的概念,即把数据和操作数据的方法封装在一个类中,通过访问修饰符(public、private、protected 等)来控制对类成员的访问。
  • 继承:理解继承的概念和作用,掌握通过 extends 关键字实现类的继承,了解继承中的构造函数调用顺序、方法重写的规则以及 super 关键字的使用。
  • 多态:多态是 Java 面向对象编程的重要特性,包括方法重载和方法重写。能够理解多态的实现原理,以及在实际编程中的应用场景,如通过接口实现不同类的多态行为。

第二Android面试最高频的问Java的HashMap的原理以及扩展

 首先HashMap的原理:

  • 数据结构
    • 数组 + 链表 / 红黑树:HashMap 底层是由数组和链表或红黑树组成的。数组的每个元素是一个链表或红黑树的头节点,这种结构也被称为 “桶”。当有新的键值对要插入时,会根据键的哈希值计算出在数组中的索引位置,然后将键值对插入到对应的链表或红黑树中。
  • 哈希算法
    • 计算哈希值:首先会调用键对象的hashCode()方法得到一个哈希值,这个哈希值是一个 32 位的整数。为了让哈希值更均匀地分布在数组中,还会进行一些扰动处理,以减少哈希冲突的概率。
    • 确定数组索引:通过对哈希值进行取模运算(在实际实现中通常使用位运算来提高效率),得到该键值对应该存储在数组中的索引位置。即index = hash & (table.length - 1),其中hash是经过处理后的哈希值,table.length是数组的长度,并且数组长度通常是 2 的幂次方,这样可以保证取模运算的结果均匀分布。

怎么操作HashMap呢?

  • 插入操作
    • 无哈希冲突:当计算出的索引位置对应的数组元素为空时,直接将键值对插入到该位置,形成一个新的链表节点或红黑树节点。
    • 有哈希冲突:当计算出的索引位置已经有元素存在时,就发生了哈希冲突。此时,新的键值对会以链表节点的形式插入到该位置的链表末尾(在 Java 7 及之前),或者在 Java 8 及之后,当链表长度达到一定阈值(默认为 8)时,链表会转化为红黑树,以提高查找效率。
  • 查找操作
    • 计算索引:首先根据要查找的键的哈希值计算出在数组中的索引位置。
    • 遍历链表或红黑树:如果该索引位置的桶中存储的是链表,则需要遍历链表,逐个比较节点的键是否与要查找的键相等;如果是红黑树,则按照红黑树的查找算法进行查找,直到找到匹配的键或确定不存在为止。

下列我将展示代码来讲解

import java.util.HashMap;
import java.util.Map;

public class HashMapExample {
    public static void main(String[] args) {
        // 1. 创建一个 HashMap 实例,键的类型为 String,值的类型为 Integer
        Map hashMap = new HashMap<>();

        // 2. 插入键值对
        hashMap.put("apple", 1);
        hashMap.put("banana", 2);
        hashMap.put("cherry", 3);

        // 3. 查找键对应的值
        Integer value = hashMap.get("banana");
        System.out.println("The value of 'banana' is: " + value);

        // 4. 检查键是否存在
        boolean containsKey = hashMap.containsKey("apple");
        System.out.println("Does HashMap contain 'apple'? " + containsKey);

        // 5. 遍历 HashMap
        System.out.println("All key-value pairs in the HashMap:");
        for (Map.Entry entry : hashMap.entrySet()) {
            System.out.println(entry.getKey() + " -> " + entry.getValue());
        }

        // 6. 删除键值对
        hashMap.remove("cherry");
        System.out.println("After removing 'cherry':");
        for (Map.Entry entry : hashMap.entrySet()) {
            System.out.println(entry.getKey() + " -> " + entry.getValue());
        }
    }
}

其中我们可以发现上述没提及的负载因子与扩容机制

创建一个初始容量为 4,负载因子为 0.5 的 HashMap。当插入 3 个元素后,负载为 3/4 > 0.5(负载因子),会触发扩容操作。扩容后数组长度变为原来的 2 倍(即 8),然后重新计算每个键值对在新数组中的索引并进行迁移。

之后我们将HashMap与其他Map比较

  • 与 Hashtable 比较
    Hashtable hashtable = new Hashtable<>();
    // hashtable.put(null, 1); // 会抛出 NullPointerException,Hashtable 不允许键为 null
    // hashtable.put("key", null); // 会抛出 NullPointerException,Hashtable 不允许值为 null
    HashMap hashMap = new HashMap<>();
    hashMap.put(null, 1);
    hashMap.put("key", null);

Hashtable 是线程安全的,不允许键或值为 null;而 HashMap 是非线程安全的,允许键和值为 null

  • 与 TreeMap 比较
TreeMap treeMap = new TreeMap<>();
treeMap.put("banana", 2);
treeMap.put("apple", 1);
treeMap.put("cherry", 3);
System.out.println("TreeMap sorts keys: " + treeMap);

  TreeMap 基于红黑树实现,会对键进行排序;而 HashMap 是根据哈希值存储和查找键值对,不保证键的顺序。TreeMap 的查找、插入和删除操作的时间复杂度为 O(log n)HashMap 在没有哈希冲突的情况下,这些操作的时间复杂度为 O(1)

最后做个总结

  1. 原理
    • 数据结构:底层采用数组 + 链表 / 红黑树的结构,数组元素作为链表或红黑树的头节点(桶),键值对依键的哈希值确定在数组中的索引位置后插入。
    • 哈希算法:先调用键对象的 hashCode() 方法获 32 位整数哈希值,经扰动处理让哈希值更均匀分布,再通过位运算(index = hash & (table.length - 1),数组长度通常为 2 的幂次方)确定数组索引。
    • 插入操作:无哈希冲突时,直接插入键值对;有哈希冲突时,Java 7 及之前新键值对插入链表末尾,Java 8 及之后链表长度达 8 则转化为红黑树。
    • 查找操作:先依键的哈希值计算数组索引,若索引处为链表,遍历链表比较键;若为红黑树,按红黑树查找算法查找。
  2. 扩展
    • 负载因子:衡量元素填充程度,等于元素数量除以数组长度,默认 0.75,元素数量达 数组长度 * 负载因子 时触发扩容,是时间和空间效率的平衡值。
    • 扩容机制:元素数量达阈值(数组长度 * 负载因子)时扩容,新数组长度为原来 2 倍,需重新计算键值对在新数组的索引位置并迁移。
    • 与其他 Map 比较
      • Hashtable:线程安全,不允许键或值为 nullHashMap 非线程安全,允许键和值为 null
      • TreeMap:基于红黑树,可对键排序,查找、插入和删除操作时间复杂度为 O(log n)HashMap 依哈希值存储查找,不保证键顺序,无哈希冲突时操作时间复杂度为 O(1)

  感谢观看!!!

你可能感兴趣的:(面试,java,学习)