您的位置:首页 > 编程语言 > Java开发

java笔记17 集合框架 List Set 泛型

2015-06-19 13:10 671 查看
1.集合框架

1.1  为什么出现集合类

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

1.2  数据多了用对象存储,封装到对象里。对象多了,用数组或者集合。

1.3  数组:固定长度,同一类型

集合类:存储不同对象,长度可变,(不可以存基本数据类型)

1.4  因为内部的数据结构不同,有多种具体容器。

 不断地向上抽取,就形成了集合框架。



2.      Collection接口

2.1  添加:

add(object):添加一个元素

addAll(Collection) :添加一个集合中的所有元素。

2.2  删除:

clear():将集合中的元素全删除,清空集合。

remove(obj) :删除集合中指定的对象。注意:删除成功,集合的长度会改变。

removeAll(collection) :删除部分元素。部分元素和传入Collection一致。

2.3  判断:

boolean contains(obj) :集合中是否包含指定元素 。

boolean containsAll(Collection) :集合中是否包含指定的多个元素。

         boolean isEmpty():集合中是否有元素。

2.4  获取:

int size():集合中有几个元素。

2.5  取交集:

boolean retainAll(Collection) :对当前集合中保留和指定集合中的相同的元素。如果两个集合元素相同,返回flase;如果retainAll修改了当前集合,返回true。

2.6  获取集合中所有元素:

Iterator  iterator():迭代器

2.7  将集合变成数组:

toArray();

import java.util.*;//使用前要导包
public class H_02CollectionDemo {
public static void main(String[] args) {
method_2();
method_get();
method_base();
}
public static void method_get()
{
ArrayList al=new ArrayList();
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");
/*Iterator it=al.iterator();//获得了Iterater接口的子类对象
while(it.hasNext()) //判断是否还有元素,
sop(it.next());//有元素则取出
*/
for(Iterator it=al.iterator();it.hasNext();)
{
sop(it.next());
}//用for循环来表示 可以节省内存

}
public static void method_2()
{
ArrayList al1=new ArrayList();//集合里存放的是地址,不会添加实体
// 添加元素
al1.add("java01");
al1.add("java02");
al1.add("java03");
al1.add("java04");
al1.add("java05");

ArrayList al2=new ArrayList();//集合里存放的是地址,不会添加实体
// 添加元素
al2.add("java01");
al2.add("java02");
al2.add("java03");
al2.add("java06");

al1.retainAll(al2);
sop(al1);//al1 变成了交集 如果没有交集 就是空[java01, java02, java03]
sop(al2);//al2不变 [java01, java02, java03, java06]
al2.removeAll(al1);
sop(al1);//al1 不变 [java01, java02, java03]
sop(al2);//al2 删除了交集 [java06]
}
public static void method_base()
{
ArrayList al=new ArrayList();//集合里存放的是地址,不会添加实体
// 添加元素
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");
al.add("java05");

// 获取个数 结合长度
sop(al.size());

//打印集合 不会出现哈希值
sop(al);//[java01, java02, java03, java04, java05, java06, java07]

//删除元素
al.remove(2);//删除了java03
sop(al);
//al.clear();//清空集合
//sop(al);

//判断元素
sop(al.contains("java01"));//true
sop(al.isEmpty());//false
}
public static void sop(Object obj)
{
System.out.println(obj+" ");
}

}

3.      List和Set

3.1  Collection:

|--List:有序(元素存入集合的顺序和取出的顺序一致),元素都有索引。元素可以重复。

|--Set:无序(存入和取出顺序有可能不一致),不可以存储重复元素。必须保证元素唯一性。

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

增加:

           add(index,element);

           addAll(index,Collection);

删除

           remove(index);



           set(index,element);



           get(index);

           subList(from,to);

           listIterater();

 

4.      List

4.1  |---List元素是有序的,元素可以重复,因为该集合体系有索引

         |---ArrayList:底层的数据接口使用的数组结构,特点:查询速度很快,但是增删稍慢,线程不同步(手动加锁),可变长度数组,50%数组延长

         |---LinkedList:底层的数据接口使用的是链表数据结构,特点:增删很快,查询

         |---Vector:底层是数组数据结构,线程同步,被ArrayList替代了。可变长度 100%延长

4.2  List集合特有的迭代器,listIterator是Iterator的子接口

在迭代时,不可以通过集合对象的方法操作结合中的元素

因为会发生ConcurrentModificationException异常

在用迭代器的时候,只能用迭代器的方法操作元素,可是Iterator的方法是有限的

只能对元素进行判断,取出,删除操作

如果想要其他的操作,如添加、修改等,就需要使用其子接口listIterator

