Java 基础-集合的视图和包装器

  在学习Java的过程中,我们知道,Java中的集合框架是非常重要,在实际的开发过程(笔者是做Android的)中也是非常的实用。在这里,将简单的介绍一下,Java中的视图和包装器。本文不会介绍Java集合的基本使用,只会介绍一下自己觉得比较重要,但是又很少见的东西。

1.视图是什么

  搞过移动开发的朋友,看到视图肯定会想到View。但是我们这里说的不是Java中的awt和swing的View,而是集合中的View。但是这个View在集合中是什么意思呢?
  在Java的集合中,我们可以通过视图(View)获得其他的实现了Collection接口和Map接口的对象。例如,我们在使用Map类的keySet方法可以获得一个Set集合对象。初看起来,这个方法创建一个新的set集合,并且将Map中所有的键都填进去,然后返回这个集合。但是,情况并且如此,keySet方法返回一个实现Set接口的类对象,这个类的方法对原来的Map对象进行操作。这个集合就称为视图(View)

2.轻量级集合包装器

  前面简单的介绍一下是什么是视图,这里将再次的介绍一些视图的具体例子,并且会介绍普通的集合与视图集合有什么区别。

(1). Arrays.asList方法

  在Arrays类中有一个静态方法--asList方法,这个方法作用是:将普通的Java数组包装成一个List集合。例如:

String []strings = new String[10];
strings[0] = "pby";
strings[1] = "pby1";
strings[2] = "pby2";
List<String> stringList = Arrays.asList(strings);

  返回的对象不是一个ArrayList对象。它就是一个视图对象,这个对象带有底层数组的get和set方法。
  那这个视图对象与普通的List或者ArrayList对象有什么区别吗?
  在这里,视图对象不能操作所有改变数组大小的方法(比如说,add方法和remove方法),在调用这些方法的时候,程序会抛出一个UnsupportedOperationException异常;但是普通的List对象能够正常的调用改变数组大小的方法。

(2). Collections.nCopies方法

  与Arrays.asList方法类似的另一个方法那就是在Collection中的nCopies方法。例如:

List<String> stringList = Collections.nCopies(100, "pby");

  上面的代码将创建一个包含100个"pby"字符串的List集合对象。这样的操作优势在于存储代价很小,因为这个对象不能修改大小。这就是视图技术的一种巧妙应用。

3. 子范围

  在Java中,我们还可以给 很多的集合建立子范围视图。例如,假设有一个集合对象list,我们想要从中取出第10个~第19个元素。可以使用subList方法来获得一个List集合对象的子范围视图。例如:

        List<String> list = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            list.add("" + i);
        }
        System.out.println(list);
        //获取第10个~第19个
        List<String> list2 = list.subList(9, 20);
        
        System.out.println(list2);
        System.out.println(list);
        //清空自子范围视图之后,原来的List集合对象相应位置的数据也会被自动清空的
        list2.clear();
        System.out.println(list2);
        System.out.println(list);

4. 不可修改的视图

  Collections还有几个方法,用于产生集合的不可修改视图。这些视图对现有的集合增加了一个运行时的检查。如果发现对集合进行修改的话(这里不仅仅是改变数组的大小,并且包括set之类的方法),就会抛出一个异常,同时这个集合将保持未修改的状态。
  可以使用如下8种方法来获得不可修改的视图:

  1. Collections.unmodifiableCollection
  2. Collections.unmodifiableList
  3. Collections.unmodifiableSet
  3. Collections.unmodifiableSortedSet
  5. Collections.unmodifiableNavigableSet
  6. Collections.unmodifiableMap
  7. Collections.unmodifiableSortedMap
  8. Collections.unmodifiableNavigableMap

  每个方法都定义于一个接口。例如,Collections.unmodifiableList方法定义于List接口,与ArrayList、LinkedList或者任何实现了List接口的其他类一起协同工作。
  例如,假设想要查看某个集合的内容,但是又能避免这个集合会被修改的情况,就可以进行下列的操作:

        LinkedList<String> list = new LinkedList<>();
        list.add("pby");
        list.add("pby2");
        List<String> list2 = Collections.unmodifiableList(list);
        //是不能被修改的
        //list2.set(0, "dasdas");

  Collections.unmodifiableList方法将返回一个List集合的对象(视图对象),我们可以从这个视图对象中取得内容,但是不能修改,因为在个视图对象中,所有修改类型的方法已经被重新定义为一个抛出UnsupportOperationException的异常,而不是将方法的调用传递给底层集合对象(这里底层集合对象指的就是当前List集合对象的实际类型对象,这种调用方式是由于Java的多态性导致的)。
  但是我们这里需要注意的是,不可更改的视图对象并不是指集合本身不可修改,我们仍然可以通过集合原来的引用来(犹如上面例子中的list)对集合进行修改。同时,如果原来的引用修改了集合,那么视图对象的内容也是跟着变化的。

