您的位置:首页 > 职场人生

黑马程序员——Java之集合框架(一)

2015-03-21 15:03 417 查看
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


一 、集合概述

面向对象语言对对象的体现都是以对象的形式,所以为了对多个对象的操作,就对对象进行存储,集合就是存储对象的最常用的一种方式。

特点:集合的长度是可变的,只能存储对象。

数组和集合都是容器有何不同?

数组虽然也可以存储对象,但长度是固定的,数组中可以存储基本类型的数据数据。

二、 集合体系结构图

 


 


三 、Collection接口

       Collection接口是最基本的集合接口,它不提供直接的实现,JavaSDK提供的类都是继承自Collection的“子接口”如List和Set。Collection所代表的是一种规则,它所包含的

元素都必须遵循一条或者多条规则。如有些允许重复而有些则不能重复、有些必须要按照顺序插入而有些则是散列,有些支持排序但是有些则不支持。

实现其接口的子接口区别:

list元素是有序的,元素可以重复,因为该体系集合有索引。

set元素是无序的,元素不可以重复。

Quenu为队列,阻塞式队列和双端队列。

 

基本操作:

(1) 单元素添加、删除操作:

boolean add(Object o):将对象添加给集合

boolean addAll(Collection<? extendsE> c) 将指定集合中的所有元素都添加到此 集合中

boolean remove(Object o): 如果集合中有与o相匹配的对象,则删除对象o

boolean removeAll(Collection<?> c)移除此集合中那些也包含在指定集合中的所有元素

boolean retainAll(Collection<?> c)从当前集合中删除c集合中不包含的元素(取交集)

void clear()清空集合中的所有元素

(2) 获取操作

int size() :返回当前集合中元素的数量

Iterator iterator() :返回一个迭代器,用来访问集合中的各个元素

(3)判断操作

boolean isEmpty() :判断集合中是否有任何元素

boolean contains(Object o) :判断查找集合中是否含有对象o

boolean containsAll(Collection<?> c) 判断此集合是否包含指定集合中的所有元素,

(4) Collection转换为Object数组 :

Object[] toArray() :返回一个内含集合所有元素的array

<T> T[] toArray(T[] a):返回一个内含集合所有元素的array。运行期返回的array和参数a的型别相同,需要转换为正确型别。

 

注意:可以把集合转换成其它任何其它的对象数组。但是不能直接把集合转换成基本数据类型的数组,因为集合必须持有对象。

1、List 接口

List接口为Collection直接接口。List所代表的是有序的Collection,即它用某种特定的插入顺序来维护元素顺序。用户可以对列表中每个元素的插入位置进行精确地控制,同时可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。实现List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。



List集合判断元素是否相同,依据的是元素的equals方法。

List集合中的contains方法和remove方法都会会调用元素本身的equals方法来进行判断。

特有方法:凡是可以操作角标的方法都是该体系特有的方法。

(1)    添加和删除

void add(int index, Object element): 在指定位置index上添加元素element

boolean addAll(int index, Collection c): 将集合c的所有元素添加到指定位置index

Object remove(int index) :删除指定位置上的元素

(2)获取

Object get(int index): 返回List中指定位置的元素

List<E> subList(int fromIndex,inttoIndex)获取部分对象元素

int indexOf(Object o): 返回第一个出现元素o的位置,否则返回-1

int lastIndexOf(Object o) :返回最后一个出现元素o的位置,否则返回-1

(3)修改

Object set(int index, Object element) :用元素element取代位置index上的元素,并且返回旧的元素

(4)遍历集合

list集合特有的迭代器listiterator是Interator的子接口。

在迭代时,不可以通过集合对象的方法操作集合的元素。因为会发生ConcurrentModificationException并发异常。所以在迭代时,只能使用迭代器的操作元素的方法。可是

interator方法时有限的,只能对元素进行判断,去除删除等操作,不能添加元素。如果想要其他操作如添加、修改等就需要使用其子接口listiterator。该接口只能通过list集合的

listiterator方法获取。

基本操作:

(1) 添加元素

void add(Object o): 将对象o添加到当前位置的前面

(2)修改

void set(Object o): 用对象o替代next或previous方法访问的上一个元素。如果上次调用后列表结构被修改了,那么将抛出IllegalStateException异常。

 (3) 正向迭代

boolean hasNext()以正向遍历列表时,判断后一个元素是否存在。

