java集合

4.集合类简介
数组是很常用的一种的数据结构,我们用它可以满足很多的功能,但是,有时我们会遇到如下这样的问题:  
1、我们需要该容器的长度是不确定的。
2、我们需要它能自动排序。
3、我们需要存储以键值对方式存在的数据。
如果遇到上述的情况,数组是很难满足需求的,接下来本章将介绍另一种与数组类似的数据结构——集合类,集合类在Java中有很重要的意义,保存临时数据,管理对象,泛型,Web框架等,很多都大量用到了集合类。
常见的集合类有这些种:(集合是否重复,判断元素的hashcode和equals判断) 
实现Collection接口的:Set、List以及他们的实现类。
Map集合:HashMap和TreeMap
他们的关系如下:
接口
简述
实现
操作特性
成员要求
Set
成员不能重复
HashSet
外部无序地遍历成员
成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。
TreeSet
外部有序地遍历成员;附加实现了SortedSet, 支持子集等要求顺序的操作,建议HashSet插入删除,再转型成TreeSet
成员要求实现caparable接口,或者使用 Comparator构造TreeSet。成员一般为同一类型。
LinkedHashSet
外部按成员的插入顺序遍历成员
成员与HashSet成员类似
List
提供基于索引的对成员的随机访问
ArrayList
提供快速的基于索引的成员访问,对尾部成员的增加和删除支持较好
成员可为任意Object子类的对象
LinkedList
对列表中任何位置的成员的增加和删除支持较好,但对基于索引的成员访问支持性能较差,数组和链表的组合
成员可为任意Object子类的对象
Map
保存键值对成员,基于键找值操作,compareTo或compare方法对键排序
HashMap
能满足用户对Map的通用需求
键成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。
TreeMap
支持对键有序地遍历,使用时建议先用HashMap增加和删除成员,最后从HashMap生成TreeMap;附加实现了SortedMap接口,支持子Map等要求顺序的操作
键成员要求实现caparable接口,或者使用Comparator构造TreeMap。键成员一般为同一类型。
LinkedHashMap
保留键的插入顺序,用equals 方法检查键和值的相等性,数组和链表的组合
成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。
IdentityHashMap
使用== 来检查键和值的相等性。
成员使用的是严格相等
WeakHashMap
其行为依赖于垃圾回收线程,没有绝对理由则少用
 
 
二、基本方法及使用
  • Collection使用
     void add(E e)  加入集合
     void addAll(Collection c) 加入所有的集合对象
     boolean contains(Ob ject o)  是否包括对象
     boolean containsAll(Collection c)是否包括集合的所有对象
     void remove(E e) 移除集合
     void removeAll(Collection c) 移除所有的集合对象
     void clear()         清空集合的对象
     boolean isEmpty()    是否为空
     int size()    集合的个数
     toArray(T[l.size()] a)   返回一个数组
     toArray()返回一个包含此Collection中所有元素的Object类型数组;
     Iterator iterator()   返回一个迭代器
 
            ↓
        boolean hasNext()→ Object next() 记得强转→如果不能通过集合的remove()就用迭代器的remove(),之前先next()