该接口只能通过List集合的listIterator接口实现。

import java.util.*;
public class H_04ListDemo {
public static void main(String[] args) {
method_listIterator();
method_List();
}
public static void method_listIterator()
{
ArrayList a=new ArrayList();
a.add("java01");
a.add("java02");
a.add("java03");
sop("原 "+a); //原 [java01, java02, java03]
ListIterator li=a.listIterator();
while(li.hasNext())
{
Object obj=li.next();
if(obj.equals("java02"))
{
li.add("java008");//直接加在后面
}
}
sop("改"+a);//改[java01, java02, java008, java03]
sop("hasNext() "+li.hasNext());//前面已经遍历到最后,后面没有元素 结果false
sop("hasprevious "+li.hasPrevious());//前面有元素 一般不使用,也不能直接使用 结果true

/*Iterator it=a.iterator();//注意迭代器的使用方式
while(it.hasNext())
{
Object obj=it.next();
if(obj.equals("java02"))
{
a.add("java008");
//it.remove();//迭代器不能添加 添加要用子类的迭代器
}
}*/
}

public static void method_List()
{
ArrayList a=new ArrayList();
//添加元素
a.add("java01");
a.add("java02");//可以重复
a.add("java01");
sop("原 "+a);//原 [java01, java02, java01]

//在指定位置添加
a.add(1, "java09");
sop("添加"+a);//添加[java01, java09, java02, java01]

//删除指定位置
a.remove(3);
sop("删除"+a);//[java01, java09, java02]

//修改元素
a.set(0, "java00");
sop("修改"+a);//[java00, java09, java02]

//通过角标获取元素
sop("获取"+a.get(0));
for(int x=0;x<a.size();x++)
{
sop(a.get(x));
}//获取全部元素
Iterator it=a.iterator();//注意迭代器的使用方式
while(it.hasNext())
sop(it.next());

//通过indexOf获取对象的位置
sop("获取位置 "+a.indexOf("java00"));//如果没有 返回-1

List sub=a.subList(0, 2);
sop(sub);//打印了0位和1位 [java00, java09]
}
public static void sop(Object obj)
{
System.out.println(obj+" ");
}
}

5.      Vector的枚举

5.1  Enumeration接口

elements  枚举是Vector的特殊取出方式。

其实枚举和迭代是一样的

因为枚举的名称以及方法名都过长

所以被迭代器取代了。

5.2  Enumeration     en=a.elements();

while(en.hasMoreElements())

           sop(en.nextElement());

5.3  因为Vector效率不高,所以有时候会出现需要用ArrayList有需要枚举的情况,这时候要使用匿名内部类重写方法。

final Iterator<String> it=al.iterator();//加final
Enumeration<String> en=new Enumeration<String>()//数组集合要用枚举,必须使用内部类重写方法
{
public boolean hasMoreElements() {
return it.hasNext();
}

public String nextElement() {
return it.next();
}
};

6.      LinkList
6.1  LinkedList特有方法

addFirst(); 元素添加到首位

addLast();元素添加到末尾

getFirst();获得第一个元素

getLast();获得最后一个元素

removeFirst();也可以获取元素 但是会删除元素

removeLast();

                  

offerFirst(); 

offerLast();

peekFirst();   获取元素 不删除元素 如果集合中没有  返回null

peekLast()          ;       

pollFirst();          获取元素 删除元素  如果集合中没有元素 返回null

pollLast();

LinkedList link=new LinkedList();
link.addFirst("java01");
link.addFirst("java02");
link.addLast("java03");
link.addLast("java04");
sop(link);// [java02, java01, java03, java04]


 
6.2  使用LinkedList来模拟一个堆栈或者队列数据结构。

堆栈:先进后出 First In Last Out FILO

队列:先进先出 First In First Out FIFO

import java.util.*;
public class H_09LinkedListTest {
public static void main(String[] args) {
Queue q=new Queue();
q.myAdd("java01");
q.myAdd("java02");
q.myAdd("java03");
sop(q);
while(!q.isNull())
sop(q.myGet());
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}

class Queue
{
private LinkedList link;
Queue()
{
link=new LinkedList();//构造的时候产生一个LinkList集合
}
public void myAdd(Object obj)
{
link.addFirst(obj);//实现堆栈用addFirst,实现队列用addLast
}
public Object myGet()
{
return link.removeFirst();//取出并删除,用removeLast实现不同的取出效果
}
public boolean isNull()
{
return link.isEmpty();
}
public String toString()
{
return "Queue [link=" + link + "]";
}
}

 
练习:去除ArrayList中的重复元素

public static ArrayList singleElement(ArrayList al)
{
//定义一个临时容器
ArrayList temp=new ArrayList();
Iterator it=al.iterator();
while(it.hasNext())
{
Object obj=it.next();
if(!temp.contains(obj))//集合中没有该元素,则添加
//对象有多个属性,要重写equals方法以便比较是否相同
temp.add(obj);
}
return temp;
}
7. Set集合

7.1   Set接口中的方法和Collection中方法一致的。Set接口取出方式只有一种,迭代器。