Object next() 返回列表中的下一个元素。

int nextIndex(): 返回下次调用next方法时将返回的元素的索引

(4)反向迭代

 boolean hasPrevious():以逆向遍历列表时判断前一个元素是否存在。

Object previous():返回前一个对象

nt previousIndex(): 返回下次调用previous方法时将返回的元素的索引7

(1)Arraylist集合

ArrayList是一个动态数组,也是我们最常用的集合。它允许任何符合规则的元素插入甚至包括null。每一个ArrayList都有一个初始容量(10),该容量代表了数组的大小。随着

容器中的元素不断增加,容器的大小也会随着增加。在每次向容器中增加元素的同时都会进行容量检查,当快溢出时,就会进行扩容操作。所以如果我们明确所插入元素的多

少,最好指定一个初始容量值,避免过多的进行扩容操作而浪费时间、效率。

在向一个ArrayList对象添加大量元素的程序中,可使用ensureCapacity方法增加capacity。这可以减少增加重分配的数量。

 (1)void ensureCapacity(int minCapacity): 将ArrayList对象容量增加minCapacity

 (2)void trimToSize(): 整理ArrayList对象容量为列表当前大小。程序可使用这个操作减少ArrayList对象存储空间。

 

ArrayList三种遍历方式写法:

    

public static void method_3(){
ArrayList a= new ArrayList();
a.add( "aaa");
a.add( "bbb");
a.add( "ccc");
a.add( "ddd");
//第一种方式:迭代器
Iterator it=a.iterator();
while(it.hasNext()){//使用完后对象还依然存在;
System.err.println(it.next());
}
//第二种方式:for循环迭代器
for(Iterator it=a.iterator();it.hasNext();){
//使用完后对象不存在,节省内存;
System. err.println(it.next());
}
//第三种方式:for普通循环,get获取
for(int x=0;x<a.size();x++){
System. err.println(a.get(x));
}
}


练习一:去除ArrayList中的重复元素。
public class ArrayListTest {
//去除ArrayList中的重复元素。
public static ArrayList SingElement(ArrayList al){
ArrayList a= new ArrayList();
for(Iterator in=al.iterator();in.hasNext();){

Object ob=in.next();
if(!a.contains(ob)){
a.add(ob);
}
}
return a;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList al= new ArrayList();
al.add( "1");
al.add( "2");
al.add( "3");
al.add( "1");
al.add( "2");
System. err.println(al);
al= SingElement(al);
System. err.println(al);
}
}


练习三;去除ArrayList中的重复对象元素。
public class Person {
private String name;
private int age ;
Person(String name, int age){
this.name =name;
this.age =age;
}
public String getName() {
return name ;
}
public int getAge() {
return age ;
}
public boolean equals(Object obj){//contains会调用元素的equals方法。
if(!(obj instanceof Person))
return false ;
Person p=(Person)obj;
//System.err.println(this.name+p.name);
return this .name .equals(p.name) &&this.age ==p.age ;
}
}

public class ArrayListTest1 {
//判断元素是否相同。
public static ArrayList SingElement(ArrayList al){
//建立一个新容器。
ArrayList all= new ArrayList();
for(Iterator it=al.iterator();it.hasNext();){
Object obj=it.next();
if(!all.contains(obj)){
all.add(obj);
}
}
return all;
}
public static void main(String[] args) {
ArrayList al= new ArrayList();
al.add( new Person("lisi001.." ,30));
al.add( new Person("lisi002.." ,30));
al.add( new Person("lisi003.." ,30));
al.add( new Person("lisi001.." ,30));
al= SingElement(al);
Iterator it=al.iterator();
while(it.hasNext()){
Person p=(Person)it.next();
System.err.println(p.getName()+p.getAge());
}
}
}


(2)Linkedlist集合

同样实现List接口的LinkedList与ArrayList不同,ArrayList是一个动态数组,而LinkedList是一个双向链表。所以它除了有ArrayList的基本操作方法外还额外提供了get,

remove,insert方法在LinkedList的首部或尾部。

由于实现的方式不同,LinkedList不能随机访问,它所有的操作都是要按照双重链表的需要执行。在列表中索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。这

样做的好处就是可以通过较低的代价在List中进行插入和删除操作。

 

特有方法:

LinkedList类添加了一些处理列表两端元素的方法。

