阅读了一些关于JAVA面试中关于集合类的题目,发现自己在这方面的了解还相当有限,因此决定花些时间进行学习和提升。
Java的java.util包内藏有一系列重要的集合类。对于集合类,其核心在于理解其内部结构和遍历集合的迭代模式。
我们必须关注Collection接口。作为最基本的集合接口,Collection代表一组Object元素。值得注意的是,不同的Collection允许元素的唯一性和排序性各不相同。Java SDK并不直接提供继承自Collection的类,而是提供了如List和Set等“子接口”。
所有实现Collection接口的类都必须提供两个标准的构造函数。无参数的构造函数用于创建一个空的Collection,而另一个带有Collection参数的构造函数允许用户复制一个Collection,这使得新的Collection与传入的Collection拥有相同的元素。
关于Collection接口的一个重要方法boolean add(Object c),它的返回值表示的是集合内容是否发生变化,而非添加操作是否成功。类似的addAll、remove、removeAll、retainAll方法亦是如此。
谈及集合的遍历,Iterator模式起到关键作用。Collection提供了一个iterator()方法,返回一个Iterator用于遍历集合的所有元素。这一模式使得访问逻辑能够从不同的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。其典型用法如下:
```java
Iterator it = collection.iterator(); // 获取一个迭代器
while(it.hasNext()) {
Object obj = it.next(); // 获取下一个元素
}
```
每一种集合类返回的Iterator具体类型可能不同,但它们都实现了Iterator接口。我们无需关心具体的Iterator类型,只需获得Iterator接口即可。这就是接口的魅力,也是面向对象的优势。为确保遍历过程的顺利进行,需遵循原则:只在一个线程中使用此集合,或在多线程中对遍历代码进行同步。
从Collection接口派生出两个重要的接口:List和Set。
在实现List接口的类中选择一个LinkedList作为例子。LinkedList实现了List接口并允许null元素。LinkedList还提供在列表首部或尾部进行get、remove、insert操作的额外方法。这些特性使得LinkedList可以被用作堆栈、队列或双向队列。值得注意的是,LinkedList没有同步方法,如果多个线程同时访问List,则必须自行实现访问同步。一种解决方案是在创建List时构造一个同步的List,如:`List list = Collections.synchronizedList(new LinkedList(...));`
关于数据结构的时间复杂度与特性
在编程中,数据结构如ArrayList、Vector、Stack、Set、Map和Hashtable等,具有不同的时间复杂度和特性。了解这些特性对于优化代码性能至关重要。
Vector与ArrayList类似,但有一个重要的区别:Vector是同步的。当使用Iterator时,如果在另一个线程中改变了Vector的状态,将会抛出ConcurrentModificationException异常。为了避免这种情况,必须捕获该异常。
Stack是一种特殊的类,继承自Vector并实现了后进先出(LIFO)的堆栈结构。它提供了几个额外的方法,如push和pop来模拟堆栈操作。还有peek方法获取栈顶元素,empty方法检查堆栈是否为空,以及search方法检测元素在堆栈中的位置。创建后,Stack是空的。
Set是一种不包含重复元素的集合。任何两个元素都不能相等(即e1.equals(e2)=false)。Set最多只能有一个null元素。创建Set时需要注意传入的集合参数不能包含重复元素。操作可变对象时需要特别小心,因为对象状态的改变可能导致Set中的元素变得相等,从而引发问题。
Map是一种提供键值对映射的数据结构。它不允许使用重复的键,每个键只能映射一个值。Map提供了三种视图,可以将内容视为键集合、值集合或键值对映射集合。Hashtable是继承Map接口的哈希表实现。任何非空对象都可以作为键或值。添加和获取数据的时间开销为常数。Hashtable通过初始容量和负载因子来调整性能。在实际使用中需要根据需求和性能要求来调整这两个参数。使用Hashtable的示例代码清晰地展示了其使用方法。需要注意的是,作为键的对象必须实现hashCode和equals方法以确保正确的映射操作。当我们深入探讨Java中的类如HashMap、Hashtable和WeakHashMap时,需要注意一些重要的细节和原则。特别是在自定义类作为哈希表的key时,我们需要深入理解hashCode和equals方法的重要性。
Hashtable是一个同步的类,而HashMap则与之不同,它是非同步的。这意味着在多线程环境下,使用Hashtable需要额外的同步开销,而HashMap则可能具有更高的效率。HashMap允许null值(包括null key),而Hashtable则不允许。当将HashMap视为Collection时(通过values()方法返回Collection),其迭代操作的时间开销与HashMap的容量成比例。如果迭代操作的性能至关重要,应该谨慎设置HashMap的初始容量和负载因子。
接下来是WeakHashMap类。这是一个改进的HashMap,它对key实行“弱引用”。这意味着如果一个key不再被外部引用,那么该key可以被垃圾回收器(GC)回收。这对于需要动态管理内存的资源密集型应用来说是一个很好的选择。
关于抽象编程和接口的使用也非常重要。尽量返回接口而非实际的类型,例如返回List而非ArrayList。这样做的好处是,如果以后需要将ArrayList换成LinkedList或其他实现List的类,客户端代码无需改变。这体现了良好的编程习惯和面向接口的编程思想。
以上内容源自51CTO博客的深入解析和总结。
文章来自《钓虾网小编|www.jnqjk.cn》整理于网络,文章内容不代表本站立场,转载请注明出处。