iterator相当于有个箭头指在第一个元素之前,next之后就会返回刚经过的元素,这时箭头指在第一个和第二个之间,remove同样的必须先next,删除刚经过的元素。
同一个集合循序多个迭代器读取元素,但不允许多个迭代器同时修改元素。会抛异常。
  1. Collection集合遍历方法
  1. 使用foreach循环  要求对象实现iterable接口
  2. 使用迭代器
    • List集合(能重复)数组能用下标
    • 实现类:ArrayList 和LinkedList ,前者访问速度快,但是插入删除慢,后者相反  List<E> list = new ArrayList<E>();
    • 在Cllection接口基础上增加如下方法:
    • E get(int dex)和E set(int dex,Object b)
       List<E> subList(int fromIndex, int toIndex)   返回实现了List接口的指定的 fromIndex(包括 )和 toIndex(不包括)的视图
      int indexOf(Object o)   int lastIndexOf(Object o)
      List转换成数组:list.toArray(new String[list.size()]) 传入转换后相同的类型
      数组转换成List:Arrays.asList()  不能对其增删,不然抛出异常
      add(int index, E element) 将指定的元素插入列表中的指定位置。
      addAll(int index, Collection<? extends E> c) 从指定的位置开          始,将指定 collection 中的所有元素插入到此列表中。
      remove(int index)移除指定位置上的元素。(还有很多其他重载方法)
    • List的排序
    • Comparable接口中有int campareTo(T t)方法,集合的元素如果实现了接口就可以调用Collections.sort(T t)方法实现自然递增排序,导致sort方法对我们的类有较强的侵入性。
      Public class A implements Comparable<A>{
      Public int CompareTo(A a){
      return >0 大  =0  相等  <0  小}}
      推荐使用:自定义排序方法:
    • 方式一:通过自定义一个类实现Comparator接口,实现Comparator接口的int compare(T t1,T t2)方法,传入对象进行自定义排序。调用Collections.sort(T t,com)
    • 方式二:使用匿名内部类更好
    • Collections.sort(T t,new Comparator<T>(){
      public int compare(T t1,T t2){
      return 1,0,-1;
      }})
      它的好处在于:
    • 不要求对象实现了comparable接口,
    • 有些元素已经实现了comparable接口并且定义了比较规则,但是不满足要求的,也可以用额外的比较器来定义规则满足要求。
    • LinkedListt是一个双向链表,实现了队列(Queue接口)可以将队列看成特殊的线性表,队列限制了对线性表的访问方式:只能从线性表的一端添加(offer)元素,从另一端取出(poll)元素,队列遵循先进先出(FIFO First Input First Output )的原则。Queue extends Collection接口,另增方法如下:
    • boolean offer(E e):将元素追加到队列末尾,若添加成功则返回true。
      E poll():从队首删除并返回该元素。
      E peek():返回队首元素,但是不删除。
      综合:LinkedList遍历就增加一种方法:
    • 使用循环poll()方法。While(e.poll()!=null)
    • Pop pollFirst poll
    • Offer  offerLast
    • Deque是Queue的子接口,定义了所谓“双端队列”即从队列的两端分别可以入队(offer)和出队(poll),LinkedList实现了该接口。如果将Deque限制为只能从一端入队和出队,则可实现“栈”(Stack)的数据结构,对于栈而言,入栈称之为push,出栈称之为pop。栈遵循先进后出(FILO First Input Last Output )的原则。
pollLast
offerFirst push
 
 
  • 首先看这两类都实现List接口,而List接口一共有三个实现类,分别是ArrayList、Vector和LinkedList。List用于存放多个元素,能够维护元素的次序,并且允许元素的重复。3个具体实现类的相关区别如下:
    1. ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要讲已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。
    2. Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。
    3. LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。另外,他还提供了List接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。
 
Set集合(不能重复,且加入的元素必须实现了Comparable接口)
  • 实现类:HashSet(哈希表,不保证顺序不变)和 TreeSet
  • TreeSet还实现了java.util.SortedSet类,TreeSet可以按照自然顺序递增或者比较器递增,所以增加的方法有如下:
new TreeSet(); Set的元素实现了Comparable接口,按自然排序
new TreeSet(new Comparator(){}) 如果自然排序不满足条件,可以自定义排序,这里是使用匿名内部类。
  • first()  Set集合的第一个对象
    Last() Set集合的最后一个对象
    tailSet( E e)  Set集合中e(包括)之后的对象
    headSet( E e)    Set集合中e(不包括)之前的对象
    subSet(E e1,E e2)返回不包含e2的实现Set接口的视图对象
  •  
  • Map集合
  • 含义:Map没有继承Collection接口,提供key value的键值对,key不能重复(equals判断),如果key值已存在,将会替换value值
  • 实现类:(hash表)HashMap和(二叉树)TreeMap(底层采用红黑树)
  •   
    区别:前者哈希表,查找迅速但顺序不是不变,后者相反,后者还实现了java.util.sortedMap接口。
  • Hash表
  • 原理:HashMap内部维护着一个散列数组(就是一个存放元素的数组),每个列表都是一个桶,我们称其为散列桶,而当我们向HashMap中存入一组键值对时,HashMap首先获取key这个对象的hashcode()方法的返回值,得出一个数字,再对桶总数取余,这个数字就是这组键值对要存入散列数组中的下标位置。桶为空就加进去,不为空就用equals()查看散列数组的链表里是否已经包含这个对象,HashMap会将每组键值对封装为一个Entry的实例,然后将该实例存入链表,否则就替换value。
    那么在获取元素时,HashMap同样先根据key的hashcode值进行散列算法,找到它在散列数组中的位置,然后遍历该位置的链表,找到该key所对应的value之后返回。HashMap 的实例有两个参数影响其性能:初始容量16 和加载因子(默认0.75). 达到16*0.75=12时,map扩容一倍成32.