(1)添加元素

 void addFirst(Object o): 将对象o添加到列表的开头

void addLast(Object o):将对象o添加到列表的结尾

(2) 获取元素

Object getFirst(): 返回列表开头的元素

Object getLast(): 返回列表结尾的元素

(3) 删除元素

Object removeFirst(): 删除并且返回列表开头的元素

Object removeLast():删除并且返回列表结尾的元素

(4) 构造方法

LinkedList(): 构建一个空的链接列表

LinkedList(Collection c): 构建一个链接列表,并且添加集合c的所有元素

<pre name="code" class="java">//模拟堆栈:先进后出。队列:先进先出;
class MyLinkedList
{
private LinkedList ll;
public MyLinkedList(){
ll=new LinkedList();
}
//添加元素到开头;
public void  myadd(Object obj){
ll.addFirst(obj);
}
//先进先出
public Object fifo(){
return ll.removeLast();
}
//先进后出
public Object fiao(){
return ll.removeLast();
}
//元素是否为空;
public boolean isnull(){
return ll.isEmpty();
}
}
public class Test3
{
public static void main(String[] args){
MyLinkedList mll=new MyLinkedList();
mll.myadd("11");
mll.myadd("22");
mll.myadd("33");
mll.myadd("44");
while(!mll.isnull()){
System.out.println(mll.fifo());
}
}
}




(2) vector枚举

与ArrayList相似,但是Vector是同步的。所以说Vector是线程安全的动态数组。它的操作与ArrayList几乎一样。

枚举是vector的特有的取出方式,枚举和迭代其实一样。因为枚举的名字以及方法名过长而别迭代器替换掉了,

ArrayList和Vector的异同点:



三种遍历方式:   

<pre name="code" class="java">public class Test3
{
public static void main(String[] args){
Vector v=new Vector();
v.add("111");
v.add("222");
v.add("333");
v.add("444");
//第一种方式:随机访问;
for(int x=0;x<v.size();x++){
System.out.println(v.get(x)+"...get...");
}
//第二种:迭代器。
Iterator it=v.iterator();
while(it.hasNext()){
System.out.println(it.next()+"...迭代器(iterator)...");
}
ListIterator li=v.listIterator();
while(li.hasNext()){
System.out.println(li.next()+"...迭代器(listiterator)...");
}
//第三种方式:枚举遍历
Enumeration e=v.elements();
while(e.hasMoreElements()){
System.out.println(e.nextElement()+"...elements...");
}
//第四种方式:for循环
for(Iterator it1=v.iterator();it1.hasNext();){
System.out.println(it1.next()+"...for循环(iterator)...");
}
for(ListIterator li1=v.listIterator();li1.hasNext();){
System.out.println(li1.next()+"...for循环(listiterator)...");
}
}
}



<pre name="code" class="java">运行结果:


<pre name="code" class="java">111...get...
222...get...
333...get...
444...get...
111...迭代器(iterator)...
222...迭代器(iterator)...
333...迭代器(iterator)...
444...迭代器(iterator)...
111...迭代器(listiterator)...
222...迭代器(listiterator)...
333...迭代器(listiterator)...
444...迭代器(listiterator)...
111...elements...
222...elements...
333...elements...
444...elements...
111...for循环(iterator)...
222...for循环(iterator)...
333...for循环(iterator)...
444...for循环(iterator)...
111...for循环(listiterator)...
222...for循环(listiterator)...
333...for循环(listiterator)...



(4)stack

Stack继承自Vector,实现一个后进先出的堆栈。

栈是一种非常常见的数据结构,它采用典型的先进后出的操作方式完成的。每一个栈都包含一个栈顶,每次出栈是将栈顶的数据取出。



Stack提供5个额外的方法使得Vector得以被当作堆栈使用。

         操作                                          说明
empty()

测试堆栈是否为空。
peek()

查看堆栈顶部的对象,但不从堆栈中移除它。
pop()

移除堆栈顶部的对象,并作为此函数的值返回该对象。
push(E item)

把项压入堆栈顶部。
search(Object o)

返回对象在堆栈中的位置,以 1 为基数。

2、Set接口

 Set 接口继承 Collection 接口,而且它不允许集合中存在重复项,每个具体的 Set 实现类依赖添加的对象的 equals()方法来检查独一性。Set接口没有引入新方法,所以Set就

