java在list中查指定的元素并返回下标_Java程序员必须要掌
握的13个集合类操作优化...
本⽂⾸先针对 Java 集合接⼝进⾏了⼀些介绍,并对这些接⼝的实现类进⾏详细描述,包括 LinkedList、
ArrayList、Vector、Stack、Hashtable、HashMap、WeakHashMap 等,然后对⼀些实现类的实现⽅式和使⽤经验进⾏讲解,同时重点介绍 WeakHashMap。希望通过本⽂介绍,可以让读者对集合的操作⽅式、注意事项等有⼀些了解。
在实际的项⽬开发中会有很多的对象,如何⾼效、⽅便地管理对象,成为影响程序性能与可维护性的重要环节。Java 提供了集合框架来解决此类问题,线性表、链表、哈希表等是常⽤的数据结构,在进⾏ Java 开发时,JDK 已经为我们提供了⼀系列相应的类来实现基本的数据结构,所有类都在 java.util 这个包⾥,清单 1 描述了集合类的关系。
⼀、集合类之间关系
Collection├List│├LinkedList│├ArrayList│└Vector│ └Stack└SetMap├Hashtable├HashMap└WeakHashMap
本⽂讲的就是集合框架的使⽤经验总结,注意,本⽂所有代码基于 JDK7。
Ⅰ.集合接⼝
Collection 接⼝
Collection 是最基本的集合接⼝,⼀个 Collection 代表⼀组 Object,即 Collection 的元素(Elements)。⼀些 Collection 允许相同的元素、⽀持对元素进⾏排序,另⼀些则不⾏。JDK 不提供直接继承⾃ Collection 的类,JDK 提供的类都是继承⾃ Collection 的⼦接⼝,如List 和 Set。所有实现 Collection 接⼝的类都必须提供两个标准的构造函数,⽆参数的构造函数⽤于创建⼀个空的 Collection,有⼀个Collection 参数的构造函数⽤于创建⼀个新的 Collection,这个新的 Collection 与传⼊的 Collection 有相同的元素,后⼀个构造函数允许⽤户复制⼀个 Collection。
如何遍历 Collection 中的每⼀个元素?
不论 Collection 的实际类型如何,它都⽀持⼀个 iterator() 的⽅法,该⽅法返回⼀个迭代⼦,使⽤该迭代⼦即可逐⼀访问 Collection 中每⼀个元素。典型的⽤法如下:
Iterator it = collection.iterator();// 获得⼀个迭代⼦while(it.hasNext()){ Object obj = it.next(); // 得到下⼀个元素}
Collection 接⼝派⽣的两个接⼝是 List 和 Set。
Collection 接⼝提供的主要⽅法:
java中index是什么意思1. boolean add(Object o) 添加对象到集合;
2. boolean remove(Object o) 删除指定的对象;
3. int size() 返回当前集合中元素的数量;
4. boolean contains(Object o) 查集合中是否有指定的对象;
5. boolean isEmpty() 判断集合是否为空;
6. Iterator iterator() 返回⼀个迭代器;
7. boolean containsAll(Collection c) 查集合中是否有集合 C 中的元素;
8. boolean addAll(Collection c) 将集合 C 中所有的元素添加给该集合;
9. void clear() 删除集合中所有元素;
10. void removeAll(Collection c) 从集合中删除 C 集合中也有的元素;
11. void retainAll(Collection c) 从集合中删除集合 C 中不包含的元素。
List 接⼝
List 是有序的 Collection,使⽤此接⼝能够精确的控制每个元素插⼊的位置。⽤户能够使⽤索引(元素在 List 中的位置,类似于数组下标)来访问 List 中的元素,这类似于 Java 的数组。和下⽂要提到的 Set 不同,List 允许有相同的元素。
除了具有 Collection 接⼝必备的 iterator() ⽅法外,List 还提供⼀个 listIterator() ⽅法,返回⼀个 ListIterator 接⼝。和标准的 Iterator 接⼝相⽐,ListIterator 多了⼀些 add() 之类的⽅法,允许添加、删除、设定元素、向前或向后遍历等功能。实现 List 接⼝的常⽤类有LinkedList,ArrayList,Vector 和 Stack 等。
List 接⼝提供的主要⽅法:
1. void add(int index,Object element) 在指定位置上添加⼀个对象;
2. boolean addAll(int index,Collection c) 将集合 C 的元素添加到指定的位置;
3. Object get(int index) 返回 List 中指定位置的元素;
4. int indexOf(Object o) 返回第⼀个出现元素 O 的位置;
5. Object removeint(int index) 删除指定位置的元素;
6. Object set(int index,Object element) ⽤元素 element 取代位置 index 上的元素, 返回被取代的元素。
Map 接⼝
Map 没有继承 Collection 接⼝。Map 提供 Key 到 Value 的映射,⼀个 Map 中不能包含相同的 Key,每个 Key 只能映射⼀个 Value。Map 接⼝提供 3 种集合的视图,Map 的内容可以被当作⼀组 Key 集合,⼀组 Value 集合,或者⼀组 Key-Value 映射。
Map 提供的主要⽅法:
1. boolean equals(Object o) ⽐较对象;
2. boolean remove(Object o) 删除⼀个对象;
3. put(Object key,Object value) 添加 key 和 value。
RandomAccess 接⼝
RandomAccess 接⼝是⼀个标志接⼝,本⾝并没有提供任何⽅法,任务凡是通过调⽤ RandomAccess 接⼝的对象都可以认为是⽀持快速随机访问的对象。此接⼝的主要⽬的是标识那些可⽀持快速随机访问的 List 实现。任何⼀个基于数组的 List 实现都实现了RaodomAccess 接⼝,⽽基于链表的实现则都没有。因为只有数组能够进⾏快速的随机访问,⽽对链表的随机访问需要进⾏链表的遍历。因此,此接⼝的好处是,可以在应⽤程序中知道正在处理的 List 对象是否可以进⾏快速随机访问,从⽽针对不同的 List 进⾏不同的操作,以提⾼程序的性能。
Ⅱ.集合类介绍
LinkedList 类
LinkedList 实现了 List 接⼝,允许 Null 元素。此外 LinkedList 提供额外的 Get、Remove、Insert 等⽅法在 LinkedList 的⾸部或尾部操作数据。这些操作使得 LinkedList 可被⽤作堆栈(Stack)、队列(Queue)或双向队列(Deque)。请注意 LinkedList 没有同步⽅法,它不是线程同步的,即如果多个线程同时访问⼀个 List,则必须⾃⼰实现访问同步。⼀种解决⽅法是在创建 List 时构造⼀个同步的 List,⽅法如
List list = Collections.synchronizedList(new LinkedList(...));
ArrayList 类
ArrayList 实现了可变⼤⼩的数组。它允许所有元素,包括 Null。Size、IsEmpty、Get、Set 等⽅法的运⾏时间为常数,但是 Add ⽅法开销为分摊的常数,添加 N 个元素需要 O(N) 的时间,其他的⽅法运⾏时间为线性。
每个 ArrayList 实例都有⼀个容量(Capacity),⽤于存储元素的数组的⼤⼩,这个容量可随着不断添加新元素⽽⾃动增加。当需要插⼊⼤量元素时,在插⼊前可以调⽤ ensureCapacity ⽅法来增加 ArrayList 的容量以提⾼插⼊效率。和 LinkedList ⼀样,ArrayList 也是线程⾮同步的(unsynchronized)。
ArrayList 提供的主要⽅法:
1. Boolean add(Object o) 将指定元素添加到列表的末尾;
2. Boolean add(int index,Object element) 在列表中指定位置加⼊指定元素;
3. Boolean addAll(Collection c) 将指定集合添加到列表末尾;
4. Boolean addAll(int index,Collection c) 在列表中指定位置加⼊指定集合;
5. Boolean clear() 删除列表中所有元素;
6. Boolean clone() 返回该列表实例的⼀个拷贝;
7. Boolean contains(Object o) 判断列表中是否包含元素;
8. Boolean ensureCapacity(int m) 增加列表的容量,如果必须,该列表能够容纳 m 个元素;
9. Object get(int index) 返回列表中指定位置的元素;
10. 10.Int indexOf(Object elem) 在列表中查指定元素的下标;
11. Int size() 返回当前列表的元素个数。
Vector 类
Vector ⾮常类似于 ArrayList,区别是 Vector 是线程同步的。由 Vector 创建的 Iterator,虽然和 ArrayList 创建的 Iterator 是同⼀接⼝,但是,因为 Vector 是同步的,当⼀个 Iterator 被创建⽽且正在被使⽤,另⼀个线程改变了 Vector 的状态(例如,添加或删除了⼀些元素),这时调⽤ Iterator 的⽅法时将抛出 ConcurrentModificationException,因此必须捕获该异常。
Stack 类
Stack 继承⾃ Vector,实现了⼀个后进先出的堆栈。Stack 提供 5 个额外的⽅法使得 Vector 得以被当作堆栈使⽤。除了基本的 Push 和Pop ⽅法,还有 Peek ⽅法得到栈顶的元素,Empty ⽅法测试堆栈是否为空,Search ⽅法检测⼀个元素在堆栈中的位置。注意,Stack 刚创建后是空栈。
Set 类
Set 是⼀种不包含重复的元素的 Collection,即任意的两个元素 e1 和 e2 都有 e1.equals(e2)=false。Set 最多有⼀个 null 元素。很明显,Set 的构造函数有⼀个约束条件,传⼊的 Collection 参数不能包含重复的元素。请注意,必须⼩⼼操作可变对象(Mutable Object),如果⼀个 Set 中的可变元素改变了⾃⾝状态,这可能会导致⼀些问题。
Hashtable 类
Hashtable 继承 Map 接⼝,实现了⼀个基于 Key-Value 映射的哈希表。任何⾮空(non-null)的对象都可作为 Key 或者 Value。添加数据使⽤ Put(Key,Value),取出数据使⽤ Get(Key),这两个基本操作的时间开销为常数。
Hashtable 通过 Initial Capacity 和 Load Factor 两个参数调整性能。通常缺省的 Load Factor 0.75 较好地实现了时间和空间的均衡。增⼤ Load Factor 可以节省空间但相应的查时间将增⼤,会影响像 Get 和 Put 这样的操作。使⽤ Hashtable 的简单⽰例,将 1、2、3这三个数字放到 Hashtable ⾥⾯,他们的 Key 分别是”one”、”two”、”three”,代码如清单 2 所⽰。
⼆、Hashtable ⽰例
Hashtable numbers = new Hashtable();numbers.put(“one”, new Integer(1));numbers.put(“two”, new Integer(2));numbers.put(“three”, new Integer(3));
如果我们需要取出⼀个数,⽐如 2,可以⽤相应的 key 来取出,代码如清单 3 所⽰。
三、从 Hastable 读取数据
Integer n = ((“two”); System.out.println(“two =”+ n);
由于作为 Key 的对象将通过计算其散列函数来确定与之对应的 Value 的位置,因此任何作为 key 的对象都必须实现 HashCode 和Equals ⽅法。HashCode 和 Equals ⽅法继承⾃根类 Object,如果你⽤⾃定义的类当作 Key 的话,要相当⼩⼼,按照散列函数的定义,如果两个对象相同,即 obj1.equals(obj2)=true,则它们的 HashCode 必须相同,但如果两个对象不同,则它们的 HashCode 不⼀定不同,如果两个不同对象的 HashCode 相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增⼤,所以尽量定义好的 HashCode()⽅法,能加快哈希表的操作。
如果相同的对象有不同的 HashCode,对哈希表的操作会出现意想不到的结果(期待的 Get ⽅法返回 Null),要避免这种问题,最好同时复写 Equals ⽅法和 HashCode ⽅法,⽽不要只写其中⼀个。
HashMap 类
HashMap 和 Hashtable 类似,不同之处在于 HashMap 是线程⾮同步的,并且允许 Null,即 Null Value 和 Null Key。但是将HashMap 视为 Collection 时(values() ⽅法可返回 Collection),其迭代⼦操作时间开销和 HashMap 的容量成⽐例。因此,如果迭代操作的性能相当重要的话,不要将 HashMap 的初始化容量设得过⾼,或者 Load Factor 参数设置过低。
WeakHashMap 类
WeakHashMap 是⼀种改进的 HashMap,它对 Key 实⾏“弱引⽤”,如果⼀个 Key 不再被外部所引⽤,那么该 Key 可以被 GC 回收。
Ⅰ.集合类实践
ArrayList、Vector、LinkedList 均来⾃ AbstractList 的实现,⽽ AbstractList 直接实现了 List 接⼝,并扩展⾃
AbstarctCollection。ArrayList 和 Vector 使⽤了数组实现,ArrayList 没有对任何⼀个⽅法提供线程同步,因此不是线程安全
的,Vector 中绝⼤部分⽅法都做了线程同步,是⼀种线程安全的实现。LinkedList 使⽤了循环双向链表数据结构,由⼀系列表项连接⽽成,⼀个表项总是包含 3 个部分,元素内容、前驱表项和后驱表项。
当 ArrayList 对容量的需求超过当前数组的⼤⼩时,需要进⾏扩容。扩容过程中,会进⾏⼤量的数组复制操作,⽽数组复制时,最终将调⽤System.arraycopy() ⽅法。LinkedList 由于使⽤了链表的结构,因此不需要维护容量的⼤⼩,然⽽每次的元素增加都需要新建⼀个 Entry 对象,并进⾏更多的赋值操作,在频繁的系统调⽤下,对性能会产⽣⼀定的影响,在不间断地⽣成新的对象还是占⽤了⼀定的资
源。⽽因为数组的连续性,因此总是在尾端增加元素时,只有在空间不⾜时才产⽣数组扩容和数组复制。
ArrayList 是基于数组实现的,⽽数组是⼀块连续的内存空间,如果在数组的任意位置插⼊元素,必然导致在该位置后的所有元素需要重新排列,因此其效率较差,尽可能将数据插⼊到尾部。LinkedList 不会因为插⼊数据导致性能下降。
ArrayList 的每⼀次有效的元素删除操作后都要进⾏数组的重组,并且删除的元素位置越靠前,数组重组时的开销越⼤,要删除的元素位置越靠后,开销越⼩。LinkedList 要移除中间的数据需要便利完半个 List。
四、ArrayList 和 LinkedList 使⽤代码
import java.util.ArrayList;import java.util.LinkedList;public class ArrayListandLinkedList { public static void main(String[] args){ long start = System.currentTi 五、运⾏输出
639129669690015
HashMap 是将 Key 做 Hash 算法,然后将 Hash 值映射到内存地址,直接取得 Key 所对应的数据。在 HashMap 中,底层数据结构使
⽤的是数组,所谓的内存地址即数组的下标索引。HashMap 的⾼性能需要保证以下⼏点:
Hash 算法必须是⾼效的;
Hash 值到内存地址 (数组索引) 的算法是快速的;
根据内存地址 (数组索引) 可以直接取得对应的值。
HashMap 实际上是⼀个链表的数组。前⾯已经介绍过,基于 HashMap 的链表⽅式实现机制,只要 HashCode() 和 Hash() ⽅法实现得⾜够好,能够尽可能地减少冲突的产⽣,那么对 HashMap 的操作⼏乎等价于对数组的随机访问操作,具有很好的性能。但是,如果HashCode() 或者 Hash() ⽅法实现较差,在⼤量冲突产⽣的情况下,HashMap 事实上就退化为⼏个链表,对 HashMap 的操作等价于
遍历链表,此时性能很差。
HashMap 的⼀个功能缺点是它的⽆序性,被存⼊到 HashMap 中的元素,在遍历 HashMap 时,其输出是⽆序的。如果希望元素保持输
⼊的顺序,可以使⽤ LinkedHashMap 替代。
LinkedHashMap 继承⾃ HashMap,具有⾼效性,同时在 HashMap 的基础上,⼜在内部增加了⼀个链表,⽤以存放元素的顺序。
HashMap 通过 hash 算法可以最快速地进⾏ Put() 和 Get() 操作。TreeMap 则提供了⼀种完全不同的 Map 实现。从功能上
讲,TreeMap 有着⽐ HashMap 更为强⼤的功能,它实现了 SortedMap 接⼝,这意味着它可以对元素进⾏排序。TreeMap 的性能略微
低于 HashMap。如果在开发中需要对元素进⾏排序,那么使⽤ HashMap 便⽆法实现这种功能,使⽤ TreeMap 的迭代输出将会以元素顺序进⾏。LinkedHashMap 是基于元素进⼊集合的顺序或者被访问的先后顺序排序,TreeMap 则是基于元素的固有顺序 (由 Comparator 或者 Comparable 确定)。
LinkedHashMap 是根据元素增加或者访问的先后顺序进⾏排序,⽽ TreeMap 则根据元素的 Key 进⾏排序。
六、所⽰代码演⽰了使⽤ TreeMap 实现业务逻辑的排序(TreeMap 实现排序)。
import java.util.Iterator;import java.util.Map;import java.util.TreeMap;public class Student implements Comparable{ public String name; public int score; pu

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。