但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。jdk7中采用数组+链表,而JDK1.8中,哈
希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间
  • 方法:
  • void put(K key,V value)  加入对象如果在Map中重新存入以有的key,那么key的位置会不会发生改变,只是将value值替换。
    object get(K key)  返回对象  没有key就返回Null
    value remove(Object key) 移去键值对,返回value值
    putAll(Map<? extends K,? extends V> m)
    boolean containsKey(K key)  是否包括
    boolean containsValue(Object o) 
    isEmpty() 如果此映射未包含键-值映射关系,则返回 true。
    Void clear()  清空所有键值对
    size() 返回此映射中的键-值映射关系数
    Set keySet()  返回一个key的set接口的视图  不是hashset treeset,是某个实现了set的类
    Collection values()返回value的集合视图(不常用)
    Set<Map.Entry<K,V>> entrySet()  返回此映射中包含的映射关系视图           的set视图  entry.getKey()  entry.getValue()
  • 遍历Map的三种方式(上面的三种方法)
  • 遍历所有的key  keySet()
    遍历每一组键值对(Entry)
    遍历所有的value(不常用)
  • TreeMap:实现了SortedMap接口,所以映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法:元素顺序固定
  • new TreeMap();   自然
    new TreeMap(Comparator)  比较器  
  • SortedMap中有subMap()返回视图  headMap() tailMap()方法
  • 线程安全:List、Set、Map都不是线程安全。调用Collections.synchronizedList()synchronizedSet()synchronizedMap()实现线程安全.
  • 就算是线程安全的集合,也不与迭代器的遍历互斥,但是迭代器在遍历集合时不能通过集合增删元素,否则会抛出异常
HashMap 是 Hashtable 的轻量级实现(非线程安全的实现),他们都实现了 Map 接口,主要区别在于 HashMap 允许空(null)键值(key),由于非线程安全,效率上高于 Hashtable。HashMap 允许将 null 作为一个 entry 的 key 或者 value,而 Hashtable 不允许。HashMap 把 Hashtable 的 contains 方法去掉了,改成 containsvalue 和 containsKey。因为 contains 方法容易让人引起误解。Hashtable 继承自 Dictionary 类,而 HashMap 是 Java1.2 引进的 Map interface 的一个实现。最大的不同是, Hastable 的方法是 synchronize 的,而 HashMap 不是,在多个线程访问 Hashtable 时,不需要自己为 它的方法实现同步,而 HashMap 就必须为之提供同步。Hashtable使用枚举类型遍历。他的hash值是直接使用对象的hash值,而hashmap是重写了hash的计算。hashtable数组的默认大小极其增长方式不同。
 
几种视图:
可修改:列表支持set方法
可改变大小:支持add remove方法。
普通试图:subList() SortedSet接口的subSet() headSet() tailSet() SortedMap接口的subMap() headMap() tailMap() 对视图的操作会导致对原集合的操作影响。
Array.asList(a);把数组变成集合,能遍历查看,可以修改,但不能改变大小,
不可修改视图:当视图修改视图的内容,比如add remove,会抛异常  如Array.asList(s) 底层会调用collections的方法返回视图。
同步视图:collections的方法可以把不同步的集合编程多线程同步的。
检查视图:调用Collections的方法。List<String> l=new ArrayList<>();List ll=l;ll.add(2);不会报错,Collections.checkedList(l);之后再add这就会报错。
 
 
 
posted @ 2020-10-18 15:52  z街角的风铃y  阅读(91)  评论(0)    收藏  举报