是一个Collection,只不过其行为不同。实现了Set接口的集合有:HashSet、TreeSet、LinkedHashSet、EnumSet。



(1)Hashset集合

HashSet堪称查询速度最快的集合,因为其内部是以HashCode来实现的。它内部元素的顺序是由哈希码来决定的,所以它不保证set 的迭代顺序;特别是它不保证该顺序恒久不变。

hashset底层结构是哈希表,如何保证唯一性?

是通过元素的俩个方法,hashcode和equals来完成。

如果元素的hashcode值相同,才会判断equals是否为ture.

如果元素的hashcode值不相同,则不会调用equals。

注意:对于判断元素是否存在,以及删除等操作,依赖的方法时元素的hashcode和equals方法。

<pre name="code" class="java">class Person
{
private String name;
private int age;
Person(String name,int age){
this.name=name;
this.age=age;
}
public void setName(String name)
{
this.name=name;
}
public String getName(){
return name;
}
public void setAge(int age)
{
this.age=age;
}
public int getAge(){
return age;
}
//重写hashCode方法,可以提高运行效率。
public int hashCode(){
System.out.println(this.name+"...code...");
return name.hashCode()+age*15;
}
//重写equals方法,保证数据唯一性。
public boolean equals(Object obj){
if(!(obj instanceof Person))
return false;
Person p=(Person)obj;
System. err.println(this .name +"重复...判断equals..." +p.name );
return this.name.equals(p.name)&&this.age==age;
}
}
class Test3
{
public static void main(String[] args){
HashSet hs=new HashSet();
hs.add(new Person("zhangsan",30));
hs.add(new Person("lisi",40));
hs.add(new Person("wangwu",50));
hs.add(new Person("zhangsan",30));
hs.remove("lisi");
for(Iterator it=hs.iterator();it.hasNext();){
Person p=(Person)it.next();
System.out.println("姓名:"+p.getName()+" 年龄:"+p.getAge());
}
}
}



运行结果:

zhangsan...code...
lisi...code...
wangwu...code...
zhangsan...code...
zhangsan重复...判断equals...zhangsan
姓名:wangwu 年龄:50
姓名:lisi 年龄:40
姓名:zhangsan 年龄:30


从运行结果上可以看出:
当向Set集合插入数据时候,首先比较hashCode编码,如果集合中已有此hashCode,则进而去比较equals方法,若返回true,则两个数据相同,此时不予插入,若返回false,则可以插入,此时数据插入同一个hashCode的“筒”,若集合中没有此hashCode,则可以直接插入,不需要比较equals方法

(2)Treeset集合

基于TreeMap,生成一个总是处于排序状态的set,内部以TreeMap来实现。底层数据结构是二叉树。它是使用元素的自然顺序对元素进行排序,或者根据创建Set 时提供的 

Comparator 进行排序,具体取决于使用的构造方法。



 Comparable接口
    在“集合框架”中有两种比较接口:Comparable接口和Comparator接口。像String和Integer等Java内建类实现Comparable接口以提供一定排序方式,但这样只能实现该接口

一次。对于那些没有实现Comparable接口的类、或者自定义的类,您也可以通过Comparator接口来定义您自己的比较方式。

  Comparable接口强行对实现它的每个类的对象进行整体排序。此排序被称为该类的自然排序 ,类的 compareTo 方法被称为它的自然比较方法 。实现此接口的对象列表(和数组)可以通过 Collections.sort(和 Arrays.sort )进行自动排序。

问题一:需要重写hashcode和equals方法吗?

如果只拿TreeSet来说,不用重写equals。因为TreeSet里边的对象都要实现Comparable接口并重写compareTo方法,TreeSet判断元素是否相同以及元素的顺序,都是靠这个方法。
以HashSet为代表的就要重写hashcode 和 equals 方法。

Treeset排序一:自然排序

让对象自身具备比较性,需要实现comparable接口,覆盖compareto方法。

代码实现:

