首先需要清楚JDK类库给我们提供了Java集合,所有的Java集合都位于java.unti包中。与Java数组不同,Java集合不能存放基本类型数据,而只能存放对象的引用。
一、了解集合框架
1.1集合概念
集合有时称为容器,是一个包含很多元素的对象。集合可用来存储,检索和操作数据。
1.2集合框架
集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。所有的集合框架包含:接口、实现以及算法。
二、接口与实现
集合接口封装了不同的,collection接口是集合框架的基础,如图:
Set是一种特殊的Collection,SortedSet又是一种特殊的Set,Map不属于Collection。所有的Collection接口都是支持泛型的。
public interface Collection<T>,但是当要定义一个Collection实例的时候,需引入指定在该集合中包含的对象的类型。这可让编译器在编译时明确放入该集合的元素是那种类型的,减少了运行时错误的发生。
在理解了怎样使用这些接口后,也就理解了Java集合的框架,会根据实际情况选择哪一种集合接口。
下面简单介绍一下集合接口。
Collection接口是一组允许重复的对象。
Set接口继承Collection,但不允许重复,使用自己内部的一个排列机制。
List接口继承Collection,允许重复,以元素安插的次序来放置元素,不会重新排列。
Map接口是一组成对的键—值对象,即所持有的是key_value pairs.Map中不能有重复的Key。
三,接口中的方法
3.1 Collection接口
Collection接口用于表示任何对象或元素组。想要尽可能以常规方式处理一组元素时,就使用此接口,可以把集合转换成任何其他的对象组。但是,不能直接把集合转换成基本数据类型的数组,因为集合必须持有对象。
1.单元素的添加、删除操作
boolean add(Object o):将对象添加给集合;
boolean remove(Object o):如果集合中有与o相匹配的对象,则删除对象o;
2.查询操作
int size():返回当前集合中元素的个数
boolean isEmpty();
boolean contains(Object o):查找集合中是否有对象o
Iterator iterator():返回一个迭代器,用来访问集合中各个元素
3.组操作
boolean containsAll(Collection c):查找集合中是否含有集合c中的所有元素
boolean addAll(Collection c):将集合c中所有元素添加到该集合
void RemoveAll(Collection c):删除
void retainAll(Collection c):从集合中删除集合c不包含的元素
4.遍历集合
两种方式:
(1)通过for_each结构
for(Object o:collection){
System.out.println(o);
}
(2)使用迭代器
迭代器是一种能遍历集合并且能从集合删除元素的对象。可以通过调用Collection接口中的Iterator方法创建该对象。
下面是Iterator接口的定义:
public interface Iterator<T>{
boolean hasNext();
T next();
void remove();
}
如何使用迭代器:
Iterator<T> it = list.iterator();
while(it.hasNext()){
T data = it.next();
System.out.print(data + " ");
}
Collection转换为Object数组
Object[] toArray():返回一个内含集合所有元素的array
Object[]toArray(Object[] a)返回一个内含集合所有元素的array,运行期返回的array和参数a的类型相同,需要转化为正确的类型。
3.2 List接口
List接口继承了Collection接口,可以定义一个有重复项的有序集合。
1.面向位置的操作
包含Collection的功能,还包括获取、除去或更改元素的功能。在List中搜索元素可以从列表的头部或者尾部开始,如果找到,返回元素的位置。
void add(int index,Object elenemt)
boolean addAll(int index,Collection c);
Object get(int index)返回List中指定位置的元素
int indexOf(Object o)返回第一个出现o的位置
2.处理集合的子集
ListIterator listIterator():返回一个列表迭代器,用来访问列表中的元素
ArrayList和LinkedList
Java平台包含两种常用List实现:ArrayList和LinkedList。使用两种List的哪一种取决于特定的需要。如果要支持随机访问,而不必在尾部任何位置插入或者去除元素,那么,ArrayList提供了可选的集合。但如果要频繁的从列表的中间位置添加和去除元素,而只要顺序地访问列表中的元素,那么LinkedList实现更好。
LinkedList的方法:
getFirst()
getLast()
获取元素,但不删除,操作失败,抛出异常(NoSuchElementException);
peekFirst()
peekLast()
获取元素,但不删除,失败返回null;
add:队尾入 remove:对头出 element:查看对头元素 失败抛异常 NoSuchElementException
offer:队尾入 poll 对头出 peek 失败返回null
练习代码:
public static void testArrayList(){
//需要一个数组序列容器,=》ArrayList
ArrayList<Integer> list1 = new ArrayList<Integer>();
for(int i=0; i<10; ++i){
list1.add((int)(Math.random()*100));
}
printList(list1);
list1.add(0, 100);
printList(list1);
//list1.add(100, 200);
int index = 0;
if((index = list1.indexOf(100)) != -1){
list1.remove(index);
}
printList(list1);
list1.set(0, -2);
printList(list1);
Object[] array1 = list1.toArray();
System.out.println(Arrays.toString(array1));
Integer[] array2 = new Integer[1];//将容器元素返回到数组中
array2 = list1.toArray(array2); //此处可以推到数组元素的类型的
System.out.println(Arrays.toString(array2));
List<Integer> list2 = Arrays.asList(array2);
for(int val : list2){
System.out.print(val + " ");
}
System.out.println();
System.out.println(list1.retainAll(list2));
System.out.println(list1.addAll(list2));
System.out.println(list1.containsAll(list2));
}
练习代码:
public static void testLinkedList(){
LinkedList<Integer> list1 = new LinkedList<Integer>();
for(int i=0; i<10; ++i){
list1.add((int)(Math.random()*100));
}
printList(list1);
list1.addFirst(10);
printList(list1);
list1.addLast(20);
printList(list1);
list1.add(30);
printList(list1);
list1.remove();
printList(list1);
list1.offer(40);
printList(list1);
list1.poll();
printList(list1);
System.out.println(list1.peek());
list1.clear();
System.out.println(list1.peek());
//对于所有的Java集合容器,都支持普通的额向后遍历的迭代器
//但对于LinkedList,还提供的双向迭代器
ListIterator<Integer> list = list1.listIterator();
System.out.println(list.hasPrevious());
}
**重点内容**ArrayList和LinkedList的区别?
(1)都是Llist接口实现的,但是LinkedList还实现了Deque;
(2)但是ArrayList底层是个数组,LinkedList底层是个双向循环链表;
(3)LinkedList提供了双向迭代器,ArrayList只有普通的向后遍历的迭代器;
(4)LinkedList便于删除插入,ArrayList便于查找;
(5)需要链表、栈、队列时最好实现LinkedList;
3.3 Queue接口
Queue接口除了可以使用Collection的方法,还提供了加入(offer())、删除(poll())等操作;
需要注意的是:Queue使用时要尽量避免Collection的add()和remove()方法,而是要使用offer()来加入元素,使用poll()来获并移除元素。他们的优点是通过返回值可以判断成功与否;
练习:
创建ArrayList对象,加入10000个随机整数,整数的范围是0-20000
请在最短时间内打印出最大的前10个元素!(PriorityQueue()小跟堆)
请在最短时间内打印出最小的前10个元素!(PriorityQueue()大根堆来实现)
代码如下:
class TestList{
public static void testPriorityQueue(){ //最小前10个元素(利用大根堆,将要插入的元素与堆根元素比较,堆根元素大于要插入的元素,这移出堆根元素,将新元素插入)
PriorityQueue<Integer> queue= new PriorityQueue<Integer>(10,new Comparator<Integer>(){//利用构造函数变成大根堆
@Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
int res=o1-o2;
return res>0?-1:(res==0?0:1);
}
});
ArrayList<Integer> list=new ArrayList<Integer>();
for(int i=0;i<10000;i++){
list.add((int)(Math.random()*20000));
}
int size=10;
for(int i=0;i<size;i++){
queue.add(list.get(i));
}
for(int i=size;i<10000;i++){
int value=list.get(i);
int data=queue.peek(); //返回头部元素
if(data>value){
queue.remove(data);//删除
queue.add(value);
}
}
while(!queue.isEmpty()){
int top = queue.peek();
System.out.print(top + " ");
queue.poll(); //出队
}
System.out.println();
}
public static void testPriorityQueue1(){ //小根堆
ArrayList<Integer> list=new ArrayList<Integer>();
PriorityQueue<Integer> queue=new PriorityQueue<Integer>();
for(int i=0;i<10000;i++){
list.add((int)(Math.random()*20000));
}
int size=10;
for(int i=0;i<size;i++){
queue.add(list.get(i));
}
for(int i=size;i<10000;i++){
int value=list.get(i);
int data=queue.peek(); //peek返回头部元素
if(data<value){
queue.remove(data);
queue.add(value);
}
}
while(!queue.isEmpty()){
int top = queue.peek();
System.out.print(top + " ");
queue.poll();
}
System.out.println();
}
}
public class TestCollectionDemo1 {
@Test
public void testPriorityQueue(){
System.out.println("最小前10个元素 ");
TestList.testPriorityQueue();
}
@Test
public void testPriorityQueue1(){
System.out.println("最大前10个元素");
TestList.testPriorityQueue1();
}
}
运行结果: