(整理)java面试题(七)数组相关

1、Java中的集合及其继承关系
关于集合的体系是每个人都应该烂熟于心的,尤其是对我们经常使用的List,Map的原理更该如此.这里我们看这张图即可:
此处输入图片的描述

2、poll()方法和remove()方法区别?
poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空,但是 remove() 失败的时候会抛出异常。

3、LinkedHashMap和PriorityQueue的区别
PriorityQueue 是一个优先级队列,保证最高或者最低优先级的的元素总是在队列头部,但是 LinkedHashMap 维持的顺序是元素插入的顺序。当遍历一个 PriorityQueue 时,没有任何顺序保证,但是 LinkedHashMap 课保证遍历顺序是元素插入的顺序。

优先级队列PriorityQueue;迭代不是按照元素的排列顺序访问。删除总是删掉列表中优先级数最小的那个元素

public class Main {

public static void main(String[] args) {
PriorityQueue<LocalDate> pq=new PriorityQueue<>();
pq.add(LocalDate.of(1906,12,9));
pq.add(LocalDate.of(1815,12,9));
pq.add(LocalDate.of(1903,12,9));
pq.add(LocalDate.of(1910,12,9));

System.out.println("Iterating elements...");
for (LocalDate ld:pq)
System.out.println(ld);//迭代不是按照元素的排列顺序访问。
System.out.println("Removing elements...");

while (!pq.isEmpty())
System.out.println(pq.remove());//删除总是删掉列表中优先级数最小的那个元素
}
}
运行结果:
Iterating elements...
1815-12-09
1906-12-09
1903-12-09
1910-12-09
Removing elements...
1815-12-09
1903-12-09
1906-12-09
1910-12-09

4、WeakHashMap与HashMap的区别是什么?
WeakHashMap 的工作与正常的 HashMap 类似,但是使用弱引用作为 key,意思就是当 key 对象没有任何引用时,key/value 将会被回收。

5、ArrayList和LinkedList的区别?
最明显的区别是 ArrrayList底层的数据结构是数组,支持随机访问,而 LinkedList 的底层数据结构是双向循环链表,不支持随机访问。使用下标访问一个元素,ArrayList 的时间复杂度是 O(1),而 LinkedList 是 O(n)。

6、ArrayList和Array有什么区别?
Array可以容纳基本类型和对象,而ArrayList只能容纳对象。
Array是指定大小的,而ArrayList大小是固定的
7、ArrayList和HashMap默认大小?
在 Java 7 中,ArrayList 的默认大小是 10 个元素,HashMap 的默认大小是16个元素(必须是2的幂)。这就是 Java 7 中 ArrayList 和 HashMap 类的代码片段。

8、Comparator和Comparable的区别?
Comparable 接口用于定义对象的自然顺序,而 comparator 通常用于定义用户定制的顺序。Comparable 总是只有一个,但是可以有多个 comparator 来定义对象的顺序。

Comparable可以认为是一个内比较器,实现了Comparable接口的类有一个特点,就是这些类是可以和自己比较的,至于具体和另一个实现了Comparable接口的类如何比较,则依赖compareTo方法的实现,compareTo方法也被称为自然比较方法。如果开发者add进入一个Collection的对象想要Collections的sort方法帮你自动进行排序的话,那么这个对象必须实现Comparable接口。compareTo方法的返回值是int,有三种情况:
1、比较者大于被比较者(也就是compareTo方法里面的对象),那么返回正整数
2、比较者等于被比较者,那么返回0
3、比较者小于被比较者,那么返回负整数

