1、关于集合的两道面试题:
先来看几道题目:
1、创建一个不可变的的集合:
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
set.add("Java");
set.add("JEE");
set.add("Spring");
set.add("Hibernate");
set = Collections.unmodifiableSet(set);
set.add("Ajax"); // not allowed.
}
可以看到,创建不可变集合主要是调用了Collections的unmodifiableSet()方法,而Collections类通过装饰模式实现了对一般集合的封装。
2、去除List集合中的重复元素,且保持原有的顺序
public static void main(String args[]) {
List<String> list=new ArrayList();
list.add("A");
list.add("B");
list.add("C");
list.add("A");
// List中允许元素重复
for(int i=0;i<list.size();i++)
System.out.print(" "+list.get(i));// A B C A
// 去除重复的元素且保持原有元素的顺序
List list2=new testD().function(list);
for(int i=0;i<list2.size();i++)
System.out.print(" "+list2.get(i));// A B C
}
public <e> List<e> function (List<e> list) {
return new ArrayList<e>(new LinkedHashSet<e>(list));
}
上面的代码代码通过把原有列表传入一个LinkedHashSet来去除重复的元素。在这个情况里,LinkedHashSet可以保持元素原来的顺序。如果这个顺序是不需要的话,那么上面的LinkedHashSet可以用HashSet来替换。
2、集合框架类图及集合对比:
其实如上的两道题目并不难,只是没有对集合了解的更深。下面就来大概的看一下集合吧。集合的继承类如下所示。
大概能够分为三种不同的集合,List、Set和Map,还有一些辅助的工具类,像Collections、Arrays等,各个集合的比较情况如下图。
其实对于集合的操作,无外乎就是
增加:向一个集合中添加一个元素、将一个集合中的所有元素插入到当前集合中
删除:删除指定的一个元素、删除集合中所有的元素
修改:修改指定的一个元素
查找:查当前集合的大小、查找两个集合中的共同元素等
其它:如将一个集合中的元素转换为数组表示形式
3、Iterable、Iterator、Collection接口:
从Java集合框架图可以看出,所有的类都直接或间接继承了Iterable接口,源代码如下:
// Implementing this interface allows an object to be the target of the "foreach" statement.
public interface Iterable<T> {
Iterator<T> iterator();
}
接口中定义了一个迭代器,可以使用增强的for循环(foreach)对集合进行遍历。Iterator类的源代码如下:
/**
* Iterator takes the place of Enumeration. Iterators differ from enumerations in two ways:
*
* <li> Iterators allow the caller to remove elements from the
* underlying collection during the iteration with well-defined
* semantics.
* <li> Method names have been improved.
*/
public interface Iterator<E> {
boolean hasNext();
E next();
/**
* Removes from the underlying collection the last element returned
* by this iterator (optional operation).
*/
void remove();
}
继续看Collection接口,源代码如下:
public interface Collection<E> extends Iterable<E> {
// 查找操作
int size();
boolean isEmpty();
boolean contains(Object o);
boolean containsAll(Collection<?> c);
// 增加操作
boolean add(E e);
boolean addAll(Collection<? extends E> c);
// 删除操作
boolean remove(Object o);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
void clear();
// 集合转换
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
// 提供equals()和hashCode()
boolean equals(Object o);
int hashCode();
}
这个接口是专门针对集合定义了各种操作,他定义了常用集合的一般操作,但是由于抽象层次较高,所以一般一个具体的集合实现类,如ArrayList、HashMap等都不会直接继承这个接口,而是继承这个接口的一些子类来实现。所以说每个集合的具体实现类都直接或间接继承了这个接口。4、List所依赖的接口及抽象类:
查找集合框架图后可知,具体的List实现类继承了AbstractCollection抽象类并且实现了List接口。List接口中又定义了哪些操作呢?
public interface List<E> extends Collection<E> {
// Query Operations
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
// Modification Operations
boolean add(E e);
boolean remove(Object o);
// Bulk Modification Operations
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean addAll(int index, Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
void clear();
// Comparison and hashing
boolean equals(Object o);
int hashCode();
// Positional Access Operations
// Returns the element at the specified position in this list.
E get(int index);
E set(int index, E element);// 修改指定位置的元素
void add(int index, E element);
E remove(int index);
// Search Operations
int indexOf(Object o);
int lastIndexOf(Object o);
// List Iterators
ListIterator<E> listIterator();
ListIterator<E> listIterator(int index);
// View
List<E> subList(int fromIndex, int toIndex);
}
如上的一些方法都在Collection中定义过的,这里完全可以省略,只是为了明了,同时这个接口中还定义了一些更为具体的方法,这些方法是针对List类型的集合而设定的。这样,Map集合就可以只继承Collection,而不用实现List集合中的方法。所以说,如果在实际应用中,只针对List集合,则完全可以使用List接口,因为这个细化的接口中可以看到许多针对List而定义的方法。如果不确定集合类型,或者是需要List集合与Map集合互操作,那就需要使用Collection接口了。
对于List遍历操作又定义出了更加细化的一个迭代接口,这个接口继承了Iterator接口,如下:
public interface ListIterator<E> extends Iterator<E> {
// Query Operations
boolean hasNext();
E next();
boolean hasPrevious();
E previous();
int nextIndex();
int previousIndex();
void remove();
void set(E e);
void add(E e);
}
这个接口中增加了不少针对List集合的遍历查找方法。在List接口中使用ListIterator,类似与AbstractCollection使用Iterator一样。
/**
* This class provides a skeletal implementation of the <tt>Collection</tt>
* interface, to minimize the effort required to implement this interface. <p>
*/
public abstract class AbstractCollection<E> implements Collection<E> {
// protected类型的,以便子类能够顺利调用
protected AbstractCollection() { }
public abstract Iterator<E> iterator();// 定义一个iterator的抽象方法
public abstract int size();
public boolean isEmpty() {
return size() == 0;
}
public boolean contains(Object o) {
Iterator<E> it = iterator();
if (o==null) {
while (it.hasNext())
if (it.next()==null)
return true;
} else {
while (it.hasNext())
if (o.equals(it.next()))
return true;
}
return false;
}
public boolean remove(Object o) {
Iterator<E> it = iterator();
if (o==null) {
while (it.hasNext()) {
if (it.next()==null) {
it.remove();
return true;
}
}
} else {
while (it.hasNext()) {
if (o.equals(it.next())) {
it.remove();
return true;
}
}
}
return false;
}
public Object[] toArray() {
// Estimate size of array; be prepared to see more or fewer elements
Object[] r = new Object[size()];
Iterator<E> it = iterator();
for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) // fewer elements than expected
return Arrays.copyOf(r, i);
r[i] = it.next();
}
return it.hasNext() ? finishToArray(r, it) : r;
}
public <T> T[] toArray(T[] a) {
// Estimate size of array; be prepared to see more or fewer elements
int size = size();
T[] r = a.length >= size ? a :
(T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
Iterator<E> it = iterator();
for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) { // fewer elements than expected
if (a != r)
return Arrays.copyOf(r, i);
r[i] = null; // null-terminate
return r;
}
r[i] = (T)it.next();
}
return it.hasNext() ? finishToArray(r, it) : r;
}
// 定义分配数组最大的栈尺寸,防止溢出
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* Reallocates the array being used within toArray when the iterator
* returned more elements than expected, and finishes filling it from
* the iterator.
*/
private static <T> T[] finishToArray(T[] r, Iterator<?> it) {
int i = r.length;
while (it.hasNext()) {
int cap = r.length;
if (i == cap) {
int newCap = cap + (cap >> 1) + 1;
// overflow-conscious code
if (newCap - MAX_ARRAY_SIZE > 0)
newCap = hugeCapacity(cap + 1);
r = Arrays.copyOf(r, newCap);
}
r[i++] = (T)it.next();
}
// trim if overallocated
return (i == r.length) ? r : Arrays.copyOf(r, i);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError
("Required array size too large");
return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
// Modification Operations
// 省略 ...
// String conversion
/**
* Returns a string representation of this collection. The string
* representation consists of a list of the collection's elements in the
* order they are returned by its iterator, enclosed in square brackets
* ("[]"). Adjacent elements are separated by the characters * ", " (comma and space).
*/
public String toString() {
Iterator<E> it = iterator();
if (! it.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
if (! it.hasNext())
return sb.append(']').toString();
sb.append(',').append(' ');
}
}
}
从如上的两个方法contains()和remove()方法可以看出,其具体的子类如果继承了这两个方法,那么集合中就可以添加null元素,例如:ArrayList aList=new ArrayList();
aList.add("a");
aList.add("b");
aList.add("d");
aList.add(null);// ArrayList中允许有null值
System.out.println(aList.size());//4
Iterator it=aList.listIterator();
while(it.hasNext()){
System.out.println(it.next());// a b d null
}
可以看到,在ArrayList集合中完全将null看作一种集合元素进行处理的,但是如果集合中只有一个null元素的时候,调用具体实现类的isEmpty()方法后返回值将为false。由于Set集合也继承了如上的抽象方法,所以这个性质与List类似。5、Set所依赖的接口及抽象类:
Set接口继承了Collection,其中也定义了一些针对Set集合定义的一些方法,可以看到,与List接口中定义的方法一样。
public interface Set<E> extends Collection<E> {
// Query Operations
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
// Modification Operations
boolean add(E e);
boolean remove(Object o);
// Bulk Operations
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean retainAll(Collection<?> c);
boolean removeAll(Collection<?> c);
void clear();
// Comparison and hashing
boolean equals(Object o);
int hashCode();
}
如上接口中定义的方法都一样,只是实现不一样。但是这样分开为List和Set等集合定义接口的好处就是,如果针对某一个具体集合的操作有一个特殊的操作,那么只在这个接口中定义即可,其他集合不会受到任何影响。6、Map所依赖的接口及抽象类: