第8章java 集合

来源:互联网 发布:西南民族大学知乎 编辑:程序博客网 时间:2024/05/24 05:59

第8章java 集合

java集合大致可以分为 Set  List  Queue  Map四类 其中 Set表示无序,不可重复的集合,List代表有序,重复集合Map表示具有映射关系的集合Queue 代表队列集合实现方式

8.1集合概述

为了保持数量不确定,以及保存具有映射关系的数据,java提供了集合类

数组元素既可以保存基本类型的数据,也可以保存对象 ,但是 集合就只能保存对象

 

java集合类主要是由2个接口派生出来的Collection Map, 这2个接口是集合框架的根接口

 

8.2 CollectionIterater接口

Collection接口是 List SetQueue接口的父接口,

Collection实现类都是重写了toString()方法

Collection的父接口是Iterater

 

Iterater并不是把集合元素本身传给了迭代变量,而是集合元素的值传给了迭代变量

只有通过Iteraterremove()方法删除上一次next()方法返回的集合元素才可以

Iterater迭代器采用的是快速失败 机制 ,一旦迭代器过程中检测到了集合已经被修改,通常是程序中的其他线程修改,都会引起一场 这样可以避免共享资源而引发的问题

 

8.2.5 使用java 8新增的Predicate操作集合

java 8 Collection集合新增了一个removeIf(Predicate filter)方法,该方法将会批量删除符合filter条件的所有元素Pridicate是一个谓词(相当于是一个条件)

 

8.2.6 使用java 8新增的Stream操作集合

java 8 新增了Stream IntStream LongStream DoubleStream等流式API这些API代表支持多个串行和并行聚集操作的元素

独立使用stream的步骤

1. 使用Stream 或者XxxStreambuilder()方法创建该Stream对于的Builder

2. 重复调用Builderadd()方法向该流中添加多个元素

3. 调用Builderbuild()方法向该流里添加对应的Stream

4. 调用Stream的聚集方法  调用的聚集方法每次只能执行一行

 

Stream提供了大量的方法进行聚集操作,这些方法既是 中间的 也可以是末端的

中间方法 中间操作允许流保持打开状态,并允许直接调用后续方法。

末端方法 末端方法是对流的最终操作 执行末端方法后该流将被消耗 且不再可用

有状态的方法 这种方法会给流增加一些新的属性  比如元素的唯一性

短路方法 短路方法可以尽早的接结束对流的操作

  

Stream提供的方法可以对所有的集合元素进行处理 这样大大简化了集合编程的代码

 

8.3 Set 集合

8.3.1 HashSet

HashSet是按Hash算法来存储的集合,因此具有很好的查找性能

HashSet 集合具有以下的几个特点

1. 不能保证元素的排列顺序

2. HashSet 不是同步的

3. 集合元素值可以为空

 

HashSet集合中存入元素时,HashSet会调用hashCode()方法来得到该对象的hashCode值,然后根据该hashCode值决定该对象在HashSet中存储的位置,通过有两个元素通过equals 方法比较返回值为true但他们hashCode码不相同,HashSet依然会把他们存储在不同的地方

因此HashSet判断两个元素是否相等的标准是通过对象equals()方法和hashCode()两个方法判断

 

如果需要重写equals()方法时 也应该重写hashCode()方法 规则是 如果返回的equals()方法判断相关元素相同 ,那么hashCode()方法也应该相同

 

如果equals()方法判断为相同hashCode()值不同,那么这两个对象会被保存在不同的地方 从而使元素添加成功,但是就与Set集合的规则冲突了

 

如果equals()判断为false但是hashCode()值相同,HashSet集合试图想他吗存在同一个位置,但是又不行,所以实际上回采用链式存储 导致性能降低

 

重写hashCode()方法

1.在程序运行中,同一个对象调用hashCode()方法的返回值应该相同

2.当两个对象通过equals()方法判断为true时,hashCode()值应该相同

3.对象中用作equals()方法比较的实例变量,都应该用于计算hashCode

 

如果想HashSet集合中添加一个可变对象后,后面的程序修改了可变的对象,则可能导致这个集合出现相同的元素,从而导致无法准确访问到该对象

 

8.3.2 LinkedHashSet

LinkedHashSet 集合也是根据hashCode值来决定元素存储位置的,但它同时也使用链表维护次序,LinkedHashSet将会按照元素的添加顺序来访问集合里的元素

在迭代全部元素的时候性能较好

 

8.3.3 TreeSet

TreeSetSortSet接口的实现类,TreeSet可以确保集合元素处于排序状态

Comparator comparator() 如果TreeSet 采用了定制排序,则该方法返回定制所有使用的Comparator如果TreeSet采用自然排序则返回 null

TreeSet 采用的是红黑树数据结构来存储集合元素的

 

TreeSet有两种排序方式 自然排序和定制排序

1. 自然排序 会调用集合元素的compareT哦(Object obj)方法来比较元素之间的大小关系,然后将集合元素按升序排序 

0bj1.compareTo(obj2) 如果该方法返回0  表示两个元素相等 如果返回 正整数 表示obj1 > obj2

 

如果试图把一个对象添加到TreeSet时,则该对象的类必须实现Comparable接口,否则会抛出异常

TreeSet集合中添加元素时,只有第一个元素无需实现Comparable接口,后面的都添加的所有元素都不想实现Comparable接口 。但当试图从TreeSet中取出元素时,依然会出异常

 

大部分类在实现CompareTo(object obj)方法时,都需要将比较对象obj强制类型转换成相同类型,只有两个相同类才能比较大小

 

所有希望TreeSet集合能正常运行就必须加入相同类型的元素

 

对于TreeSet 集合判断两个对象是否相等的唯一标准就是 两个对象通过compareTo()方法返回值 是否等于0如果为0则新对象无法加入TreeSet集合中

 

TreeSet中添加可变对象后,这将导致它与其他的对象大小发生改变,但是TreeSet并不会改变它的大小 有可能会导致TreeSet中存储的两个对象通过compareTo(Object obj) 方法比较返回 0

 

2制定排序    

需要在创建TreeSet集合对象时,提供一个Comparable(函数式接口)对象与集合关联 修改Comparable对象负责对集合元素的排序逻辑

 

8.3.4 EnumSet

EnumSet是一个专门为枚举设计的集合类,EnumSet中的所有元素必须是指定的枚举类型,该枚举类型在创建EunmSet时显示或者隐式指定EunmSet的集合顺序由枚举类内的顺序来决定

 

EnumSet在内部以位向量的形式存储,这种存储形式非常紧凑,高效,因此EnumSet对象占内存很小,运行效率很高, 尤其是进行批量操作

 

EnumSet集合类不运行加入的元素为null

EnumSet没有提供构造器来创建该类的实例,只能通过类方法来创建EnumSet对象

 

当试图添加 或者复制一个Collection集合来创建EnumSet集合是,必须保证 元素都是同一个枚举类的枚举值

8.3.5 Set集合的性能分析

 

HashSet 性能总是比TreeSet好 特别是在 添加 查询 元素等操作 因为TreeSet 需要额外的红黑色树算法来维护元素的次序

 

HashSet 有一个子类 LinkedHashSet对于普通的插入。删除都要比HashSet要慢一些 但是遍历LinkedHashSet更快

 

EnumSet 性能是最好的,但是它只能保存同一个枚举值类的枚举值作为集合元素

 

HashSet  EnumSet  LinkedHashSet 都是线程不安全的

 

8.4 List 集合

List集合代表一个元素有序,可重复集合,list集合默认按元素的添加顺序设置元素的索引

 

8.4.1 java 8 改进List接口和ListIterator

List判断对象相等时通过equals()方法判断的

 

程序试图删除一个集合元素,会一次与集合进行比较,如果该equals()方法以缪一个集合元素作为参数时返回true,所以每次都是删除第一个元素

 

8.4.2 ArrayList Vector实现类

ArrayList Vector都是基于数组实现,所以ArrayListVector类封装了一个动态的,允许再次分配的Object[]数组

 

如果向ArrayList或者Vector中增加大量的元素时,可以使用ensureCapacity方法,一次性的增加,可以减少分配次数,从而提高性能

initialCapacity 默认的长度为10

 

ArrayList 是线程不安全的  Vector是线程安全的

 

Vector还提供了一个Stack子类,它用于模拟栈 ,与其他集合一样,进栈和出栈都是Object,因此从栈中取出元素,必须要进行类型转换

 

ArrayDeque 也是List的实现类,ArrayDeque也实现了Deque接口,因此可以用来作为栈使用 (也是基于数组实现的,性能也很好)

 

8.4.3 固定长度的List

Arrays 类中提供了一个方法asList(Object ...a)方法 该方法可以把一个数组或者指定个数的对象转换成一个List集合 ,这个数组既不是ArrayList实现类 也不是Vector实现类,而是Arrays内部类ArrayList的实例

Arrays.ArrayList是一个固定长度的List集合,程序只能遍历访问该集和里的元素

 

8.5 Queue 集合

Queue 用于模拟队列这种数据结构,队列通常指“先进先出”的容器,通常队列不允许随机访问对列中的元素

Deque 代表了一个双端对列,可以同时从两端添加,删除,因此也可以被当做队列使用,也可以做到栈来使用。

JAVA Deque提供了ArrayDequeLinkDeque两个实现类

 

8.5.1 PriorityQueue 实现类

PriorityQueue是一个比较标准的队列实现类,这是因为PriorityQueue保存队列元素顺序并不是按照添加队列的顺利,而是按照队列元素的大小进行重新排序

PriorityQueue 不允许插入null元素

PriorityQueue有两种排序方式 自然排序 和制定排序

8.5.2 接口与ArrayDeque实现类

Deque 底层数组的长度是16

当程序中需要使用栈这个数据结构时,推荐使用ArrayDeque尽量避免使用stack因此stack是一个古老的集合 ,性能较差

 