public class Main {

public static void main(String[] args)
{
Person[] people=new Person[]{new Person("xujian", 20),new Person("xiewei", 10)};
System.out.println("排序前");
for (Person person : people)
{
System.out.println(person.getName()+":"+person.getAge());
}
Arrays.sort(people);
System.out.println("\n排序后");
for (Person person : people)
{
System.out.println(person.getName()+":"+person.getAge());
}
}

}
class Person implements Comparable<Person>
{
String name;
int age;
public Person(String name, int age)
{
super();
this.name = name;
this.age = age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
@Override
public int compareTo(Person p)
{
return this.age-p.getAge();
}
}
运行结果:
排序前
xujian:20
xiewei:10

排序后
xiewei:10
xujian:20

Comparator可以认为是是一个外比较器,个人认为有两种情况可以使用实现Comparator接口的方式:
1、一个对象不支持自己和自己比较(没有实现Comparable接口),但是又想对两个对象进行比较
2、一个对象实现了Comparable接口,但是开发者认为compareTo方法中的比较方式并不是自己想要的那种比较方式
Comparator接口里面有一个compare方法,方法有两个参数T o1和T o2,是泛型的表示方式,分别表示待比较的两个对象,方法返回值和Comparable接口一样是int,有三种情况:
①o1大于o2,返回正整数
②o1等于o2,返回0
③o1小于o3,返回负整数

注意:1、若一个类要实现Comparator接口:它一定要实现compare(T o1, T o2) 函数,但可以不实现 equals(Object obj) 函数。
实例:

public class Main {

public static void main(String[] args)
{
Person[] people=new Person[]{new Person("xujian", 20),new Person("xiewei", 10)};
System.out.println("排序前");
for (Person person : people)
{
System.out.println(person.getName()+":"+person.getAge());
}
Arrays.sort(people,new PersonCompartor());
System.out.println("排序后");
for (Person person : people)
{
System.out.println(person.getName()+":"+person.getAge());
}
}

}
class Person
{
String name;
int age;
public Person(String name, int age)
{
super();
this.name = name;
this.age = age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
class PersonCompartor implements Comparator<Person>
{
@Override
public int compare(Person o1, Person o2)
{
return o1.getAge()-o2.getAge();
}
}
运行结果:
排序前
xujian:20
xiewei:10
排序后
xiewei:10
xujian:20

9、如何实现集合排序?
你可以使用有序集合,如 TreeSet 或 TreeMap,你也可以使用有顺序的的集合,如 list,然后通过 Collections.sort() 来排序。

10、如何打印数组内容
方法一:使用循环打印

public class Main {
public static void main(String[] args) {
String[] infos = new String[]{"Java", "Android", "C/C++", "Kotlin"};
StringBuffer strBuffer = new StringBuffer();
for (int i = 0; i < infos.length; i++) {
if (i > 0) {
strBuffer.append(", ");
}
strBuffer.append(infos[i]);
}
System.out.println(strBuffer);
}
}

方法二:使用 Arrays.toString() 打印

public class Main {
public static void main(String[] args) {
String[] infos = new String[] {"Java", "Android", "C/C++", "Kotlin"};
System.out.println(Arrays.toString(infos));
}
}

方法三:使用 JDK8 的 java.util.Arrays.stream() 打印

public class Main {
public static void main(String[] args) {
String[] infos = new String[] {"Java", "Android", "C/C++", "Kotlin"};
Arrays.stream(infos).forEach(System.out::println);
}
}

方法四:使用 Arrays.deepToString() 方法打印。如果数组中有其它数组,即多维数组,也会用同样的方法深度显示

public class Main {
public static void main(String[] args) {
String[] infos = new String[] {"Java", "Android", "C/C++", "Kotlin"};
System.out.println(Arrays.deepToString(infos));
}
}

11、LinkedList的是单向链表还是双向?
双向循环列表,具体实现自行查阅源码。

12、TreeMap是实现原理
采用红黑树实现,具体实现自行查阅源码。

遍历ArrayList时如何正确移除一个元素
1、for循环遍历list

for(int i=0;i<list.size();i++){
if(list.get(i).equals("del"))
list.remove(i);
//i--;
}

这种方式的问题在于,删除某个元素后,list的大小发生了变化,而你的索引也在变化,所以会导致你在遍历的时候漏掉某些元素。比如当你删除第1个元素后,继续根据索引访问第2个元素时,因为删除的关系后面的元素都往前移动了一位,所以实际访问的是第3个元素。因此,这种方式可以用在删除特定的一个元素时使用,但不适合循环删除多个元素时使用。

2、增强for循环

for(String x:list){
if(x.equals("del"))
list.remove(x);
}

这种方式的问题在于,删除元素后继续循环会报错误信息ConcurrentModificationException,因为元素在使用的时候发生了并发的修改,导致异常抛出。但是删除完毕马上使用break跳出,则不会触发报错。

3、iterator遍历

Iterator<String> it = list.iterator();
while(it.hasNext()){
String x = it.next();
if(x.equals("del")){
it.remove();
}
}

这种方式可以正常的循环及删除。但要注意的是,使用iterator的remove方法,如果用list的remove方法同样会报上面提到的ConcurrentModificationException错误。

总结:
  (1)循环删除list中特定一个元素的,可以使用三种方式中的任意一种,但在使用中要注意上面分析的各个问题。
  (2)循环删除list中多个元素的,应该使用迭代器iterator方式。

13、什么是ArrayMap?它和HashMap有什么区别?
ArrayMap是Android SDK中提供的,非Android开发者可以略过。
ArrayMap是用两个数组来模拟map,更少的内存占用空间,更高的效率。

14、HashMap的实现原理

  1. HashMap概述: HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
  2. HashMap的数据结构: 在java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。

当我们往Hashmap中put元素时,首先根据key的hashcode重新计算hash值,根据hash值得到这个元素在数组中的位置(下标),如果该数组在该位置上已经存放了其他元素,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放入链尾.如果数组中该位置没有元素,就直接将该元素放到数组的该位置上.

需要注意Jdk 1.8中对HashMap的实现做了优化,当链表中的节点数据超过八个之后,该链表会转为红黑树来提高查询效率,从原来的O(n)到O(logn)

15、你了解Fail-Fast机制吗?
Fail-Fast即我们常说的快速失败,

Fail-fast和Fail-safe有什么区别
Iterator的fail-fast属性与当前的集合共同起作用,因此它不会受到集合中任何改动的影响。Java.util包中的所有集合类都被设计为fail->fast的,而java.util.concurrent中的集合类都为fail-safe的。当检测到正在遍历的集合的结构被改变时,Fail-fast迭代器抛出ConcurrentModificationException,而fail-safe迭代器从不抛出ConcurrentModificationException。

-------------本文结束感谢您的阅读-------------