一些建筑设计网站,电子商务名词解释,新手织梦网建设网站,搜索引擎优化的技巧一、一面问题
1.线程池的主要参数
核心线程数最大线程数空闲线程存活时间存活时间单位任务队列线程工厂拒绝策略允许核心线程超时
2. 线程的状态
新建状态就绪状态运行状态阻塞状态死亡状态
补充#xff1a;线程阻塞的原因
线程调用sleep()方法进入睡眠状态 线程得到一个…一、一面问题
1.线程池的主要参数
核心线程数最大线程数空闲线程存活时间存活时间单位任务队列线程工厂拒绝策略允许核心线程超时
2. 线程的状态
新建状态就绪状态运行状态阻塞状态死亡状态
补充线程阻塞的原因
线程调用sleep()方法进入睡眠状态 线程得到一个锁但是该锁正在被其他线程占用线程可能在等待某个触发条件线程调用了一个在I/O上被阻塞的操作
3. 双精度类型转整型精度丢失问题底层原因
(1)在数据表示方式的差异
双精度类型在Java等编程语言中double类型使用64位二进制数表示一个双精度浮点数。这64位被分为三部分1位符号位、11位指数位和52位尾数位。这种表示方式可以精确地表示非常大或非常小的数值但在表示某些十进制小数时由于二进制和十进制之间的转换限制可能会出现精度损失。整型int类型是一个32位的有符号整数没有小数部分。它只能表示整数范围内的数值。
(2)浮点数的运算特性
不精确性由于浮点数采用二进制科学计数法表示某些十进制小数在转换为二进制时无法精确表示因此浮点数运算通常是不精确的。舍入误差当双精度浮点数转换为整型时由于浮点数本身可能存在精度损失且整型没有小数部分因此必须进行舍入。
(3)底层实现的原因
二进制与十进制的转换计算机内部使用二进制进行数据存储和运算而人类通常使用十进制。当十进制小数转换为二进制浮点数时可能会出现无法精确表示的情况。硬件和指令集的限制计算机硬件和指令集对浮点数的运算和表示方式有特定的要求。这些要求可能导致在浮点数转换为整型时出现精度丢失。
(4)代码示例 double originalDouble 9.99;
int convertedInt (int) originalDouble;
System.out.println( 原始 double 值: originalDouble);
System.out.println( 转换后的 int 值: convertedInt);
(5)解决方法
使用BigDecimal类BigDecimal类可以提供任意精度的浮点数运算从而避免精度丢失。 四舍五入在转换之前使用Math.round() 等方法对浮点数进行四舍五入处理以减小精度丢失的影响。四舍五入在转换之前使用Math.round() 等方法对浮点数进行四舍五入处理以减小精度丢失的影响。
4.static修饰的类和修饰方法在底层上有什么不同。
内存分配静态内部类拥有独立的内存空间而静态方法是分配在类的内存区域中不占用对象的内存。加载时机静态内部类在编译时被加载而静态方法在类加载时被初始化。访问方式静态内部类可以直接访问外部类的静态成员但不能直接访问非静态成员而静态方法只能访问静态属性和其他静态方法不能直接访问实例变量和实例方法。
5.代码题
需求: 输入两个字符串类型的数两数相加最后返回一个字符串类型的结果。
(1)步骤
类定义定义了一个名为StringNumberAdder的公开类。方法定义在StringNumberAdder类中定义了一个静态方法addStrings该方法接收两个字符串参数num1和num2并返回一个字符串类型的结果。字符串转整数使用Integer.parseInt(num1) 和Integer.parseInt(num2) 方法将输入的字符串转换为整数。整数相加将转换后的两个整数相加得到和sum。整数转字符串使用Integer.toString(sum) 方法将和转换回字符串。主方法在main方法中创建了两个字符串num1和num2并调用addStrings方法将它们相加。打印出结果。 (2)代码
public class StringNumberAdder {/** * 将两个字符串类型的数字相加并返回结果字符串。 * * param num1 第一个字符串数字 * param num2 第二个字符串数字 * return 相加后的结果字符串*/ public static String addStrings(String num1, String num2) { // 将字符串转换为整数进行相加 int intNum1 Integer.parseInt(num1); int intNum2 Integer.parseInt(num2); // 计算和 int sum intNum1 intNum2; // 将和转换回字符串并返回return Integer.toString(sum); } public static void main(String[] args) {// 测试用例 String num1 123; String num2 456; String result addStrings(num1, num2);System.out.println(The result of adding num1 and num2 is: result);}
}
二、二面问题
1.说一下数据库的存储引擎有哪些
MySQL/MariaDB InnoDB MySQL的默认存储引擎。支持ACID事务、行级锁定和外键约束。适用于需要高并发和事务处理能力的应用。提供了自动崩溃恢复功能。 MyISAM MySQL早期的默认存储引擎之一。提供了高速的读操作性能但写操作性能相对较差。支持表级锁定、全文索引和数据压缩。适用于读多写少的应用场景。 MemoryHEAP 将数据存储在内存中提供极快的读写速度。由于数据存储在内存中重启后会丢失数据。适用于需要快速访问的临时数据如会话信息和缓存。 CSV 将数据存储在CSV文件中便于数据的导入和导出。不支持事务和索引性能较低。适用于需要与其他应用进行数据交换的场景。 Archive 适用于存储大量历史数据和归档数据。提供了高效的数据压缩能力但只支持INSERT和SELECT操作。不支持事务和外键约束。 Federated 允许在不同的MySQL实例之间创建分布式表。适用于分布式数据库系统。不存储实际数据和索引只是一个指向远程表的接口。 Aria MariaDB的默认存储引擎。旨在替代MyISAM提供了更好的崩溃恢复能力和事务支持。支持事务和行级锁定但不支持外键约束。 TokuDB 适用于处理大数据量和高写入负载的应用。采用了Fractal Tree索引技术提供了高效的压缩和写入性能。支持事务和行级锁定。 RocksDB 由Facebook开发以高效的写入性能和低延迟著称。使用了LSMLog-Structured Merge树索引结构。支持事务和行级锁定。
MySQL Cluster NDB MySQL Cluster的默认存储引擎。支持高可用性和高扩展性。提供了数据的水平分片和自动故障转移功能。
其他 XtraDB Percona Server的默认存储引擎。在InnoDB的基础上进行了多项优化如支持更多的并发线程和更高效的缓存管理。 MERGE 允许将多个MyISAM表组合成一个逻辑表。适用于需要对大数据集进行分区管理的场景。 EXAMPLE 一个示例存储引擎主要用于学习和开发自定义存储引擎。不实际存储数据仅用于展示如何实现一个存储引擎的基础框架。 BLACKHOLE 一种虚拟存储引擎所有写入的数据都会被丢弃。通常用于测试和调试数据库复制和日志记录机制。
2. 数据库的存储结构是
(1)关系型数据库的存储结构
关系表索引视图触发器
(2) 键值对
文档图形列族
(3)储存层次
页区段表空间
(4)连接器
3. B树和B树的区别
(1)节点存储数据的方式不同
B树叶子结点和非叶子节点都会存储数据指针和数据共同保存在同一节点中。B树数据均保存在叶子节点非叶子节点只存储索引信息。
(2)查找数据过程不同
B树需要在各个节点上进行查找查找数据的效率不稳定。B树需要在叶子节点上查找非叶子节点只用于索引定位每次查找都会从父节点到叶子节点结束。
(3)空间利用率不同
B树每个节点都存储数据空间利用率相对较低。B树只有叶子节点存储数据非叶子节点只存储索引信息空间利用率更高。
(4)结构稳定性不同
B树插入和删除数据需要频繁变更树的结构结构不稳定。B树插入和删除数据操作均放在叶子节点维护了树结构的稳定性
(5)范围查找性能不同
B树需要在各个节点上逐个查找范围查找效率较低。B树所有数据记录都存储在叶子节点上且叶子节点同时还维护了一条双向链表提高了范围查询的效率。
(6)使用场景不同
B树更适合于数据库的索引结构处理大量点查询。B树更适合文件系统等场景处理大量范围查询和排序操作。
4.HashMap底层实现(HashMap相关知识) 一、什么是
基于哈希表的数据结构允许以O(1)的时间复杂度进行元素的插入查询和删除
二、底层结构
1.数据结构
在1.8以后数组链表红黑树
数组HashMap底层是一个数组每个数组元素存放一个链表或红黑树在JDK 1.8之后链表过长时会转化为红黑树。链表当新元素插入HashMap时它首先根据哈希值找到数组中的某个位置桶。如果该位置为空则直接插入如果该位置已经存在元素发生碰撞则通过链表解决冲突。红黑树在JDK 1.8中当链表长度超过一定阈值默认是8时链表会转换为红黑树从而将时间复杂度从O(n)降低到O(log n)。 2.哈希函数和哈希值
每个键都会通过哈希函数计算出一个哈希值然后通过哈希值决定数据应该存储在哪个桶中。桶是一个数组的存储位置。哈希函数的主要目的是将数据均匀地分布在不同的桶中从而减少哈希碰撞即两个不同的键映射到同一个桶中的情况。
3.负载因子和扩容
(1)负载因子
HashMap有一个重要的参数叫负载因子它决定了当数组中元素数量超过数组容量的多大比例时会触发扩容操作。默认的负载因子是0.75。
(2)扩容操作
当HashMap的元素数量达到数组容量的75%时HashMap会自动进行扩容操作通常会将数组容量扩展为原来的2倍。 扩容时HashMap会重新分配一个更大的数组并将原来的元素重新映射到新的数组中
三、底层代码
1.关键点解析
(1)构造函数
HashMap提供了多个构造函数包括无参构造函数、指定初始容量的构造函数、指定初始容量和负载因子的构造函数等。
(2)put方法
put方法是插入元素的核心逻辑。首先计算键的哈希值。其次根据哈希值找到数组中的桶位置。最后如果该位置为空则直接插入新元素如果该位置已经存在元素则通过链表或红黑树解决冲突。如果链表长度超过阈值默认是8则链表会转换为红黑树。如果元素数量超过扩容阈值默认是数组容量的75%则进行扩容操作。
(3)get方法 get方法用于根据键查找对应的值。首先计算键的哈希值。其次根据哈希值找到数组中的桶位置。最后如果该位置为空则返回null如果该位置上有元素则遍历该位置对应的链表或红黑树找到与键相等的键值对然后返回该键值对的值。
(4)resize方法
resize方法是扩容的核心逻辑。重新分配一个更大的数组并将原来的元素重新映射到新的数组中。在重新映射过程中会根据元素的哈希值计算在新数组中的位置并将元素插入到相应的桶中。
2.示例代码
(1)思路
首先创建了一个HashMap实例并插入了一些键值对。然后通过键来访问元素并遍历了HashMap中的所有键值对。最后通过插入大量元素来触发扩容操作并再次遍历了HashMap。
(2)代码
import java.util.HashMap; public class HashMapExample { public static void main(String[] args) { // 创建HashMap实例 HashMapString, Integer map new HashMap();// 插入键值对 map.put(apple, 1);map.put(banana, 2); map.put(cherry, 3); // 访问元素System.out.println(map.get(apple)); // 输出: 1 System.out.println(map.get(orange)); // 输出: null // 遍历HashMap for (String key : map.keySet()) {System.out.println(key : map.get(key)); }// 扩容操作自动触发 for (int i 0; i 1000; i) {map.put(key i, i); } // 再次遍历HashMap for (String key : map.keySet()) {System.out.println(key : map.get(key)); } }
}
四、线程安全性
1.HashMap是非线程安全
多个线程同时访问和修改HashMap时可能会出现数据不一致的情况。解决方法增加额外的同步机制
2.ConcurrentHashMap
一个线程安全的哈希表通过分段锁来实现并发访问
五、键的null值
1.HashMap允许null值
HashMap允许使用一个null键和多个null值。当使用null键时它会被映射到数组的第一个位置索引为0的桶。 map.put(null, value1); // 允许键为 null
map.put(key1, null); // 允许值为 null 2.与HashTable的区别
Hashtable不允许键或值为null。如果强制将null作为键或值插入Hashtable会抛出NullPointerException。
六、遍历方式
1. 使用 for-each 循环遍历键值对
for (Map.EntryString, String entry : map.entrySet()) {String key entry.getKey(); String value entry.getValue(); System.out.println(Key: key , Value: value);
}
2. 使用 Iterator 遍历键值对
IteratorMap.EntryString, String iterator map.entrySet().iterator();
while (iterator.hasNext()) {Map.EntryString, String entry iterator.next(); String key entry.getKey(); String value entry.getValue(); System.out.println(Key: key , Value: value);
}
3. 使用 keySet 遍历键
for (String key : map.keySet()) {String value map.get(key); System.out.println(Key: key , Value: value);
}
4. 使用 values 遍历值
for (String value : map.values()) {System.out.println(Value: value);
}
七、常见面试题
1.为什么 HashMap 在 JDK 1.8 中引入了红黑树
在 JDK 1.8 之前HashMap 使用链表来解决哈希冲突。当链表过长时查找、插入和删除操作的时间复杂度会退化为 O(n)。引入红黑树后当链表长度超过 8 时链表会转换为红黑树将时间复杂度降低到 O(log n)提高了性能。
2.HashMap 的初始容量和负载因子是多少
HashMap 的默认初始容量是 16负载因子是 0.75。
3.HashMap 的扩容机制是什么
当 HashMap 的元素数量超过数组容量的 75% 时会触发扩容操作数组容量会扩展为原来的 2 倍。扩容时HashMap 会重新分配一个更大的数组并将原来的元素重新映射到新的数组中。
4.HashMap 如何保证线程安全
HashMap 本身不是线程安全的。如果需要在多线程环境下使用 HashMap可以使用 ConcurrentHashMap 或者对 HashMap 进行同步处理例如使用 Collections.synchronizedMap 方法。
八、HashMap性能优化
1.初始容量的选择
2.负载因子的调整
3.自定义哈希函数
九、Hashmap、HashTable和LinkedHashMap区别
特点/实现类HashMapHashTableLinkedHashMap线程安全非线程安全线程安全非线程安全允许null键/值允许一个null键和多个null值不允许null键和null值允许一个null键和多个null值迭代顺序不保证顺序不保证顺序保持插入顺序底层实现数组链表红黑树JDK 1.8及以后数组链表继承自HashMap内部维护一个双向链表性能特点通常性能最高但不安全性能较低但线程安全性能略低于HashMap因为维护了顺序同步机制无使用synchronized关键字无扩容机制当元素数量超过数组容量的75%时扩容同HashMap同HashMap适用场景单线程环境下需要高性能的键值对存储多线程环境下需要线程安全的键值对存储需要保持键值对插入顺序的场景
5.多线程的执行过程
线程创建定义线程类并创建线程实例。线程启动调用 start() 方法启动线程。线程调度线程进入就绪状态等待 CPU 时间片然后进入运行状态。线程同步使用同步机制避免竞态条件。线程阻塞线程在等待某个条件或资源时进入等待或阻塞状态。线程终止线程正常或异常终止或通过显式终止。线程销毁线程终止后资源被释放垃圾回收器回收不再使用的线程对象。
6.代码题
需求:字符串压缩。利用字符重复出现的次数编写一种方法实现基本的字符串压缩功能。比如字符串aabcccccaaa会变为a2b1c5a3。若“压缩”后的字符串没有变短则返回原先的字符串。你可以假设字符串中只包含大小写英文字母a至z。 (1)步骤
在这个实现中我们使用了StringBuilder来构建压缩后的字符串因为它比直接使用字符串拼接使用操作符更加高效。StringBuilder内部维护了一个可变的字符数组可以在不创建新字符串对象的情况下追加字符和字符串。
另外我们注意到在遍历字符串时我们只需要遍历到n - 1的位置因为最后一个字符的处理是在循环之外单独完成的。这是为了避免在循环中处理最后一个字符时越界访问数组。
最后我们通过比较压缩后的字符串长度和原始字符串长度来决定返回哪个字符串。如果压缩后的字符串更短则返回它否则返回原始字符串。
(2)代码
public class StringCompression {public static String compressString(String str) {if (str null || str.length() 1) {return str;}StringBuilder compressed new StringBuilder();int count 1;char lastChar str.charAt(0); for (int i 1; i str.length(); i) {if (str.charAt(i) lastChar) {count;} else {compressed.append(lastChar).append(count); lastChar str.charAt(i); count 1;}}// 添加最后一个字符及其计数compressed.append(lastChar).append(count); // 比较原始字符串和压缩字符串的长度 return compressed.length() str.length() ? compressed.toString() : str;}public static void main(String[] args) {String input aabcccccaaa;String compressed compressString(input);System.out.println(Original: input);System.out.println(Compressed: compressed);}
}
(3)结果
对于输入字符串 aabcccccaaa输出将是
Original: aabcccccaaa
Compressed: a2b1c5a3