JAVA面试汇总(三)集合(三)
JAVA面试汇总-集合部分,计划四篇,这是第三篇。标题我就按照上一篇的继续,这样每次一部分总结完了,可以直接串起来来个长篇的。
11.集合Set实现Hash怎么防止碰撞
(1)这个实际上问的,HashSet在存入值时候的操作。
(2)HashSet存入的时候,先获取存入值的hashCode,如果HashSet中没有这个hashCode,则认为是新值,直接存入。
(3)如果hashCode相同,这个时候先通过==或者euqals判断,存入的值是否相同,==或者equals有任何一个相同,则认为是相同的值,就返回false。
具体可以看下这一行源码:
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
12.数组和链表的区别
(1)数组在内存中连续;链表采用动态内存分配的方式,在内存中不连续,每个节点存储了下个节点的位置
(2)数组长度固定,不支持动态改变数组大小(只能通过复制到新的扩容后的数组中);链表支持动态增加或者删除元素,直接在链上增加或者删除即可
(3)数组在内存中顺序存储,可通过下标访问,访问效率高;链表访问效率低,如果想要访问某个元素,需要从头遍历,查找效率慢
(4)数组可能越界,链表是逐个向后查找,不存在越界
(5)数组适合查找次数多,增加修改次数少的情况;链表适合增加修改次数多,查找次数少的情况。
13.Array和ArrayList有何区别?什么时候更适合用Array
(1)Array是数组;ArrayList是列表
(2)Array声明时候定于长度,且长度不可变;ArrayList不必声明长度,后期达到一定程度,会自动扩容。
(3)Array声明时候定义类型,如下面的String[];ArrayList如果不适用泛型,则可以存入多种类型。
String[] b = new String[10];
b[0] = "0";
(4)知道初始确定的长度且长度不变的时候就可以用Array;不确定长度,而且经常变化可以用ArrayList
14.EnumSet是什么?
(1)EnumSet是一个针对Enum枚举类的集合,抽象类,不能通过new来创建
(2)底层有两个实现,集合中个数超过64个为JumboEnumSet,小于64个为RegularEnumSet
(3)RegularEnumSet通过long作为存储,实际是按照位去存储的,这样非常节省空间,比如我下面那个代码中的实际上就4个内容,存储时候通过二进制的1111(转换为十进制就是15存储到long中),就把全部四个内容存储进入了,如果只有三个内容,TEST_0,TEST_1,TEST_3,那么就存储为1101(转换为十进制就是13存储到long中),即可。
(4)JumboEnumSet超过64位按照long存储费劲了,因此采取了long[]数组来存储,分成64位一组,这样超过64的时候分成多组,比如110个,那就么就是long[0]是满的存储-1,long[1]就是110个Enum-64个Enum=46个Enum,通过位数实际写成46个1,高位补齐0,转换为十进制,就是long[1]=70368744177663。
(5)有一些方法,allOf,noneOf等的操作,具体如下:
import java.util.EnumSet;
public enum TestEnum {
TEST_0(0),
TEST_1(1),
TEST_2(2),
TEST_3(3);
private final int value;
private TestEnum(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
public static void main(String[] args) {
System.out.println("EnumSet.allOf");
EnumSet<TestEnum> set = EnumSet.allOf(TestEnum.class);
set.forEach(s-> System.out.println(s));
System.out.println("======");
System.out.println("EnumSet.noneOf");
EnumSet<TestEnum> enumSetNone = EnumSet.noneOf(TestEnum.class);
enumSetNone.forEach(s-> System.out.println(s));
System.out.println("======");
System.out.println("EnumSet.add");
enumSetNone.add(TestEnum.TEST_2);
enumSetNone.forEach(s-> System.out.println(s));
System.out.println("======");
System.out.println("EnumSet.range TEST_0 to TEST_2");
EnumSet<TestEnum> enumSetRange = EnumSet.range(TestEnum.TEST_0, TestEnum.TEST_2);
enumSetRange.forEach(s-> System.out.println(s));
System.out.println("======");
System.out.println("EnumSet.of");
EnumSet<TestEnum> enumSOf = EnumSet.of(TestEnum.TEST_0, TestEnum.TEST_2);
enumSOf.forEach(s-> System.out.println(s));
}
}
//输出
EnumSet.allOf
TEST_0
TEST_1
TEST_2
TEST_3
======
EnumSet.noneOf
======
EnumSet.add
TEST_2
======
EnumSet.range TEST_0 to TEST_2
TEST_0
TEST_1
TEST_2
======
EnumSet.of
TEST_0
TEST_2
15.Comparable和Comparator接口有何区别?
(1)Comparable接口通过实现类实现compareTo方法来实现实体的比较大小。这样就可以直接使用Collections.sort就可以对实体的List进行排序了,排序的结果是按照compareTo方法比较大小的降序。
(2)Comparator接口是在Collections.sort中直接使用的,可以针对那些没有实现Comparable的实体,或者不按照compareTo去比较排序的情况。
public static void main(String[] args) {
List<TestComparator> list = new ArrayList<TestComparator>();
list.add(TestComparator.builder().a("a").b(1).build());
list.add(TestComparator.builder().a("a1").b(2).build());
Collections.sort(list,new Comparator<TestComparator>(){
@Override
public int compare(TestComparator o1, TestComparator o2) {
return o2.getB() - o1.getB();
}
});
list.forEach(System.out::println);
}
@Data
@Builder
static class TestComparator{
String a;
int b;
}
//输出
TestEnum.TestComparator(a=a1, b=2)
TestEnum.TestComparator(a=a, b=1)