HashMap的特性
1.考察目标
- HashMap底层的数据结构
- HashMap和Hashtable的区别
2. 题目分析
-
Map集合的特点
-
Map是一个双列集合,将键映射到值的对象
- Map集合的数据结构,只针对键有效,跟值没有关系
-
Map集合的键不能重复,并且一个键最多只能映射到一个值
-
HashMap集合的数据结构是什么?
- 哈希表结构:数组 + 链表
- 通过哈希表结构配合对象类型的hashCode和equals方法就可以保证键的唯一性
- 建议:今后Map集合中键是对象类型的,不要忘记重写它的hashCode和equals方法
- 哈希表结构:数组 + 链表
-
HashMap和Hashtable的区别是?
相同点:
- 都是双列集合,一个键对应一个值
- 都是键不能重复,但是值可以重复
不同点:
-
HashMap是JDK1.2版本出现的,允许存储null键和null值
Hashtable是JDK1.0版本出现的,不允许存储null键和null值
-
HashMap是不同步的,线程不安全
Hashtable是同步的,线程安全
应用场景:
- 双列集合优先使用HashMap集合
- 如果是在多线程的场景下,也无需使用Hashtable集合,我们可以使用ConcurrentHashMap集合。该集合时一个线程同步的。
- Hashtable集合虽然使用不多,但是它有一个给力的子类:Properties集合还在大量使用中,一般配合IO流来加载配置文件!
3. 问题扩展
-
JDK1.8之后,哈希表结构引入了二叉树
- 提高了查询的效率
-
JDK1.8版本之后,某一个索引下的节点数量达到一定程度的时候,会把原本的链表结构,改成二叉树结构
TreeSet集合:二叉树结构(数据能够排序)
二叉树取数据:先取节点左边的,当左边的全部获取完毕,再取自己,最后取节点右边的
4. 应用场景
用于商品出现次数的统计
/**
* 统计商品出现次数的使用场景----HashMap
* 关键是键的对象类型必须重写hashCode和equals方法
*/
public class Demo02_HashMap {
public static void main(String[] args) {
Goods g1 = new Goods("小米手机", 1000);
Goods g2 = new Goods("平板电脑", 7000);
Goods g3 = new Goods("小米手机", 1000);
List<Goods> goodsList = new ArrayList<>();
goodsList.add(g1);
goodsList.add(g2);
goodsList.add(g3);
// HashMap用来计数
Map<Goods, Integer> countMap = new HashMap<>();
for (Goods goods : goodsList) {
if (!countMap.containsKey(goods)) {
// 代表Map集合中不存在该商品
countMap.put(goods, 1);
} else {
// 代表Map集合中已经存在该商品
countMap.put(goods, countMap.get(goods)+1);
}
}
System.out.println(countMap);
}
}
class Goods{
private String name;
private Integer price;
// 省略构造、getter、setter、toString、hashCode、equals方法
}
============================================================
控制台结果:
{Goods{name='小米手机', price=1000}=2, Goods{name='平板电脑', price=7000}=1}
Java虚拟机中的内存模型
1. 考察目标
- JVM(虚拟机)的内存划分
- 不同的数据使用的是那一块内存空间
2. 题目分析
-
Java虚拟机有哪几块内存空间
-
栈内存
方法运行时所进入的内存,里面还会存储程序的局部变量
-
堆内存
new出来的数据都会进入堆内存
-
方法区
字节码文件加载时所进入的内存
-
本地方法区:这块内存空间主要调用的是操作系统相关资源
被native修饰的方法是使用了本地方法区的
public static native long currentTimeMillis(); // 获取1970-1-1至今的毫秒值
-
寄存器:交给CPU进行使用的
-
-
案例:创建Student类并使用,画出数据在内存中的分布
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3HqjSYDj-1602545177026)(C:\Users\Administrator\Desktop\面试题\image\1.PNG)]
3. 问题扩展
-
案例:带有线程的内存图
-
每一个线程都会有自己独立的栈内存空间
-
堆内存中的数据,是被多个线程所共享的(该数据就是共享资源)
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sm7tgjZP-1602545177029)(C:\Users\Administrator\Desktop\面试题\image\2.PNG)]
4.应用场景
- 多线程消费同一商品,可以将该商品库存定义为共享资源,存放于堆内存中
例如:
- 写一个售票窗口类,将车票数定义为共享资源
public class Ticket implements Runnable {
// 多线程共享的票数资源
int ticket = 100;
@Override
public void run() {
while(true) {
synchronized (Ticket.class){
if (ticket <= 0) {
break;
}
System.out.println(Thread.currentThread().getName() + "卖出了第-" + ticket-- + "-号票");
}
}
}
}
- 多线程共享车票数
public class Demo01_Memory {
public static void main(String[] args) {
Ticket t = new Ticket();
new Thread(t, "窗口1").start();
new Thread(t, "窗口2").start();
}
}
String类型
1. 考察目标
- String属于什么数据类型
- String常用的方法
- String创建对象有什么特点
2. 题目分析
-
String属于什么数据类型?
-
数据类型:
基本数据类型:byte、short、int、long、float、double、char、boolean
引用数据类型:类、数组、接口、枚举
-
String属于引用数据类型,因为String被final修饰,所以不能被继承
-
-
String常用的方法?
equals(Object o) // 比较字符串的内容是否相同 equalsIgnoreCase(Object o) // 忽略大小比较字符串 length() // 获取字符串的长度 contains(String s) // 判断是否包含传入的字符串 substring(int start) // 从start索引开始截取字符串 substring(int start, int end) // 从start索引开始到end索引结束截取字符串,[start, end) toLowerCase() // 把字符串转换为小写字符串 toUpperCase() // 把字符串转换为大写字符串 String[] split(String regex) // 根据传入的字符串切割原字符串,返回的是字符串数组;如果切割的是.的话,传入的字符串应该写"\\."
需要注意的点:
- substring(int start, int end) 包含start索引的字符,不包含end索引的字符
- split(String regex) 参数是正则字符串,一些特殊字符串如
“.”
的话,应该写成“\\.”
-
String创建对象有什么特点?
问题:
String s = new String("xyz")
一共创建了多少次对象?回答:字符串常量都是String对象。如果常量池中存在,则直接调用;不存在则创建。
而new关键字一定是创建了对象。所以该问题分两种情况:
“xyz”在常量池中没有,则创建了两次对象;
“xyz”在常量池中存在,则创建了一次对象。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tCJXcFMv-1602545177031)(C:\Users\Administrator\Desktop\面试题\image\3.PNG)]
3. 应用场景
-
String它被用于裁剪,拼接。搜索字符串,比较字符串,截取字符串,转换大小写等。
-
在项目中不经常发生变化的业务场景中,优先使用String
举例:
-
用户登录需要验证账号密码,我们需要使用equals方法进行判断
-
用户登录中需要填写验证码,我们可以使用equalsIgnoreCase方法进行忽略大小写判断
String,StringBuilder,StringBuffer三者的区别
1. 考察目标
- String和StringBuilder的效率对比
- StringBuilder和StringBuffer的效率对比
2. 问题分析
- 问题:String和StringBuilder的本质区别?
- String是一个不可改变的字符序列,底层是被final修饰的char类型数组
- StringBuilder是一个可变的字符序列,字符串的缓冲区(提高效率)
- 问题:常见的字符串拼接,该选择谁
3. 问题扩展
- 在工作中你们如果在业务层去拼接sql,使用String类型去接收的吗?
4. 应用场景
ingBuilder的效率对比
- StringBuilder和StringBuffer的效率对比
2. 问题分析
- 问题:String和StringBuilder的本质区别?
- String是一个不可改变的字符序列,底层是被final修饰的char类型数组
- StringBuilder是一个可变的字符序列,字符串的缓冲区(提高效率)
- 问题:常见的字符串拼接,该选择谁
3. 问题扩展
- 在工作中你们如果在业务层去拼接sql,使用String类型去接收的吗?
4. 应用场景
- 在项目中如果频繁地拼接字符串,推荐使用StringBuilder