public class Student implements Comparable{//该接口强制让学生具有比较性。
private String name;
private int age ;
Student(String name, int age){
this .name =name;
this .age =age;
}
public String getName() {
return name ;
}
public int getAge() {
return age ;
}
public int compareTo(Object o) {
// TODO Auto-generated method stub
if (!(o instanceof Student))
throw new RuntimeException("此对象不是学生" );
Student s=(Student)o;
if (this .age >s.age )
return 1;
if (this .age ==s.age ){
return this .name .compareTo(s.name);
//如果参数字符串等于此字符串,则返回值 0;
//如果此字符串按字典顺序小于字符串参数,则返回一个小于 0 的值;
//如果此字符串按字典顺序大于字符串参数,则返回一个大于 0 的值。
}
return -1;//return new Integer(this.age).compareTo(new Integer(s.age));
}
}
public class TreesetDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub4
TreeSet ts= new TreeSet();
ts.add( new Student("lisi001" ,20));
ts.add( new Student("lisi007" ,25));
ts.add( new Student("lisi003" ,19));
ts.add( newStudent("lisi05" ,23));
ts.add( new Student("lisi04" ,23));
for (Iterator it=ts.iterator(); it.hasNext();){
Student s=(Student) it.next();
System. err.println(s.getName()+"...." +s.getAge());
}
}
}


2)     Comparator接口

若一个类不能用于实现java.lang.Comparable,或者具备的比较性不是自己所需要的。这时就需要让集合本身具备比较性,在集合初始化时,就有了比较方式。定义比较器,将

比较器对象对象作为参数传递给treeset集合的构造函数。

覆盖方法:

(1)int compare(Object o1, Object o2):

对两个对象o1和o2进行比较,如果o1位于o2的前面,则返回负值。

如果在排序顺序中认为o1和o2是相同的,返回0。

如果o1位于o2的后面,则返回正值。

(2)boolean equals(Object obj): 指示对象obj是否和比较器相等。

该方法覆写Object的equals()方法,检查的是Comparator实现的等同性,不是处于比较状态下的对象。”

 

Treeset排序二:自定义排序

如何定义比较器?

定义一个类,类实现comparator接口,覆盖compare方法。

<pre name="code" class="java">//自然排序
class Student implements Comparable
{
private String name;
private int age;
Student(String name,int age){
this.name=name;
this.age=age;
}
public void setName(String name)
{
this.name=name;
}
public String getName(){
return name;
}
public void setAge(int age)
{
this.age=age;
}
public int getAge(){
return age;
}
public int compareTo(Object obj){
if(!(obj instanceof Student))
throw new RuntimeException("不是学生");
Student s=(Student)obj;
//第一种写法:
if(this.age>s.age)
return 1;
if(this.age==s.age)
return this.name.compareTo(s.name);
return -1;
//return new Integer(this.age).compareTo(new Integer(s.age));
}
}

//定义比较器
class Mycomparator implements Comparator<Student>
{
public int compare(Student s1,Student s2){
int num=new Integer(s1.getName().length()).compareTo(new Integer(s2.getName().length()));
if(num==0)
return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
return num;
}
}

class Test3
{
public static void main(String[] args){
TreeSet<Student> ts=new TreeSet<Student>(new Mycomparator());
ts.add(new Student("aa",30));
ts.add(new Student("bbb",50));
ts.add(new Student("aaaaa",40));
ts.add(new Student("dddd",70));
ts.add(new Student("dddd",50));
for(Iterator it=ts.iterator();it.hasNext();){
Student s=(Student)it.next();
System.out.println("姓名:"+s.getName()+" 年龄:"+s.getAge());
}
}
}

注意:从结果中也可以看出,当两种方式都存在时,以比较器为主。


(3)LinkedHashSet

LinkedHashSet扩展HashSet。如果想跟踪添加给HashSet的元素的顺序,LinkedHashSet实现会有帮助。LinkedHashSet的迭代器按照元素的插入顺序来访问各个元素。它

提供了一个可以快速访问各个元素的有序集合。同时,它也增加了实现的代价,因为哈希表元中的各个元素是通过双重链接式列表链接在一起的。

“为优化HashSet空间的使用,您可以调优初始容量和负载因子。TreeSet不包含调优选项,因为树总是平衡的。”

(4)EnumSet

是枚举的专用Set。所有的元素都是枚举类型。

3、Quenu

队列,它主要分为两大类,一类是阻塞式队列,队列满了以后再插入元素则会抛出异常,主要包括ArrayBlockQueue、PriorityBlockingQueue、LinkedBlockingQueue。另一种

队列则是双端队列,支持在头、尾两端插入和移除元素,主要包括:ArrayDeque、LinkedBlockingDeque、LinkedList。

 

 

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 编程语言