二、容器

☞ java容器都有哪些?

常用容器:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-12Qc8R6r-1571392817152)(https://note.youdao.com/yws/res/7645/8233303C35754917A1E197FBAF8EF788)]

☞ Collection和Collections有什么区别?

  • java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。
  • Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。
    Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。

☞ List、Set、Map之间的区别是什么?

List Set Map
继承接口 Collection Collection
实现类 AbstractList(ArrayList,Vector,LinkedList) AbstractSet(HashSet,LinkedHashSet,TreeSet) HashMap,HashTable
常用方法 add(),remove(),clear(),get(),contains(),size() add(),remove(),clear(),contains(),size() get(),put(),remove(),clear(),containsKey(),cotainsValue(),size(),keySet(),values()
是否有序 有序 无序(实际上由hashCode决定)
元素重复性 可重复 不可重复 不可重复
线程安全 Vector线程安全 HashTable线程安全

☞ HashMap和Hashtable有什么区别?

  • HashMap没有HashTable的contains()方法,有containsKey()和containsValue()方法
  • HashMap线程不安全,HashTable线程安全;HashMap效率比HashTable高
  • HashMap允许键值为空;HashTable不允许
  • HashTable的线程安全是通过synchronized实现的

多线程下使用HashMap,如何保证线程安全?

使用Collections.synchronizedMap(m)获取一个被装饰过的map,该map的所有方法被重写,均加了synchronized修饰,源码示例如下

private static class SynchronizedMap<K, V> implements Map<K, V>, Serializable {
	private static final long serialVersionUID = 1978198479659022715L;

	private final Map<K, V> m; // Backing Map
	final Object mutex; // Object on which to synchronize
	SynchronizedMap(Map<K, V> m) {
		this.m = Objects.requireNonNull(m);
		mutex = this;
	}
	SynchronizedMap(Map<K, V> m, Object mutex) {
		this.m = m;
		this.mutex = mutex;
	}
	public int size() {
		synchronized (mutex) {
			return m.size();
		}
	}
	public boolean isEmpty() {
		synchronized (mutex) {
			return m.isEmpty();
		}
	}
	...
}

SynchronizedMap是Collections的内部类,实现了Map接口,故此处使用的装饰者模式(即静态代理)

☞ 如何决定使用HashMap还是TreeMap?

如果只是对元素进行插入,删除,定位元素这类操作,HashMap是最好的选择;如果需要给元素排序,就是用TreeMap

☞ 说一下HashMap的实现原理?

Java中数组查询快增删慢,链表增删快但是查询慢;hashmap结合数组和链表的优点,使得查询和增删都非常快,时间复杂度平均为O(1);hashmap底层是一个可以扩展的entry数组,用于存储键值对;在添加元素时,hashmap会重新计算key的hash值,从而得到元素在entry数组中的下标;如果数组在该位置已经存在元素了,则该位置的元素以链表的形式存在,新增加的元素放置在链表的首部;在jdk1.8中,如果链表中的节点超过8个后,链表会转化为红黑树,以提高查询效率,时间复杂度平均由O(n)降低至O(log(n))

☞ 说一下HashSet的实现原理?

  • HashSet底层由HashMap实现
  • HashSet的值存放于HashMap的key上
  • HashSet无序,允许null值存入,非线程安全

☞ ArrayList和LinkedList的区别是什么?

  1. 两者都是线程不安全的容器
  2. ArrayList底层使用数组,LinkedList底层是双向循环链表
//存储ArrayList元素的数组缓冲区
private transient Object[] elementData;
private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}
  1. ArrayList支持随机访问,linkedList不支持随机访问
  2. 使用下标访问元素,ArrayList时间复杂度是O(1),LinkedList是O(n)
  3. 添加或删除元素时,ArrayList时间复杂度为O(n),LinkedList是O(1)
public boolean add(E e) {
    linkLast(e);
    return true;
}
void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

☞ 如何实现数组和List之间的转换?

数组转换成List(转换之后只能进行访问不能进行删除或者添加元素)(转换之后为什么不增删元素?

arr.asList();

List转换成数组

list.toArray();

☞ ArrayList和Vector的区别是什么?

ArrayList和Vector都是基于数组的实现,ArrayList非线程安全,Vector线程安全

☞ Array和ArrayList有何区别?

  1. Array可以容纳基本类型和对象,ArrayList只能容纳对象
  2. Array容量固定,ArrayList容量可以变化

☞ 在Queue中poll()和remove()有什么区别?

poll()和remove()都是取出并删除队首的元素,区别是队列为空时,poll()返回null,而remove抛出异常

☞ 哪些集合类是线程安全的?

  • vector,向量:线程安全的数组
  • Stack,栈:一个线程安全的容器,继承了vector,线程安全,特点是先进后出
  • HashTable:一个线程安全的map

☞ 迭代器Iterator?

  1. 迭代器Iterator是一种设计模式
  2. 迭代器Iterator用于遍历容器中的元素
  3. 迭代器Iterator本身是一个接口,提供了三个方法:
boolean hasNext();
E next();
void remove();

但是具体的需要容器去实现,如hashmap:

private abstract class HashIterator<E> implements Iterator<E> {
    Entry<K,V> next;        // next entry to return
    int expectedModCount;   // For fast-fail
    int index;              // current slot
    Entry<K,V> current;     // current entry

    HashIterator() {
        expectedModCount = modCount;
        if (size > 0) { // advance to first entry
            Entry[] t = table;
            while (index < t.length && (next = t[index++]) == null)
                ;
        }
    }

    public final boolean hasNext() {
        return next != null;
    }

    final Entry<K,V> nextEntry() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        Entry<K,V> e = next;
        if (e == null)
            throw new NoSuchElementException();

        if ((next = e.next) == null) {
            Entry[] t = table;
            while (index < t.length && (next = t[index++]) == null)
                ;
        }
        current = e;
        return e;
    }

    public void remove() {
        if (current == null)
            throw new IllegalStateException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        Object k = current.key;
        current = null;
        HashMap.this.removeEntryForKey(k);
        expectedModCount = modCount;
    }
}

☞ Iterator怎么使用?有什么特点?

Iterator本身提供了三个核心方法:

//判断是否存在下一个元素
boolean hasNext();
//取出下一个元素
E next();
//移除当前元素
void remove();

特点:单向移动,功能简单

☞ Iterator和ListIterator有什么区别?

  • Iterator可用来遍历Set和List集合,ListIterator只能用来遍历List
  • Iterator可以前向遍历,ListIterator既可以前向遍历也可以后向遍历
  • ListIterator继承于Iterator

☞ 怎么确保一个集合不能被修改?

Collections包下的unmodifiableMap方法,装饰传入的map,当调用任何修改方法时,会抛出UnsupportedOperationException异常

Map<String, Object> map = new HashMap<>();
map.put("name", "lizza");
map.put("age", 18);
System.out.println(map);
map = Collections.unmodifiableMap(map);
map.put("name", "john");
System.out.println(map);

参考资料

【1】Java最常见的208道面试题及答案
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章