8.5.3 LinkedList 实现类

LinkedList类实现了List接口,也实现了Deque接口,可以被当做双端队列来使用,因此既可以被当做栈来使用,也可以当做队列使用

8.5.4 各种线性表的性能分析

java提供的List就是一种线性表接口,而ArrayListLinkedList又是线性表的两种典型实现。Queue代表了队列,Deque代表了双端队列(既可以作为队列使用,又可以作为栈 使用)

 

LinkedList 不仅提供了List集合的功能, 还提供了双端队列,栈的功能

 

对于ArrayList 集合,应该使用随机访问方法来遍历集合元素, 对于LinkedList集合则应该使用迭代器来遍历集合

 

8.6 JAVA 8 增强MAP集合

8.6.2 java 8 改进HashMapHashtable实现类

Hashtable不允许使用 null作为keyvalue

HashMap 可以使用null作为key value

HashMapHashtable是通过equals()方法比较返回true即可。

 

如果程序使用了可变的对象作为HashMap  Hashtable key,并且程序中修改了key的可变对象,则可能使得程序无法准确的访问MAP中的被修改过的key

 

8.6.3 LinkedHashMap 实现类

LinkedHashMap 是使用双向链表来维护key-value对的次序,该链表负责维护Map的迭代顺序,迭代顺序与key-value对的插入顺序一致。

 

 

LinkedHashMap可以避免HashMapkey-value对进行维护

LinkedHashMap 需要维护元素的插入顺序,因此性能低于hashMap

8.6.4 使用Properties读写属性文件

Properties类时Hashtable类的子类,该对象在处理属性文件时特别方便,Properties类可以吧Map对象和属性关联起来,从而可以把Map对象中的key-values对写入属性文件中。也可以把属性文件中的 属性名=属性值 加载到Map对象中

 

8.6.5 SortedMap接口和TreeMap实现接口

TreeMap就是红黑树数据结构,每个key-value对即作为红黑树的一个节点,

 

TreeMap是根据节点排序TreeMap可以保证素有的key-value处于有序状态

 

8.6.7 IdentityHashMap 实现类

IdentityHashMap类中当且仅当两个key严格相等(key1=key2)时,才认为这两个key相等

 

8.6.8 EnumMap实现类

EnumMap类时一个与枚举类一起使用的Map实现类,EnumMap中的所有key都必须是单个的枚举类的枚举值。创建EnumMap时,必须显示或者隐式的创建一个

EnumMap类的特点

1. EnumMap类在内部已数组形式保存,所以这种实现形式非常紧凑,高效

2. EnumMap根据key的自然顺序(枚举值在枚举类中定义的顺序)来维护key-value

3. EnumMap不允许使用null作为key但允许使用null作为value

8.6.9 Map实现类的性能分析

TreeMap通常要比HashMapHashtable要慢(用红黑树来管理key-value

TreeMap 集合中的key-value值总是处于有序状态,

一般场景 多考虑HashMap,因为HashMap正式为快速查询设计的(底层采用数组来存储key-value),假如程序需要一个总是排好的Map时,则可以考虑使用TreeMap

LinkedHashMapHashMap慢一定点,因为它要维护链表来维护key-value的添加顺序

EnumMap性能最好

8.7 HashSetHashMap的性能选项

hash表里可以存储的元素的位置被称为桶,每一个桶里存储一个元素时桶的性能最好,但hash表的状态是open的 在hash发生冲突的时候,单桶里可能存储多个元素,这些元素会以链表的形式存储,必须按顺序搜索

 

负载因子 HashSet HashMap hashtable集合的默认的负载极限是0.75,超过会成倍的增长

 

8.8操作集合的工具类Collections

java提供了一个操作Set list map集合的工具类Collection,该工具类里提供了大量方法对集合元素进行排序

8.8.3同步控制

Collection类中提供了很多synchronizedXxx()方法,该方法可以将指定的集合包装成线程同步的集合,从而可以解决多线程并发访问集合的线程安全问题

8.9总结

8.9.1List集合的三种遍历方式

1. 增强for

2. 普通forlist.size()

3. 迭代器 Itreator iter = new Itreator; while(iter.hasNext) { iter.next()}

8.8.2 Set集合遍历

1. 增强for

2. 迭代器 Itreator iter = new Itreator; while(iter.hasNext) { iter.next()}

8.8.3 Map集合遍历集合

for-each循环中使用entries来遍历 

for (Map.Entry<Integer, Integer> entry : map.entrySet()) 

entry.getKey()+"--->"+entry.getValue());

 

for-each循环中遍历keysvalues

for (Integer key : map.keySet()) 

使用Iterator遍历

Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator(); 

while (entries.hasNext()) { 

 Map.Entry<Integer, Integer> entry = entries.next(); 

Set entrySet() 返回Map中包含的key-value对所组成的Set集合,每个集合元素都是Map.Entry  Map的内部类  Map集合里的方法

0 0
原创粉丝点击