         |--HashSet:底层数据结构是哈希表,线程是不同步的。无序,高效;

         HashSet集合保证元素唯一性:通过元素的hashCode方法,和equals方法完成的。

                   当元素的hashCode值相同时,才继续判断元素的equals是否为true。

                   如果为true,那么视为相同元素,不存。如果为false,那么存储。

                   如果hashCode值不同,那么不判断equals,从而提高对象比较的速度。

            |--LinkedHashSet:有序,hashset的子类。

         |--TreeSet:对Set集合中的元素的进行指定顺序的排序。不同步。TreeSet底层的数据结构是二叉树。

7.2    哈希表的原理:

1、对对象元素中的关键字(对象中的特有数据),进行哈希算法的运算,并得出一个具体的算法值,这个值称为哈希值。

2、哈希值就是这个元素的位置。

3、如果哈希值出现冲突,再次判断这个关键字对应的对象是否相同。如果对象相同,就不存储,因为元素重复。如果对象不同,就存储,在原来对象的哈希值基础 +1顺延。

4、存储哈希值的结构,我们称为哈希表。

5、既然哈希表是根据哈希值存储的,为了提高效率,最好保证对象的关键字是唯一的。

  这样可以尽量少的判断关键字对应的对象是否相同,提高了哈希表的操作效率。

7.3  集合判断元素存在的方式

对于ArrayList集合,判断元素是否存在,或者删元素底层依据都是equals方法。

对于HashSet集合,判断元素是否存在,或者删除元素,底层依据的是hashCode方法和equals方法。

通常都要复写hashCode和equals方法

class People
{
private String name;
private int age;
People(String name,int age)
{
this.name=name;
this.age=age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public int hashCode()
{
return name.hashCode()+age;
}
public boolean equals(Object obj)
{
if(!(obj instanceof People))
return false;
People p=(People)obj;
return this.name.equals(p.name)&&this.age==p.age;
}
}

 
8.      TreeSet

8.1 TreeSet:可以对Set集合中的元素进行排序

  1、排序的时候会转化成obj

  2、必须实现Compareto方法

  3、复写该方法,以需要的参数作为排序条件

  4、如果想反序:

a逆转条件

b直接使用Iterator it=ts.descendingIterator();

      c 逆转比较器

  5、主要条件相同的情况下,一定要比较其他条件

          returnthis.name.compareTo(s.name);            

  6、 底层数据结构是二叉树