问题:这个有一个疑惑,笔者也是没有搞清楚。unmodifiableList方法将返回一个List集合的对象,它的equals方法不会调用底层集合的equals方法,相反的是,List继承于Object类,因此调用的是Object类中的equals方法,来检测这两个对象是否是同一个对象。

说了这么多,其实想表达的意思就是:通过unmodifiableList方法获得视图对象,在调用equals方法来比较两个对象,是比较两个对象是同一个对象,相当于是==的作用。

意思是非常的简单,但是如下代码:

        LinkedList<String> list = new LinkedList<>();
        list.add("pby");
        list.add("pby2");
        List<String> list2 = Collections.unmodifiableList(list);
        
        System.out.println(list.equals(list2));
        System.out.println(list == list2);

  第一个输出的是true,第二个输出的是false。按道理来说,这里的equals方法是判断两个引用是否是指向同一个,返回的是true;而==的作用也是如此--判断两个引用是否是指向同一个对象。但是这里的结果却是不一样,这里就不是很好的理解了。如果有朋友懂的话,麻烦请解释一下,谢谢!

5. 同步视图

  如果多个线程访问集合,就确保集合不会被意外的破坏。例如,如果一个线程视图将元素添加到Hash表中,同一个另一个线程正在对Hash表进行再散列,这种操作的结果是灾难性的。
  但是我们使用视图机制来确保常规集合的线程安全,而不是实现线程安全的集合类。例如,Collections类的静态方法synchronizedMap方法可以将任何一个映射表转换成为具有同步访问方法的Map:

Map<String, String> map = Collections.synchrizedMap(new HashMap<String, String>());

  现在,就可以自由的使用多线程来访问map对象了。像get和put这类方法都是同步操作的,即在另一个线程中调用另一个方法之前,刚才的方法调用必须彻底执行完毕。

6. 受查视图

  受查视图是用来对泛型类型发生问题时,提供调试的支持。

        List<String> list = new ArrayList<>();
        List list2 = list;
        //程序运行到这里是不会报错的,但是如果后面访问这里元素,
        //并且试图强制转换为String类型的变量才会抛出一个ClassCastException的异常
        list2.add(10);
        //这里会抛出ClassCastException异常
        //String string = (String) list2.get(0);
        

  例如,上面的例子,先创建了一个List<String>类型的对象,再将它的泛型类型擦除,变成了List类型,由于泛型类型被擦除,原来的泛型类型就会被Object代替,所以我们在list2中添加一个整数(Integer)类型是不会有问题的,同时程序运行到add方法那里也不会报错的。也就是说,程序的编译时和运行时的错误,我们都成功的越过去了。但是在将添加进去的那个元素强制转成为String类型,就会抛出ClassCastException的异常。
  ,如果我们使用受查视图的话,例如:

        List<String> list3 = Collections.checkedList(list, String.class);
        List list4 = list3;
        //程序运行到这里就会抛出一个ClassCastException的异常
        list4.add(10);

  虽然在编译时,程序是没有报错的,但是程序一旦运行到add方法那里直接会抛出一个ClassCastException的异常。也就是说,通过受查视图,可以逃避编译时的检查,但是躲不过运行时的检查!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,640评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,254评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,011评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,755评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,774评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,610评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,352评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,257评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,717评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,894评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,021评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,735评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,354评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,936评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,054评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,224评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,974评论 2 355

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,639评论 18 399
  • 翻译自“Collection View Programming Guide for iOS” 0 关于iOS集合视...
    lakerszhy阅读 3,866评论 1 22
  • 集合框架: 1)特点:存储对象;长度可变;存储对象的类型可不同2)Collection(1)List:有序的;元素...
    Demo_Yang阅读 1,261评论 0 4
  • 台风混杂夏日的烦躁,吹来关于秋天的预告。 我沉浸在热火朝天的夏日,却不知早已是多事之秋 有些故事风起叶落,有些人过...
    几个啥阅读 123评论 0 0
  • 凡世光阴静谧中, 坐禅内观始知风。 优人神鼓心头落, 苏菲旋槃灵性空。 注:优人神鼓是台湾的禅鼓表演节目,苏菲旋舞...
    飞雪姐姐阅读 309评论 0 2