X32专项练习部分16

HashMap和Hash Table的区别

/*
Hashtable 和 HashMap 的区别是:
正确答案: B C D E

Hashtable 是一个哈希表,该类继承了 AbstractMap,实现了 Map 接口
HashMap 是内部基于哈希表实现,该类继承AbstractMap,实现Map接口
Hashtable 线程安全的,而 HashMap 是线程不安全的
Properties 类 继承了 Hashtable 类,而 Hashtable 类则继承Dictionary 类
HashMap允许将 null 作为一个 entry 的 key 或者 value,而 Hashtable 不允许

A、错,Hashtable 是一个哈希表,该类继承自Dictionary类,实现了Map接口
B、错,HashMap是基于哈希表实现的,每一个元素是一个key-value对,
    其内部通过单链表解决哈希冲突问题,容量不足(超过了阀值)时,同样会自动增长
    该类继承AbstractMap,实现Map接口
C、对,Hashtable是线程安全的,而HashMap是线程不安全的
    Hashtable中的方法是synchronize的
    而HashMap中的方法在缺省情况下是非synchronize的。
D、讲的没错,但这跟Hashtable和HashMap的区别没关系
E、比较细节了,记住就行,HashTable如果放null会报空指针异常

哈希值的使用不同
    HashTable直接使用对象的hashCode
    而HashMap重新计算hash值

    hashCode是jdk根据对象的地址
    或者字符串
    或者数字算出来的int类型的数值。

    Hashtable计算hash值,直接用key的hashCode()
    而HashMap重新计算了key的hash值
    Hashtable在求hash值对应的位置索引时,用取模运算

    HashMap会重新计算hash值,下面是源码
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        // key如果是null,新hashcode是0,否则计算新的hashcode
    }
    对于为什么重新计算哈希值
    这一点很难理解
    后期学计算机组成原理会涉及到这一点

扩容方式不同
    Hashtable不要求底层数组的容量一定要为2的整数次幂
    而HashMap则要求一定为2的整数次幂
    Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍

线程安全区别
    在hashmap做put操作的时候会调用到以上的方法
    现在假如A线程和B线程同时对同一个数组位置调用addEntry
    两个线程会同时得到现在的头结点,然后A写入新的头结点之后,B也写入新的头结点
    那B的写入操作就会覆盖A的写入操作造成A的写入操作丢失
 */

下三角矩阵对角元素在1维数组中的存放位置

/*
将一个n×n的对称矩阵A的下三角部分按行存放在一个一维数组B中,
A[0][0]存放在B[0]中,那么第i行的对角元素A[i][i]在B中的存放位置是(  )

正确答案: A
(i+3)×i/2
(i+1)×i/2
(2n-i+1)×i/2
(2n-i-l)×i/2

这道题我审题错了
很明显横纵坐标的位置都是i
题目让你求对角线元素的存放位置
题目让你求对角线元素的存放位置
题目让你求对角线元素的存放位置

快一点的方法,前几个代进去基本就可以得到正确答案了
    B[0] = A[0][0],i=0,在第0个位置,ABCD均符合
    B[2] =  A[1][1] ,i=1,在第2个位置,只有A符合
    再试一下B[5] = A[2][2],i=2,在第5个位置,也只有A符合

使用递归去做
    只考虑下三角,第 i 行有 i+1 个元素,设B[i]的索引为S[i]
    则S[i] = S[i-1] + i + 1
    就是上面所有的下三角元素行加上当前下三角元素行

    初始条件 S[0] = 0,S[1] = 2,S[2] = 5
    前3行的总元素个数一眼就能看出来
    可以写程序使用递归去做
    public static int findPosition(int n) {
        if (n == 0) {
            return 0;
        }else {
            return findPosition(n - 1) + n + 1;
        }
    }
 */

堆和栈的区别

/*
下列关于堆和栈的区别描述错误的有
正确答案: A
申请方式的不同,堆是系统自动分配,栈是自己申请
栈的大小是固定的,堆的大小受限于系统中有效的虚拟内存
栈的空间由系统决定何时释放,堆需要自己决定何时去释放
堆的使用容易产生碎片,但是用起来最方便

先通俗解释一下
堆:自己做菜自己吃,什么时候收盘子自己知道,但是可能会浪费(产生碎片)
因为可能自己一个人吃不完
栈:公司食堂,你吃饭由食堂工作人员帮你打饭和分配位置,吃完了工作人员帮你收盘子
你浪费粮食(碎片)那是不可能的,因为食堂会把碎片拿去喂猪

再官方说一下
栈内存操作系统来分配,堆内存由程序员人为开辟
栈有系统自动分配,只要栈剩余空间大于所申请空间
系统将为程序提供内存,否则将报异常提示栈溢出
 */

计算存储地址

/*
已知 10*12 (10行12列) 的二维数组 A ,以行序为主序进行存储,每个元素占 1 个存储单元
已知 A[1][1] 的存储地址为 420 ,则 A[5][5] 的存储地址为 ( )

正确答案: C
470
471
472
473

对于本题,不用纠结于起始位置到底在哪里
题目已经告诉我们了A[1][1]的地址
只需要求题目要求的元素与A[1][1]之间有多少个元素就阔以了
A[5][5]与A[1][1]之间有4*12+4=52个元素
而A[1][1]地址为420,那么A[5][5]的地址就为420+52=472

注意两者之间相差52
最后要减去自身
 */