          保证元素唯一性的依据是:compareTo方法 rerurn 0

8.2 实现排序的方式

1、 排序的第一种方式:

实现Comparable(可比较)接口 覆盖compareTo方法  使元素具备比较性

2、 排序的第二种方式

当元素自身不具备比较性时,或者具备的比较性不是所需求的

这时就需要让集合自身具备比较性。

定义了比较器  将比较器对象作为参数传递给TreeSet集合的构造函数

定义一个类,实现Comparator(比较器)接口,覆盖compare方法

如果两种排序方式都存在,以比较器为主。

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
public class H_01TreeSet2 {
public static void main(String[] args) {
TreeSet ts=new TreeSet(new MyCompare());//将比较器导入构造函数
ts.add(new Stu("lisi04",22));
ts.add(new Stu("wangwu",25));
ts.add(new Stu("lisi",26));
ts.add(new Stu("lili",26));

Iterator it=ts.iterator();
while(it.hasNext())
{
Stu s=(Stu)it.next();//注意转型
sop(s.getName()+" "+s.getAge());
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}

class Stu implements Comparable//该接口强制让学生具备比较性
{
private String name;
private int age;
Stu(String name,int age)
{
this.name=name;
this.age=age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
/*
public int hashCode()
{
H_12HashSetDemo.sop(this.name+" hashcode");
return name.hashCode()+age;
}
public boolean equals(Object obj)
{
if(!(obj instanceof Stu))
return false;
Stu p=(Stu)obj;
H_12HashSetDemo.sop(this.name+" equals");
return this.name.equals(p.name)&&this.age==p.age;

}
*/
@Override
public int compareTo(Object obj) //复写compareTo
{

if(!(obj instanceof Stu))
throw new RuntimeException("不是学生类");
Stu s=(Stu)obj;
if(this.age<s.age)
{
return -1;
}else if(this.age>s.age)
{
return 1;
}else//主要条件相同的情况下,比较其他条件
{
return this.name.compareTo(s.name);
}

}
}
class MyCompare implements Comparator//定义一个比较器
{
public int compare(Object o1,Object o2)
{
Stu s1=(Stu)o1;
Stu s2=(Stu)o2;
int num=s1.getName().compareTo(s2.getName());
if(num==0)
return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
return num;
}
}

    结果

        lili  26

lisi  26

lisi04  22

wangwu  25

    练习:让字符串按长度排序

   class StuLengthComparator implements Comparator
{
public int compare(Object o1,Object o2)
{
String s1=(String)o1;
String s2=(String)o2;
int num=new Integer(s1.length()).compareTo(new Integer(s2.length()));
//长度转换为包装类后再比较
if(num==0)
return s1.compareTo(s2);
return num;
}
}

 
9.  泛型

9.1 泛型概述

    泛型:Jdk1.5版本以后出现的新特性,用于解决安全问题,是一个安全机制。

    定义集合的时候需要加<数据类型>, 创建迭代器的时候也需要加上<数据了类型>

9.2  好处

    1、将运行期间出现问题ClassCastException,转移到了编译时期

        方便与程序员解决问题,让运行时期问题减少,安全。

    2、避免了强制转换的麻烦

9.3  泛型格式:通过<>来定义要操作的引用数据类型

    在使用java提供的对象时,什么时候写泛型呢

    通常在集合框架中很常见

    只要见到<>都要定义泛型

    当使用集合时,将集合要存储的数据类型作为参数传递到<>中

 

10. 泛型限定

    泛型类定义的泛型,在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。

    为了让不同方法可以操作不同类型,而且类型还不确定,那么可以将泛型定义在不同方法上。

   

    静态方法不能访问类上定义的泛型,可以定义在静态方法上。

    方法上的定义格式,放在返回值前

    publicstatic <W> void method(W w)

示例: 

public class H_09GenericDemo3 {
public static void main(String[] args) {
Demo<String> d=new Demo<String>();
d.show("hehe");
d.print("9");//只能操作定义好的类型

Demo2 d2=new Demo2();
d2.show("haha");
d2.show(111);//泛型定义在方法上 可以传入不同参数
}
}

class Demo<T>//定义在类上
{
public void show(T t)
{
System.out.println("show"+t);
}
public void print(T t)
{
System.out.println("print"+t);
}
}
class Demo2
{
public <T> void show(T t)//定义在方法上
{
System.out.println("show"+t);
}
public <Q> void print(Q q)
{
System.out.println("print"+q);
}
}

    结果:
        showhehe

print9

showhaha

show111

11. 泛型接口

interface Inter<T> {
void show(T t);
}
class InterImpl<R> implements Inter<R> {
public void show(R r) {
System.out.println("show:"+r);
}
}

12.   泛型中的通配符
可以解决当具体类型不确定的时候,这个通配符就是?  ;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。

12.1  泛型限定:

         上限:?extends E:可以接收E类型或者E的子类型对象。

         下限:?super E:可以接收E类型或者E的父类型对象。

12.2  上下限的使用

上限什么时候用:往集合中添加元素时,既可以添加E类型对象,又可以添加E的子类型对象。为什么?因为取的时候,E类型既可以接收E类对象,又可以接收E的子类型对象。

下限什么时候用:当从集合中获取元素进行操作的时候,可以用当前元素的类型接收,也可以用当前元素的父类型接收。

12.3  泛型的细节

1、泛型到底代表什么类型取决于调用者传入的类型,如果没传,默认是Object类型;

2、使用带泛型的类创建对象时,等式两边指定的泛型必须一致;

         原因:编译器检查对象调用方法时只看变量,然而程序运行期间调用方法时就要考虑对象具体类型了。

3、等式两边可以在任意一边使用泛型,在另一边不使用(考虑向后兼容);

ArrayList<String> al =new ArrayList<Object>();  //错

//要保证左右两边的泛型具体类型一致就可以了,这样不容易出错。

ArrayList<? extendsObject> al = new ArrayList<String>();//可以通过

al.add("aa");  //错

//因为集合具体对象中既可存储String,也可以存储Object的其他子类,所以添加具体的类型对象不合适,类型检查会出现安全问题。?extendsObject 代表Object的子类型不确定。

public static voidmethod(ArrayList<? extends Object> al) {

al.add("abc");  //错

         //只能对al集合中的元素调用Object类中的方法,具体子类型的方法都不能用,因为子类型不确定。

}

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java笔记