java集合(3) 集合视图与包装器

        以下的内容来自https://blog.csdn.net/pacosonswjtu/article/details/50333509,同样是来自 core java volume 1,所以我就转过来,再次熟悉一遍,也方便以后的查看和学习

(一)集合数据结构回顾

基本 类型 实现接口 说明
List 链表LinkedList Deque,List,Queue 通过存放前后结点的引用,实现双向链表
数组列表ArrayList List,RandomAccess 数据传入动态数组中,自动扩充数组大小
Set 散列集HashSet Set 哈希法存储数据,无序但查找时效率高
树集TreeSet NavigableSet,Set,SortedSet 按照一定方法排序,输出有顺序的集合
Queue 优先级队列PriorityQueue Queue 按照堆排序的方法排序的队列树
双端队列ArrayDeque Deque, Queue 可以在两端添加和删除,不能操作中间的队列
Map 散列表HashMap Map 用哈希法存放的,键值映射的表
树表TreeMap Map,NavigableMap,SortedMap 将键按照一定方法排序的表
注:前六种(不包括Map)都实现了Collection和Iterator接口,因为篇幅限制没有写出。

1.1)通过使用视图可以获得其他的实现了集合接口和映射表接口的对象;

  • 1.1.1)视图定义:映射表类的keySet方法就是这样一个荔枝;keySet 方法返回一个实现了Set接口的类对象, 这个类的方法对原映射表进行操作, 这种集合称为视图;

1.2)集合框架中的遗留类图概览 
这里写图片描述 
1.3)轻量级集包装器

  • 1.3.1)Arrays类的静态方法asList 将返回一个包装了普通 java 数组的List 包装器。 这个方法可以将数组传递给一个期望得到列表或集合变元的方法, 例如:
Card[] cardDeck = new Card[25];
List<Card> cardList = Arrays.asList(cardDeck); (数组转List视图对象)

对以上代码的分析(Analysis):

  • A1)返回的对象不是 ArrayList。 它是一个视图对象, 带有访问底层数组的get和set方法;而改变该数组大小的所有方法(与迭代器相关的add 和 remove方法) 都会抛出一个UnsupportedOperationException 异常;
  • A2)asList方法: 它已经被声明为一个具有可变参数的方法, 除了可以传递一个数组外, 还可以将各个元素直接传递给这个方法:
List<String> names =  Arrays.asList<"a", "b", "c">;//(1)该方法不适用于基本数据类型(byte,short,int,long,float,double,boolean)//(2)该方法将数组与列表链接起来,当更新其中之一时,另一个自动更新
//(3)不支持add和remove方法
  • A3)以上这个方法调用 Collections.nCopies(n , anObject) : 将返回一个实现了 List接口的不可修改对象, 并给人一种包含n个元素, 每个元素都是 Object的 错觉;
  • 1.3.2)看个荔枝:(干货——创建了 100个 DEFAULT, 由于字符串对象只存储了一次, 所以付出的存储代价很小, 这是视图技术的一种巧妙的应用) 
    这里写图片描述 
    这里写图片描述
List<String> settings = Collections.nCopies(100, "DEFAULT"); 

干货问题:(如何区分 Collection 和 Collections)

  • Anotation) Collections 类包含很多实用方法, 这些方法的参数和返回值都是集合。 不要将 Collection接口 和 Collections 包装类 混淆起来了;

  • 1.3.3)调用以下代码: Collections.singleton(anObject); 则返回一个视图对象。 这个对象实现了Set 接口。 返回的对象实现了一个不可修改的单元素集, 而不需要付出建立数据结构的 开销。 singletonList 和 singletonMap 方法类似; 

    1.4) 子范围

  • 1.4.1)可以为很多集合建立子范围视图; 
    如, List g2 = staff.subList(10,20); 从列表staff 中取出 第10个~第19个元素;(第一个 索引包含在内,第二个索引不包含在内)

  • 1.4.2)可以将任何操作应用到子范围, 并且能够自动地反应整个列表的情况; 
    如, g2.clear(); 现在, 元素自动地从 staff 列表中清除了,并且g2 为空;
  • 1.4.3)对于有序集合映射表, 可以使用 排序顺序而不是元素位置建立子范围。

  • 1.4.3.1)SortedSet 接口说明了3个方法:

SortedSet<E> subSet(E from ,E to)
SortedSet<E> headSet(E to)
SortedSet<E> tailSet(E from)
  • 以上方法将返回 大于等于from 小于to的所有元素子集;
  • 1.4.3.2)有序映射表有类似方法:
