Java Collections Framework是Java提供的对集合进行定义,操作,和管理的包含一组接口,类的体系结构。
Java集合框架的基本接口/类层次结构:
java.util.Collection [I]
+--java.util.List [I]
+--java.util.ArrayList [C]
+--java.util.LinkedList [C]
+--java.util.Vector [C]
+--java.util.Stack [C]
+--java.util.Set [I]
+--java.util.HashSet [C]
+--java.util.SortedSet [I]
+--java.util.TreeSet [C]
java.util.Map [I]
+--java.util.SortedMap [I]
+--java.util.TreeMap [C]
+--java.util.Hashtable [C]
+--java.util.HashMap [C]
+--java.util.LinkedHashMap [C]
+--java.util.WeakHashMap [C]
其中I表示接口,C表示实现类
1 List和Set
在实际开发中,需要将使用的对象存储于特定数据结构的容器中。JDK提供了这样的容器---集合(Collection)。Collection是一个接口,定义了集合相关的操作方法,其有两个子接口:List与Set
List:可重复集合
Set:不可重复的集合
其中判断元素是否重复,取决于元素的equals()比较的结果
2 集合持有对象的引用
集合中存储的都是引用类型元素,并且集合只保存每个元素对象的引用,而并非将元素对象本身存入集合。
3 集合中常用的方法:
3.1 add方法
Collection定义了一个add方法用于向集合中添加新元素。
boolean add(E e)
该方法返会将给定的元素添加进集合,若添加成功,则返回true,否则返回false。
实例1
import java.util.ArrayList;
import java.util.Collection;
public class Test00{
public static void main(String[] args) {
Collection<String> c = new ArrayList<String>();
System.out.println(c);
c.add("a");
c.add("b");
c.add("c");
System.out.println(c);
}
}
3.2 contains方法
boolean contains(Object o)
该方法用于判定给定的元素是否被包含在集合中。若包含则返回true,反则返回false。
注意:集合在判断元素是否被包含在集合中是根据每个元素的equals方法进行比较的结果。
通常有必要重写equals()保证contains()方法的合理结果
实例2:
import java.util.ArrayList;
import java.util.Collection;
public class Test00{
public static void main(String[] args) {
Collection<String> c = new ArrayList<String>();
System.out.println(c);
c.add("a");
c.add("b");
c.add("c");
System.out.println(c);
Collection<String> b = new ArrayList<String>();
b.add("b");
b.add("c");
System.out.println(c.contains(b));
System.out.println(c.contains("b"));
c.addAll(b);
System.out.println(c);
System.out.println(c.contains(b));
System.out.println(c.contains("h"));
}
}
3.3 size clear isEmpty
int size()
该方法用于返回当前集合中的元素总数
void clear()
该方法用于清空当前集合。
boolean isEmpty()
该方法用于判定当前集合中是否不包含任何元素
实例3 :
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
public class Test00{
public static void main(String[] args) {
Collection<String> c = new HashSet<String>();
System.out.println(c.isEmpty());
c.add("java");
c.add("cpp");
c.add("php");
c.add("c#");
c.add("cpp");
System.out.println("isEmpty : " + c.isEmpty() + ",size : "+ c.size());
c.clear();// 清空该集合
System.out.println("isEmpty : "+ c.isEmpty() + ",size : " + c.size());
}
}
addAll containsAll
boolean addAll(Collection <? extends E> c)
该方法需要我们传入一个集合,并将该集合中的所有元素添加到当前集合中。
boolean containsAll(Collection<?> c)
该方法用于判定当前结合是否包含给定集合中的所有元素,若包含则返回true。
实例4
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
public class Test00{
public static void main(String[] args) {
Collection<String> c1 = new ArrayList<String>();
c1.add("java");
c1.add("cpp");
c1.add("php");
c1.add("c#");
c1.add("objective-c");
c1.add("cppc");
System.out.println(c1);
Collection<String> c2 = new HashSet<String>();
c2.addAll(c1);
System.out.println(c2);
Collection<String> c3 =new ArrayList<String>();
c3.add("java");
c3.add("cpp");
System.out.println(c1.containsAll(c3));
}
}
4 Iterator
Iterator是迭代器,拥有两个方法
4.1 hasNext,next方法
迭代器用于遍历集合元素。获取迭代器可以使用Collection定义的方法:
Iterator iterator()
迭代器Iterator是一个借口,集合在重写Collection的iterator()方法时利用内部类提供了迭代器的实现。
Iterator提供了统一的遍历元素集合的方法,其提供了用于遍历集合的两个方法:
boolean hasNext():判断集合是否还有元素可以遍历
E next():返回迭代的下一个元素
实例4
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class Test00{
public static void main(String[] args) {
Collection<String> c = new HashSet<String>();
c.add("java");
c.add("cpp");
c.add("php");
c.add("c#");
c.add("objective-c");
Iterator<String> iterator = c.iterator();
while(iterator.hasNext()){
String str = iterator.next();
System.out.println(str);
}
}
}
4.2 remove方法
在使用迭代器遍历集合时,不能通过集合的remove方法删除集合元素,否则会抛出并发更改异常。我们可以通过迭代器自身提供的remove()方法来删除通过next()迭代出的元素
void remove()
迭代器的删除方法是在原集合中删除元素。
这里需要注意的是:在调用remove方法前必须通过迭代器的next方法迭代过的元素,那么删除的就是这个元素。并且不能够再次调用remove方法,除非再次调用next()后方可再次调用。
实例5:
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class Test00{
public static void main(String[] args) {
Collection<String> c = new HashSet<String>();
c.add("java");
c.add("cpp");
c.add("php");
c.add("c#");
c.add("objective-c");
Iterator<String> it = c.iterator();
while(it.hasNext()){
String str = it.next();
if(str.indexOf("c")!=-1){
it.remove();// 删除包含字母c的元素
}
}
System.out.println(c);
}
}
4.3 增强型for循环
Java5.0之后推出了一个新的特性,增强for循环,也成为新循环。该循环不通用于传统循环的工作,其只用于遍历集合或数组。
语法:
for(元素类型 e:集合或数组){
循环体
}
实例7
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class Test00{
public static void main(String[] args) {
Collection<String> c = new HashSet<String>();
c.add("java");
c.add("cpp");
c.add("php");
c.add("c#");
c.add("objective-c");
for(String str : c)
{
System.out.print(str.toUpperCase()+" ");
//C# CPP OBJECTIVE-C JAVA PHP
}
}
}
新循环并非新的语法,而是在编译过程中,编译器会将新循环转为迭代器模式。所以新循环本质上是迭代器。
5 泛型机制
泛型是Java SE5.0引入的特性,泛型的本质是参数化类型。在类、接口、和方法的定义过程中,所操作的数据类型被传入的参数指定。
Java泛型机制广泛的应用在集合框架中。所有的集合类型都带有泛型的参数,这样在创建集合时可以指定放入集合中的元素的类型。Java编译器可以据此进行类型检查,这样可以减少代码在运行时出现错误的可能性。
6 List
List接口是Collection的子接口,用于定义线性表数据结构。可以讲List理解为存放对象的数组,只不过其元素可以动态的增加或减少。
List接口的两个常见实现类为ArrayList和LinkList,分别用动态数组和链表的方式实现了List接口。
可以认为ArrayList和LinkedList的方法在逻辑上完全一样,只是在性能上有一定差别。ArrayList更适合于随机访问,LinkList更适合于插入和删除。在性能要求不是特别苛刻的情形下可以忽略这个差别。
6.1 get和set
List除了继承Collection定义方法外,还根据其线性表的数据结构定义了一系列的方法,其中最常用的就是基于下标的get和set方法
E get(int index)
获取集合执行下标对应的元素,下标从0开始。
E set(int index,Element)
将给定的元素存入给定位置,并将原位置的元素返回。
实例8
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
public class Test00{
public static void main(String[] args) {
List<String> c = new ArrayList<String>();
c.add("java");
c.add("cpp");
c.add("php");
c.add("c#");
c.add("objective-c");
for(int i = 0;i<c.size();i++){
System.out.println(c.get(i).toUpperCase());
}
String value = c.set(1, "c++");
System.out.println(value);// cpp
System.out.println(c);// [java, c++, php, c#, objective-c]
c.set(1,c.set(3, c.get(1)));// 交换1和3上的元素位置 因为内层返回的是3位置的元素
System.out.println(c);// [java, c#, php, c++, objective-c]
}
}
6.2 插入和删除
List根据下标的操作还支持插入语删除操作
void add(int index,Element);
将给定的元素插入到指定位置,原位置及后续元素都顺序向后移动。
E remove(int index);
删除给定位置的元素,并将被删除的元素返回。
6.3 List转换为数组
List的toArray方法用于将集合转换为数组。但实际上该方法是在Collection中定义的,所以所有的集合都具备这个功能。
其有两个方法:
Object[] toArray()
<T>T[] toArray(T[] a)
其中第二个方法是比较常用的,我们可以传入一个指定类型的数组,该数组的元素类型与集合的元素类型一致。返回值则是转换后的数组,该数组会保存集合中的所有元素。
6.4 数组转换为List
Arrays类中提供了一个静态方法asList,使用该方法我们可以将一个数组转换为对应的List集合。
其方法定义为:
static <T>List<T> asList<T... a>
返回的List的集合元素类型由传入的数组的元素类型决定。
并且要注意的是,返回的集合我们不能对其增删元素。并且对集合的元素进行修改hi影响数组对应的元素。
6.5 List排序
Collection.sort方法实现排序
Collection是集合的工具类,它提供了很多便于我们操作集合的方法,其中就有用于集合排序的sort方法。该方法定义为:
void sort(List<T> list)
该方法的作用是对给定的集合元素进行自然排序。
6.6 Comparable
Collection的sort方法是对集合元素进行自然排序,那么两个元素对象之间就一定要有大小之分。这个代销之分是如何界定的?
实际上,在使用Collection的sort排序的结合都必须是Comparable接口实现类,该接口表示其子类是可比较的,因为实现该接口必须重写抽象方法:
int compareTo(T t);
该方法返回值要求:
若o1>o2则返回值应>0
若o1<o2则返回值应<0
若o1=o2则返回值因为0
6.7 LinkedList
双向链表
双端效率高
方法
add(数据) 添加数据
get(i) 访问指定位置的收
remove(i) 移除指定位置的数据
size() 元素的数量
addFirst() 在链表首处添加元素
addLast() 在链表末尾添加元素
getFirst() 获得第一个元素
getLast() 获得末尾元素
removeFirst() 删除第一个元素
removeLast() 删除最后一个元素
iterator()
辅助创建迭代器对象进行设置
LinkedList实例:
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
public class Test00{
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
list.add("eee");
list.add("fff");
list.add("ggg");
list.add("hhh");
list.add("iii");
list.add("jjj");
list.add("kkk");
list.add("lll");
System.out.println("获取链表数组的大小: "+list.size());// 获取链表的大小
System.out.println("get方法获取元素 "+list.get(0));
System.out.println("获取第一个元素"+list.getFirst());
System.out.println("获取链表的之后一个元素 "+list.getLast());
LinkedList<String> list1 = new LinkedList<>();
list1.addAll(list);
for(String s1:list1){// 元素遍历
System.out.println(s1);
}
// 通过迭代器来遍历
Iterator<String> iterator = list.iterator();
if(iterator.hasNext()){
String str = iterator.next();
System.out.println(str);
}
System.out.println("删除链表的第一个元素 "+list.removeFirst());
System.out.println("删除链表的最后一个元素: "+list.removeLast());
System.out.println("找到第一个相等的元素进行删除 "+list.remove("ddd"));
System.out.println("指定删除链表中的某个元素:"+list.remove(4));
System.out.println(list.toArray());// 将list链表转为数组
Object[] listarr = list.toArray();// 将list转为数组,并利用数组的方式进行答应
System.out.println(Arrays.toString(listarr));
}
}
6.8 ArrayList和LinkedList的大致区别如下:
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据
示例如下:
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class Test00{
static final int N = 500000;
static long timeList(List list){
long start = System.currentTimeMillis();
Object o = new Object();
for(int i = 0;i<N;i++){
list.add(0,o);
}
return System.currentTimeMillis() - start;
}
static long readList(List list){
long start = System.currentTimeMillis();
for(int i = 0;i<list.size();i++){
}
return System.currentTimeMillis() - start;
}
static List addList(List list){
Object o = new Object();
for(int i = 0;i<N;i++){
list.add(0,o);
}
return list;
}
public static void main(String[] args) {
System.out.println("ArrayList添加"+N+"条耗时:"+timeList(new ArrayList()));
System.out.println("LinkedList添加"+N+"条耗时"+timeList(new LinkedList()));
List list1 = addList(new ArrayList());
List list2 = addList(new LinkedList());
System.out.println("ArrayList查找"+N+"条耗时"+readList(list1));
System.out.println("LinkedList查找"+N+"条记录耗时"+timeList(list2));
/* 结果如下
ArrayList添加500000条耗时:31141
LinkedList添加500000条耗时1
ArrayList查找500000条耗时15
LinkedList查找500000条记录耗时3
*/
}
}
7 队列和栈
7.1 Queue(队列)
队列是常用的数据结构,可以将队列看成特殊的线性表,队列限制了线性表的访问方式:只能从线性表
的一端添加(offer)元素,从另一端取出(poll)元素。
队列遵循先进先出(FIFO First Input First Output)的原则。
JDK中提供了Queue接口,同时使用LinkedList实现了该接口(选择LinkList实现Queue的原因在于
Queue经常要进行添加和删除的操作,而LinkList在这方面效率较高)。
Queue接口中主要方法如下:
boolean off(E e) 将一个元素添加至队尾,如果添加成功则返回true。
E poll() 从队首删除并返回一个元素。
E peek() 返回队首的元素(但并不删除).
7.2 Deque
Deque是Queue的子接口,定义了所谓的"双端队列",即从队列的两端分别可以入队(offer)和出队(poll),LinkList实现了该接口。
如果将Deque限制为只能从一端入队和出队,则可实现"栈"(Stack)数据结构,对栈而言,入栈称之为push,出栈称之为pop。
栈遵循先进后出(FILO First Input Last Output)的原则。
import java.util.LinkedList;
import java.util.Queue;
public class Test00{
public static void main(String[] args) {
Queue<String> queue = new LinkedList<String>();
queue.offer("a");// 将一个对象添加至队尾,如果添加成功则返回true
queue.offer("b");
queue.offer("c");
System.out.println(queue);
String str =queue.peek();
System.out.println(str);
while(queue.size()>0){
// 从队首删除并返回一个元素
str = queue.poll();
System.out.println(str + " ");
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。