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

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类中的方法,具体子类型的方法都不能用,因为子类型不确定。

}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值