第2趟快速排序的结果

/*
下列选项中,不可能是快速排序第2趟排序结果的是
正确答案: C
2,3,5,4,6,7,9
2,7,5,6,4,3,9
3,2,5,4,7,6,9
4,2,3,5,7,6,9

补充快速排序知识点
快速排序的最坏结果是正序和逆序
快排的阶段性排序结果的特点是
第i趟完成时,会有i个以上的数
出现在它最终将要出现的位置
即它左边的数都比它小,它右边的数都比它大

题目问第二趟排序的结果,即要找不存在2个这样的数的选项
A选项中,2、3、6、7、9均符合,所以A排除
B选项中,2、9均符合,所以B排除
D选项中,5、9均符合,所以D选项排除
最后看C选项,只有9一个数符合,所以C不可能是快速排序第二趟的结果

我的通俗理解
第2趟排序,至少有2个数出现在最终结果的最终位置
 */

选择合适的排序方法

/*
设有 1000 个基本有序的元素,希望用最快的速度挑选出其中前 10 个最大的元素,最后选用( )排序法。

冒泡排序
快速排序
直接插入排序
归并排序

条件说基本有序
并且在1000个数中取最大的前10个元素
冒泡排序作为交换排序的方法,在基本有序的集合中元素位移次数最少
其次,冒泡排序每次会将最大的元素放在最后面
想要获取前10个最大元素,只需要截取排序10趟过后的最后10个元素即可
 */

第1轮堆排序结果

/*
将整数数组(7-6-3-5-4-1-2)按照堆排序的方式原地进行升序排列
请问在第一轮排序结束之后,数组的顺序是
正确答案: C
2-6-3-5-4-1-7
6-2-3-5-4-1-7
6-5-3-2-4-1-7
1-5-3-2-4-6-7

原数组已经是一个大顶堆,可直接开始交换调整
交换堆顶和左子树叶子节点
然后调整堆
直到所有的父节点大于两个子树
第1轮排序才算完成

所以是考的第1轮大顶堆了理解
不仅是交换,还要调整堆

      7                 2                   6                     6
     /  \             /  \                /    \                 /  \
   6     3    ==>   6     3       ==>     2      3       ==>    5    3
  /  \  /  \       /  \    /   \       /  \    /    \         /  \    /  \
5   4  1   2      5   4  1     7      5   4     1     7       2   4  1    7

由此得出,第一轮结束后的顺序是:6,5,3,2,4,1,7。
 */

包装类和常量池

abstract class Test20 {
    public static void main(String[] args) {
        Float s=new  Float(0.1f);
        Float t=new  Float(0.1f);
        Double u=new  Double(0.1);

        System.out.println(s == t); // false
        System.out.println(u.equals(t)); // false
        System.out.println(s.equals(t)); // true
        /*
        Float,Double类都重写了equals方法
        在两者比较之前会判断
        是不是都是同一个类型
        如果是就再次比较数值的大小
        如果不是,直接返回false
        下面是源码
        public boolean equals(Object obj) {
          return (obj instanceof Float)
                  && (floatToIntBits(((Float)obj).value) == floatToIntBits(value));
        }
         */

        String a="a";
        String b="b";
        String c=a+b;
        String d=new String("ab");

        System.out.println((a+b).equals(c)); // true
        System.out.println(a+b==c); // false
        System.out.println(c==d); // false
        System.out.println(c.equals(d)); // true

        /*
        String对象的两种创建方式:
            第一种方式: String str1 = "aaa";  
            是在常量池中获取对象("aaa" 属于字符串字面量
            因此编译时期会在常量池中创建一个字符串对象
            如果常量池中已经存在该字符串对象则直接引用)
            
            第二种方式: String str2 = new String("aaa"); 
            一共会创建两个字符串对象一个在堆中
            一个在常量池中(前提是常量池中还没有"aaa"对象)
            
            System.out.println(str1==str2); //false

        String类型的常量池比较特殊
            它的主要使用方法有两种
            第1种:直接使用双引号声明出来的String对象会直接存储在常量池中
            第2种:如果不是用双引号声明的String对象,可以使用String提供的intern方法
                String.intern()是一个Native方法
                它的作用是: 
                如果运行时常量池中已经包含一个等于此 String 对象内容的字符串
                则返回常量池中该字符串的引用; 
                如果没有,则在常量池中创建与此String内容相同的字符串,并返回常量池中创建的字符串的引用
            
                String s1 = new String("AAA");
                String s2 = s1.intern();
                String s3 = "AAA";
                System.out.println(s2); // AAA
                System.out.println(s1 == s2); // false,因为一个是堆内存中的String对象一个是常量池中的String对象,
                System.out.println(s2 == s3); // true, s1,s2指向常量池中的”AAA“
                
        有关字符串拼接:
            String a = "a";
            String b = "b";
                 
            String str1 = "a" + "b";//常量池中的对象
            String str2 = a + b; //在堆上创建的新的对象     
            String str3 = "ab";//常量池中的对象
            System.out.println(str1 == str2);//false
            System.out.println(str1 == str3);//true 
            System.out.println(str2 == str3);//false
                 */
    }
}

总目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

muskfans

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值