【Java基础系列】第8章 Java集合框架

8.1 集合框架的概念

8.1.1集合框架概况

(1)集合框架是java.util包中提供的一系列工具,它为程序处理对象组提供了标准的方式,诞生自1.2版本。

1.2版本之前,处理对象的方法如下:

  1. 数组    缺点:长度固定、数组中只能存放一种类型的数据

          优点:速度快

  1. Vector、stack、properties   缺点:缺乏统一的操作方式,不易扩展

1.2版本之后,诞生了集合框架

优点: ①性能提高

②操作统一     Collection、List、Set

③提高了扩展性

④集合实现了Iterator接口

⑤迭代器:提供了访问元素的统一方法

(2)集合学习的三大部分:

①集合的继承关系

②各集合的特点

③属性各集合性能,安全性

8.1.2常用接口

Collection是所有集合的父接口,不包括Map

List实现自Collection,以列表形式存储的集合

Set实现自Collection,不允许出现相同元素

它们之间的关系是继承关系。(它们都是接口

Collection  

 --List:将以特定次序存储元素。所以取出来的顺序可能和放入顺序不同。

    --ArrayList / LinkedList / Vector  

 --Set :不能含有重复的元素

    --HashSet /TreeSet

Map  

 --HashMap

 --HashTable

 --TreeMap

集合特性:

(1)集合只能存放对象,虽然能够装载整型数,但它是包装类Integer的对象

(2)集合没有限制具体装载数据类型,不建议同一个集合装载不同的数据类型,会发生转型错误,因为它存储的数据全部是Object类型,所以你无法确认在具体某位置存放是什么,在使用该数据时,无法按特定类型来转型。

(3)集合输出时,因其重写了重写toString()方法,所以直接打印输出整个集合,这一点比数组方便。 // collection : [ andy, 123, 5003 ]

什么时候用到集合框架:

如果并不知道程序运行时会需要多少对象(集合只能存放对象),或者需要更复杂方式存储对象——可以使用Java集合框架

集合框架的内容:

Collection接口 存储一组不唯一,无序的对象

List 接口 存储一组不唯一,有序(插入顺序)的对象

Set 接口 存储一组唯一,无序的对象

Map接口 存储一组键值对象,提供key到value的映

8.2 Collection

 

Iterator接口

//创建一个集合对象, 集合只能装载对象
//创建一个没有元素的集合
Collection c = new ArrayList();    
c.add("123");
c.add("456");
c.add("andy");
c.add("ken");
System.out.println("c:"+c);

//如果想遍历这个集合
//迭代: 遍历  ---> 迭代器
Iterator it =c.iterator();
//it.next();
//it.hasNext();
//it.remove();
//通过迭代器迭代集合中的元素
while(it.hasNext()){
    // 迭代器后移, 并获取该位置上的元素
    String s = (String)it.next();   
    System.out.println("s:"+ s);
}



//如果, 我想要迭代的时候, 添加, 删除, 修改 集合中的元素
//切记不要在遍历时, 变更集合中的元素
while(it.hasNext()){
    //迭代器后移, 并获取该位置上的元素
    String s = (String)it.next();   
    System.out.println("s:"+ s);
    //删除不会报错, 迭代的结果会有偏差
    if(s.equals("andy")){
        c.remove("andy");
    }
    //当你在迭代时, 添加一个元素到集合中, 运行时报错
    //java.util.ConcurrentModificationException (并发修改异常)
    //这句话如何理解 ?
    //在遍历元素的同时, 另一边又在修改集合, 可能会引起迭代器无法确认
    //遍历的边界
    c.add("candy");
}

8.2List分类和用法

 

  1. ArrayList

[1]实现了长度可变的数组,在内存中分配连续的空间。遍历元素和随机访问元素的效率比较高。

  1. LinkedList

[1]采用链表存储方式。插入、删除元素时效率比较高。

[2]以链表的形式来实现list功能,查询可用get( index ),效率比ArrayList差,但插入、删除元素都比ArrayList优,一般插入、删除用的不多,所以一般选用ArrayList

[3]迭代顺序与插入时顺序一致

[4]双端队列,可以两头出入队

[5]单端队列,如果不用尾部功能,它就是单端队列

[6]还可以当成Stack来使用,与Stack区别:LinkedList非线程安全,性能上比Stack要快

3、Vector

[1]基于数组的存储形式来实现的

[2]添加、删除方法中用了同步锁保证线程安全

[3]性能比ArrayList差

[4]还存在一些BUG

[5]Stack是Vector的子类,它也是线程安全的

4、迭代器ListIterator

List 专门的迭代接口ListIterator,其在Iterator接口基础上增加了如下方法:

Boolean hasPrevious() : 返回迭代器关联的集合是否还有上一个元素

Object previous() :返回该迭代器的上一个元素

Void add() :在指定位置插入一个元素

8.2.2ArrayList  储存有序,可重复

     ①存储任意类型数据

//创建集合数组list
ArrayList list=new ArrayList(); 
//往list数组添加数据
list.add(2);            
//往list数组里添加数据nihao
list.add(“nihao”);  
//往数组里添加类Molly
list.add(new Molly()); 
//往数组添加方法
list.add(new Molly().fangfa()); 
//往数组第一个位置添加数据55
list.add(0,55);
//往集合中添加整个集合
list.addAll(集合);
//删除数据nihao
list.remove(“nihao”);
//删,往数组添加字符串除数组第三个数据
list.remove(2);
//获取并输出第一个
System.out.println(list.get(0));
//获取输出数组全部数据
System.out.println(list);

②存储指定类型的数据

//创建数组
ArrayList<String> list=new ArrayList<String>();
//往数组添加字符串
list.add(“您好”); 
list.add(“nihao”);

ArrayList与数组的交互

String[] s=new String[list.size()];
//括号里的s参数只代表泛型的类型,不加要强转
s=list.toArray(s);     

④构造ArrayList

//无泛型无参数
ArrayList    list=new ArrayList();            
//有泛型无参数
ArrayList<T> list2=new ArrayList<>();         
//有泛型有传值
ArrayList<T> list3=new ArrayList<>(list);     
//有泛型指定长度
ArrayList<T> list4=new ArrayList<>(3);        

8.2.3 LinkedList   链表

①构造LinkedList

//无泛型无参数
LinkedList    list=new LinkedList();           
//有泛型无参数
LinkedList<T> list2=new LinkedList<>();        
//有泛型有参数
LinkedList<T> list3=new LinkedList<>(list);    

②与数组的交互    同ArrayList

③常用方法:

//往集合list中顺序添加元素
List.add(“a”);             
//往list的第二个位置添加b
List.add(1,”b”);           
//往list的尾部添加元素last
List.addLast(“last”);      
//往list的头部添加元素first
list.addFirst(“first”);    
//获取并移除列表的头(第一个元素)
List.poll();                 
//获取但不移除列表的头(第一个元素)
List.peek();                 
//从此列表所表示的堆栈处弹出一个元素。
List.pop();                  
//获取并移除此列表的头(第一个元素)。
List.remove();               

8.3Set分类和用法

  1. HashSet:是一个无序无重复的集,它能够快速的查找指定的对象,这是因为它采用了能够适应这个需求的散列码(hash code)。
  2. LinkedHashSet:有序无重复,因其还使用了一个链表结构来记录插入时候的顺序,当用Iterator迭代时,它会使用这个顺序来迭代。
  3. TreeSet:是一个有序无重复的集。我们可以用任何的顺序向TreeSet中加入对象。

Set 集合的特点:

[1] Set 不能出现重复的元素

[2] Set 是没有顺序, 无法通过下标访问

[3] 遍历时的顺序, 未必与插入时一致

[4] 线程非安全

add():

HashSet 根据hashCode判定对象应该存放的位置 

②如果位置(即hashCode)一样, 它会调用元素的 equals 方法来判定两个元素是否为同一元素;hashCode一样且equals方法返回值一样,判断为同一元素。

 

remove():

首先去找该对象的所在位置, 再去执行 equals 判断你指定的 Object 和 它找到的元素是否为同一个元素, 如果是  --> 删除

8.3.1 HashSet    储存无序,不可重复

①构造HashSet   同ArrayList

②概念:HashSet是通过散列的机制来进行信息存储到哈希表,它不能保证集合的顺序,    但是存储的速度非常快,Set里元素不能重复,是唯一的。

③与数组的交互    同ArrayList

④常用方法:

set.add(“a”);   
//从此set中移除所有元素
set.clear();               
//如果set包含此元素,则返回true
set.contains(Object o);    
//如果此set为空,在返回true
set.isEmpty();             
//返回对此set中元素进行迭代的迭代器
set.iterator();            
//如果指定元素存在此set中,则将其移除
set.remove(Object o);      

HashSet的底层其实也是使用了HashMap作为储存元素的容器

HashSet只能插入一个null

8.3.2TreeSet

迭代时,它是以元素的排序规则来进行输出的,即已经排序好再输出

  1. 自然排序 --->

[1]元素的类型, 必须实现 Comparable, 通过 compareTo() 方法 来判断两元素大小

[2]如果你要插一个元素到 TreeSet 中, 这种元素类型必须实现  Comparable 接口,为了让 TreeSet 可遵循你所指定的顺序标准来确认元素的存放位置

  [3]Comparable 接口 的作用: 用作两个同类对象的比较, 只要你复写它的 compareTo() 方法它就能算出两个对象的差值, 通过这个差值来判断谁大谁小

总结:TreeSet自然排序:调用集合元素的compareTo( Object obj )方法(需要实现comparable接口)比较元素间大小关系(0相等,非0不相等),再升序。

例子:

/**
 * TreeSet自然排序
 */
public class TreeSetTest {
    public static void main(String[] args) {
        //自然排序
        Set treeset1 = new TreeSet();
        //加入Phone实例作为TreeSet集合数据
        treeset1.add( new Phone(2));   
        treeset1.add( new Phone(22) );
        //迭代器,循环遍历
        Iterator it = treeset1.iterator();  
        while( it.hasNext() ){
            System.out.println( "treeset1: " + it.next() );
        }
    }
}

//注意实现的接口要有泛型规定,不然都是可以自动转换为Object类型
//这样就可以兼容另一个类型同时加入TreeSet了
/**
 * Phone类是TreeSet需要需要传入的数据类型,所以要实现Comparable接口
 * 从而实现该接口的compareTo方法,判断新加入的值是否与已加入的值相同,
 * 返回0相同,其他不相同
 */
class Phone implements Comparable<Phone>{
    int num;
    public Phone( int num ){
        this.num = num;
    }

    public String toString(){
        return ("Phone num:" + String.valueOf( num ));
    }

    @Override
    public int compareTo( Phone p ) {
        if( p == null ){
            //不能为null,不能加入
            return 0;   
        }
        if( p.getClass() == Phone.class ){
            //Phone p1 = (Phone)p;
            //同一个类返回两值差
            return p.num-this.num; 
        }
        //不为同一类型返回0,不能加入
        return 0;   
    }
}

(2)定制排序  -->

[1] 通过调用以下构造器来实现 TreeSet 定制排序

public TreeSet( Comparator<? super E> comparator ) {
    this(new TreeMap<>(comparator));
}

[2] 创建一个类实现 Comparator(接口): 比较器

[3] 覆盖 Comparator 的 compare 方法

例:

/**
 * TreeSet定制排序
 */
public class TreeSetTest2 {
    public static void main(String[] args) {
        //定制排序
        //-1为逆序
        Set treeset = new TreeSet( new PhoneComparator(-1) ); 
        treeset.add( new Phone1(111) );
        treeset.add( new Phone1(1111) );
        //迭代器,用于遍历
        Iterator it = treeset.iterator(); 
        while( it.hasNext() ){
            System.out.println( "treeset1: " + it.next() );
        }
    }
}

/**
 * TreeSet集合实例化时候需要传入的一个实现Comparator接口的类
 */
class PhoneComparator implements Comparator{

    int flag;
    //传入值非-1的话,返回1。
    public PhoneComparator( int flag ){
        if( flag!=-1 ){
            this.flag = 1;
        }else{
            this.flag = -1;
        }
    }


    /**
     * 比较o1和o2这两个值
     */
    @Override
    public int compare(Object o1, Object o2) {  
        //不能有null,有则返回0
        if( o1 ==null || o2 ==null ){       
            return 0;
        }
        //两个都必须是Phone1类,否则返回0
        if( o1.getClass()!= Phone1.class|| o2.getClass()!= Phone1.class){  
            return 0;
        }
        //两个都是Phone1,所以转为Phone1类型
        Phone1 p1 = (Phone1)o1;    
        Phone1 p2 = (Phone1)o2;
        //返回两个值得差值
        return flag*(p1.num- p2.num);  
    }
}

/**
 * Phone1类,其实例用于加入到TreeSet集合
 */
class Phone1 {
    int num;
    public Phone1( int num ){
        this.num = num;
    }
    public String toString(){
        return ("Phone num:" + String.valueOf( num ));
    }
}

8.3.3 LinkedHashSet

(1)有序,不可重复

因LinkedHashSet 另外还使用一个链表结构来维护(记录), 插入时的顺序, 当你在用  iterator 迭代时, 它就以这个链表的顺序来进行迭代

Set set = new LinkedHashSet();

8.4Map的分类和使用

Map接口专门处理键值映射数据的存储,可以根据键实现对值的操作

最常用的实现类是HashMap

  • HashMap和TreeMap是继承Map,并且Map是以key --value形式显示
  • key值不可以重复,但是value值可以重复

若 新key-value 与 旧key-value 相同,旧key不会被覆盖,而旧value会被新value覆盖。

 HashMap和HashTable之间的区别

1.HashTable是一个线程安全的Map实现,HashMap线程不安全

2.HashTable不容许使用null作为key和value,如果放入将会发生空指针异常,但是HashMap当中的key能够重复,所以最多只有个key值为空

 

8.4.1 HashMap    储存数据一一对应

①存储任意类型数据

如:HashMap map=new HashMap();//创建HashMap集合

map.put(1,”神农”);// 1的值为”神农”

map.put(“二”,99);//二的值为99

map.put(3.3,new Molly());//3.3值为类Molly

map.remove(1);//删除名字为1的对应的数据

map.put(“二”,”伏羲”);//替换二中的值为“伏羲”

System.out.println(map);//输出map的所有数据

System.out.println(map.get(3.3));//输出3.3中的数据

        ②存储指定类型的数据

            如:HashMap<String,Integer> map=new HashMap<String,Integer>();

                map.put(“我”,123);  //我指定的数值为123

③HashMap的实例有两个参数影响其性能:初始化容量16个单位 和 加载因子0.75

其中key、value都可以为null

⑤用了一个数组来存放Entry对象(一个内部类),Entry对象的存放位置是通过散列算法来计算的,同一个位置若有多个Entry对象存在,它们是以链表的形式来连结的

⑥LinkedHashMap:用了一个两向链表来维护key的顺序,它保证了插入的顺序与迭代的顺序一致,性能上略低于HashMap

⑦WeakHashMap:存放在WeakHashMap的key一旦垃圾回收器执行回收时,key-value都会被回收,前提是key没有被强引用

8.4.2 SortedMap ---- TreeMap:

存入map的key必须实现comparable接口,还要复写compareTo方法,如果key没有实现comparable接口,必须使用定制排序来创建TreeMap对象

②TreeMap使用了红黑树对key进行了排序,记录了位置,在迭代的时候,使用红黑树排序顺序进行迭代

③TreeMap的性能比LinkedHashMap还要差

8.4.3 Hashtable 

①与HashMap内部实现原理基本相同

②线程安全,主要用于并发操作时,因它线程安全(synchronized)

③初始化容量:11 个单位  加载因子:0.75

④key、value都不能为null

properies用于操作属性文件

8.5 Queue集合

8.6迭代器

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__Yvan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值