SortedSet<E> subMap(E from ,E to)
SortedSet<E> headMap(E to)
SortedSet<E> tailMap(E from)
  • 返回映射表视图, 该映射表包含键落在指定范围内的所有元素; 
    这里写图片描述
  • 1.4.4) java SE6 引入的 NavigableSet 接口 赋予子范围操作更多 的控制能力。 可以指定是否包括边界:
NabigableSet<E> subSet(E from, boolean fromInclusive, E to, boolean toInclusive);
NabigableSet<E> headSet(E to, boolean toInclusive);
NabigableSet<E> tailSet(E from, boolean fromInclusive);

这里写图片描述
1.5)不可修改视图

  • Collections 还有几个方法, 用于产生集合的不可修改视图。 这些视图对现有集合增加了一个运行时的检查。 如果发现试图对集合进行修改, 就抛出一个异常, 同时这个集合将保持未修改状态; 
    (再次提醒:注意区分 Collection 和 Collections)
  • 1.5.1)可以使用下列6种方法获得不可修改视图:
Collections.unmodifiableCollection
Collections.unmodifiableList
Collections.unmodifiableSet
Collections.unmodifiableSortedSet
Collections.unmodifiableMap
Collections.unmodifiableSortedMap
  • 每个方法都定义于一个接口。如, Collections.unmodifiableList 与 ArrayList、LinkedList 或者任何实现了 List接口的其他类一起协同工作; 
    这里写图片描述

  • 1.5.1荔枝):

List<String> staff = new LinkedList<>();
lookAt(Collections.unmodifiableList(staff)); // 返回一个不可修改视图, 传递给 loopAt方法;
  • Collections.unmodifiableList 方法返回一个实现List接口的类对象。当然,lookAt方法 可以调用 List 接口中的所有方法, 而不只是访问器。但是所有的更改器方法,已经被重新定义为 抛出一个 UnsuportedOperationException 异常,而不是 将调用传递给底层集合;
  • 1.5.2)不可修改视图并不是 集合本身不可修改 (干货——只是无法通过其投影出的视图修改原始集合)。 仍然可以通过集合的原始引用对集合进行修改。并且仍然可以让集合的元素调用更改方法;
  • 1.5.3)由于视图只是包装了 接口而不是 实际的集合对象, 所以只能访问 接口中定义的方法; 如, LinkedList 类有一些非常方便的方法, addFirst 和 addLast , 它们都不是 List接口的方法,不能通过修改视图进行访问;

Warning)

  • W1) unmodifiableCollection 方法将返回一个集合, 它的equals 方法不调用底层集合的equals 方法; 相反,它继承了 Object 类的 equals 方法, 该方法只是检测两个对象是否是同一个对象;
  • W2)如果将 集 或 列表转换成集合, 就再也无法检测其内容是否相同了, 视图就是以这种方式运行的, 因为内容是否相等的检测在分层结构的这一层上没有定义妥当;
  • W3)视图将以同样的方式处理 hashCode 方法;

1.6)同步视图

  • 1.6.1)如果由多个线程访问集合,就必须确保集不会被意外破坏, 类库的设计者使用视图及直接来确保常规集合的线程安全。
  • 1.6.2)例如, Collections 类的 静态 synchronizedMap 方法将任何一个映射表转换成具有同步访问方法的 Map:
Map<String, Employee> map = Collections.synchronizedMap(new HashMap<String, Employee>());

1.7)检查视图

  • 1.7.1)出现的问题:
ArrayList<String> strings = new ArrayList<>();
ArrayList rawList = strings;
rawList.add(new Date());//
  • 这个错误的 add 命令在运行时检测不到。 相反,只有在 调用 get方法, 并将其 转换为 String 时,这个类才会抛出一个异常; 
    这里写图片描述

  • 1.7.2)解决方法:检查视图可以探测到这类问题。下面定义了一个安全列表: 
    List safeString = Collections.checkedList(strings, String.class); 
    视图的add 方法将检测 插入的对象是否属于给定的类。 如果不属于给定的类, 就立即抛出一个 ClassCastException。 这样做的好处是错误可以在正确的位置得以报告:

ArrayList rawList = safeString;
rawList.add(new Date()); // checked list chrows a ClassCastException

这里写图片描述 
Warning)

  • W1)被检测视图受限于 虚拟机可以运行的运行时检查。例如, 对于ArrayList


猜你喜欢

转载自blog.csdn.net/qq_35583089/article/details/80346862