李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
Java
正文
容器深入研究--使用Abstract类
Leefs
2020-01-09 AM
3933℃
2条
# 容器深入研究--使用Abstract类 ### 前言 本篇将要讲述《Java》编程思想第17.2.3小节,使用Abstract类 ### 概述 对于产生用于容器的测试数据问题,另一个解放方式是创建定制的Collection和Map实现。每个**java.util**容器都有自己的**Abstract**类,它们提供了该容器的部分实现,因此你必须做的只是去实现那些产生想要容器所必需的方法。 **享元模式:**可在普通解决方案需要过多对象,或者产生普通对象太占用空间时使用享元。享元模式使得对象的一部分可以被具体化,因此,与对象中的所有事物都包含在对象内部不同,可以在更加高效的外部表中查找对象的一部分或整体。 ### 示例代码 下面演示通过继承java.util.Abstract来创建定制的Map和Collection。为了创建只读的Map,可以继承Abstractmap并实现entrySet。为了创建只读的Set,可以继承AbstractSet并实现iterator和size。 ```java public class Countries { public static final String[][] DATA = { // Africa {"ALGERIA", "Algiers"}, {"ANGOLA", "Luanda"}, {"BENIN", "Porto-Novo"}, {"BOTSWANA", "Gaberone"}, {"BURKINA FASO", "Ouagadougou"}, {"BURUNDI", "Bujumbura"}, {"CAMEROON", "Yaounde"}, {"CAPE VERDE", "Praia"}, {"CENTRAL AFRICAN REPUBLIC", "Bangui"}, {"CHAD", "N’djamena"}, {"COMOROS", "Moroni"}, {"CONGO", "Brazzaville"}, {"DJIBOUTI", "Dijibouti"}, {"EGYPT", "Cairo"}, {"EQUATORIAL GUINEA", "Malabo"}, {"ERITREA", "Asmara"}, {"ETHIOPIA", "Addis Ababa"}, {"GABON", "Libreville"}, {"THE GAMBIA", "Banjul"}, {"GHANA", "Accra"}, {"GUINEA", "Conakry"}, {"BISSAU", "Bissau"}, {"COTE D’IVOIR (IVORY COAST)", "Yamoussoukro"}, {"KENYA", "Nairobi"}, {"LESOTHO", "Maseru"}, {"LIBERIA", "Monrovia"}, {"LIBYA", "Tripoli"}, {"MADAGASCAR", "Antananarivo"}, {"MALAWI", "Lilongwe"}, {"MALI", "Bamako"}, {"MAURITANIA", "Nouakchott"}, {"MAURITIUS", "Port Louis"}, {"MOROCCO", "Rabat"}, {"MOZAMBIQUE", "Maputo"}, {"NAMIBIA", "Windhoek"}, {"NIGER", "Niamey"}, {"NIGERIA", "Abuja"}, {"RWANDA", "Kigali"}, {"SAO TOME E PRINCIPE", "Sao Tome"}, {"SENEGAL", "Dakar"}, {"SEYCHELLES", "Victoria"}, {"SIERRA LEONE", "Freetown"}, {"SOMALIA", "Mogadishu"}, {"SOUTH AFRICA", "Pretoria/Cape Town"}, {"SUDAN", "Khartoum"}, {"SWAZILAND", "Mbabane"}, {"TANZANIA", "Dodoma"}, {"TOGO", "Lome"}, {"TUNISIA", "Tunis"}, {"UGANDA", "Kampala"}, {"DEMOCRATIC REPUBLIC OF THE CONGO (ZAIRE)", "Kinshasa"}, {"ZAMBIA", "Lusaka"}, {"ZIMBABWE", "Harare"}, // Asia {"AFGHANISTAN", "Kabul"}, {"BAHRAIN", "Manama"}, {"BANGLADESH", "Dhaka"}, {"BHUTAN", "Thimphu"}, {"BRUNEI", "Bandar Seri Begawan"}, {"CAMBODIA", "Phnom Penh"}, {"CHINA", "Beijing"}, {"CYPRUS", "Nicosia"}, {"INDIA", "New Delhi"}, {"INDONESIA", "Jakarta"}, {"IRAN", "Tehran"}, {"IRAQ", "Baghdad"}, {"ISRAEL", "Jerusalem"}, {"JAPAN", "Tokyo"}, {"JORDAN", "Amman"}, {"KUWAIT", "Kuwait City"}, {"LAOS", "Vientiane"}, {"LEBANON", "Beirut"}, {"MALAYSIA", "Kuala Lumpur"}, {"THE MALDIVES", "Male"}, {"MONGOLIA", "Ulan Bator"}, {"MYANMAR (BURMA)", "Rangoon"}, {"NEPAL", "Katmandu"}, {"NORTH KOREA", "P’yongyang"}, {"OMAN", "Muscat"}, {"PAKISTAN", "Islamabad"}, {"PHILIPPINES", "Manila"}, {"QATAR", "Doha"}, {"SAUDI ARABIA", "Riyadh"}, {"SINGAPORE", "Singapore"}, {"SOUTH KOREA", "Seoul"}, {"SRI LANKA", "Colombo"}, {"SYRIA", "Damascus"}, {"TAIWAN (REPUBLIC OF CHINA)", "Taipei"}, {"THAILAND", "Bangkok"}, {"TURKEY", "Ankara"}, {"UNITED ARAB EMIRATES", "Abu Dhabi"}, {"VIETNAM", "Hanoi"}, {"YEMEN", "Sana’a"}, // Australia and Oceania {"AUSTRALIA", "Canberra"}, {"FIJI", "Suva"}, {"KIRIBATI", "Bairiki"}, {"MARSHALL ISLANDS", "Dalap-Uliga-Darrit"}, {"MICRONESIA", "Palikir"}, {"NAURU", "Yaren"}, {"NEW ZEALAND", "Wellington"}, {"PALAU", "Koror"}, {"PAPUA NEW GUINEA", "Port Moresby"}, {"SOLOMON ISLANDS", "Honaira"}, {"TONGA", "Nuku’alofa"}, {"TUVALU", "Fongafale"}, {"VANUATU", "< Port-Vila"}, {"WESTERN SAMOA", "Apia"}, // Eastern Europe and former USSR {"ARMENIA", "Yerevan"}, {"AZERBAIJAN", "Baku"}, {"BELARUS (BYELORUSSIA)", "Minsk"}, {"BULGARIA", "Sofia"}, {"GEORGIA", "Tbilisi"}, {"KAZAKSTAN", "Almaty"}, {"KYRGYZSTAN", "Alma-Ata"}, {"MOLDOVA", "Chisinau"}, {"RUSSIA", "Moscow"}, {"TAJIKISTAN", "Dushanbe"}, {"TURKMENISTAN", "Ashkabad"}, {"UKRAINE", "Kyiv"}, {"UZBEKISTAN", "Tashkent"}, // Europe {"ALBANIA", "Tirana"}, {"ANDORRA", "Andorra la Vella"}, {"AUSTRIA", "Vienna"}, {"BELGIUM", "Brussels"}, {"BOSNIA", "-"}, {"HERZEGOVINA", "Sarajevo"}, {"CROATIA", "Zagreb"}, {"CZECH REPUBLIC", "Prague"}, {"DENMARK", "Copenhagen"}, {"ESTONIA", "Tallinn"}, {"FINLAND", "Helsinki"}, {"FRANCE", "Paris"}, {"GERMANY", "Berlin"}, {"GREECE", "Athens"}, {"HUNGARY", "Budapest"}, {"ICELAND", "Reykjavik"}, {"IRELAND", "Dublin"}, {"ITALY", "Rome"}, {"LATVIA", "Riga"}, {"LIECHTENSTEIN", "Vaduz"}, {"LITHUANIA", "Vilnius"}, {"LUXEMBOURG", "Luxembourg"}, {"MACEDONIA", "Skopje"}, {"MALTA", "Valletta"}, {"MONACO", "Monaco"}, {"MONTENEGRO", "Podgorica"}, {"THE NETHERLANDS", "Amsterdam"}, {"NORWAY", "Oslo"}, {"POLAND", "Warsaw"}, {"PORTUGAL", "Lisbon"}, {"ROMANIA", "Bucharest"}, {"SAN MARINO", "San Marino"}, {"SERBIA", "Belgrade"}, {"SLOVAKIA", "Bratislava"}, {"SLOVENIA", "Ljuijana"}, {"SPAIN", "Madrid"}, {"SWEDEN", "Stockholm"}, {"SWITZERLAND", "Berne"}, {"UNITED KINGDOM", "London"}, {"VATICAN CITY", "---"}, // North and Central America {"ANTIGUA AND BARBUDA", "Saint John’s"}, {"BAHAMAS", "Nassau"}, {"BARBADOS", "Bridgetown"}, {"BELIZE", "Belmopan"}, {"CANADA", "Ottawa"}, {"COSTA RICA", "San Jose"}, {"CUBA", "Havana"}, {"DOMINICA", "Roseau"}, {"DOMINICAN REPUBLIC", "Santo Domingo"}, {"EL SALVADOR", "San Salvador"}, {"GRENADA", "Saint George’s"}, {"GUATEMALA", "Guatemala City"}, {"HAITI", "Port-au-Prince"}, {"HONDURAS", "Tegucigalpa"}, {"JAMAICA", "Kingston"}, {"MEXICO", "Mexico City"}, {"NICARAGUA", "Managua"}, {"PANAMA", "Panama City"}, {"ST. KITTS", "-"}, {"NEVIS", "Basseterre"}, {"ST. LUCIA", "Castries"}, {"ST. VINCENT AND THE GRENADINES", "Kingstown"}, {"UNITED STATES OF AMERICA", "Washington, D.C."}, // South America {"ARGENTINA", "Buenos Aires"}, {"BOLIVIA", "Sucre (legal)/La Paz(administrative)"}, {"BRAZIL", "Brasilia"}, {"CHILE", "Santiago"}, {"COLOMBIA", "Bogota"}, {"ECUADOR", "Quito"}, {"GUYANA", "Georgetown"}, {"PARAGUAY", "Asuncion"}, {"PERU", "Lima"}, {"SURINAME", "Paramaribo"}, {"TRINIDAD AND TOBAGO", "Port of Spain"}, {"URUGUAY", "Montevideo"}, {"VENEZUELA", "Caracas"}, }; // Use AbstractMap by implementing entrySet() private static class FlyweightMap extends AbstractMap
{// 需要实现抽象方法 entrySet() private static class Entry implements Map.Entry
{ int index; Entry(int index) { this.index = index; } public boolean equals(Object o) { return DATA[index][0].equals(0); } @Override public String getKey() { return DATA[index][0]; } @Override public String getValue() { return DATA[index][1]; } @Override public String setValue(String value) { throw new UnsupportedOperationException(); } public int hashCode() { return DATA[index][0].hashCode(); } } // Use AbstractSet by implementing size() & iterator() static class EntrySet extends AbstractSet
> { private int size; EntrySet(int size){ if (size < 0) this.size = 0; else if (size > DATA.length) this.size = DATA.length; else this.size = size; } @Override public Iterator
> iterator() { return new Iter(); } @Override public int size() { return size; } private class Iter implements Iterator
>{ // Only one Entry object per Iterator:每个迭代器只有一个Entry对象 private Entry entry = new Entry(-1); @Override public boolean hasNext() { return entry.index < size -1; } @Override public Map.Entry
next() { entry.index++; return entry; } public void remove(){ throw new UnsupportedOperationException(); } } } private static Set
> entries = new EntrySet(DATA.length); @Override public Set
> entrySet(){ return entries; } } //创建 部分 固定size国家的map static Map
select(final int size){ return new FlyweightMap(){ @Override public Set
> entrySet(){ return new EntrySet(size); } }; } static Map
map = new FlyweightMap(); public static Map
capitals(){ return map;// 整个map } public static Map
capitals(int size){ return select(size);//部分Map } public static List
names(int size){ return new ArrayList
(select(size).keySet()); } public static void main(String[] args) { System.out.println(capitals(3)); System.out.println(names(3)); System.out.println(new HashMap<>(capitals(3))); System.out.println(new LinkedHashMap<>(capitals(3))); System.out.println(new TreeMap<>(capitals(3))); System.out.println(new Hashtable<>(capitals(3))); System.out.println(new HashSet
(names(6))); System.out.println(new LinkedHashSet
(names(6))); System.out.println(new TreeSet<>(names(6))); System.out.println(new ArrayList<>(names(6))); System.out.println(new LinkedList<>(names(6))); System.out.println(capitals().get("BRAZIL")); } } ``` > 运行结果 ```java {ALGERIA=Algiers, ANGOLA=Luanda, BENIN=Porto-Novo} [ALGERIA, ANGOLA, BENIN] {BENIN=Porto-Novo, ANGOLA=Luanda, ALGERIA=Algiers} {ALGERIA=Algiers, ANGOLA=Luanda, BENIN=Porto-Novo} {ALGERIA=Algiers, ANGOLA=Luanda, BENIN=Porto-Novo} {ALGERIA=Algiers, ANGOLA=Luanda, BENIN=Porto-Novo} [BENIN, BOTSWANA, ANGOLA, BURKINA FASO, ALGERIA, BURUNDI] [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI] [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI] [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI] [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI] Brasilia ``` FlyweightMap必须实现entrySet方法,它需要定制set实现和定制Map.Entry类。这里正是享元部分:每个Map.Entry对象都只存储了它的索引,而不是实际的键和值。当你调用getKey和getValue时,它们会使用该索引来返回恰当的DATA元素。EntrySet可以确保它的size不会大于DATA。 可以在EntrySet.Iterator中看到享元其它部分的实现。与为DATA中的每个数据对都创建Map,Entry对象不同,每个迭代器只有一个Map.Entry。Entry对象被用作数据的视窗,它只包含在静态字符数组中的索引。每次调用迭代器的nest方法时,Entry中的index都会递增,使其指向下一个元素,然后从next返回该Iterator所持有的单一Entry对象。 select方法将产生一个包含制定尺寸的EntrySet的FlyweightMap,它会被用于重载过的capitals和names方法。 Countries的尺寸受限会成为问题。可以采用与产生定制容器相同的方法来解决,其中定制容器是经过初始化的,并且具有任意尺寸的数据集。下面的类是一个list,它可以具有任意尺寸,并且用Integer数据进行了预初始化: ```java public class CountingIntegerList extends AbstractList
{ private int size; public CountingIntegerList(int size){ this.size=size<0?0:size; } @Override public Integer get(int index) { return Integer.valueOf(index); } @Override public int size() { return size; } public static void main(String[] args) { System.out.println(new CountingIntegerList(30)); } } ``` > 运行结果 ```java [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29] ``` 当我从看到输出的结果时头上出现了三个大大的问号? 我没有使用迭代器,也没有进行任何遍历操作,怎么输出来的是一个数组。 从我作为小白级别的编程经验来判断,要想寻找答案还得DEBUG调试去看源码 **问题解析** 在代码中通过继承AbstractList抽象类并重写了get()和size()方法,通过这两个方法也不能解释数据的遍历操作, 在debug模式下进行调试时发现`System.out.println(new CountingIntegerList(30))`在创建完对象进行输出时会先主动去调用AbstractCollection中的toString()方法 那么问题又来了,AbstractCollection又是从哪冒出来的? **源码** ```java public abstract class AbstractList
extends AbstractCollection
implements List
{ ``` 抽象类AbstractList继承了AbstractCollection并对其方法中的iterator()方法进行了实现 ```java //AbstractList.java public Iterator
iterator() { return new Itr();//调用成员内部类的构造器 } private class Itr implements Iterator
{ /** * Index of element to be returned by subsequent call to next. */ int cursor = 0;//代表下一次调用next将会返回的元素索引 /** * Index of element returned by most recent call to next or * previous. Reset to -1 if this element is deleted by a call * to remove.如果lastRet索引元素已经被删除,那么lastRet会置为-1 */ int lastRet = -1;//代表上一次调用next或previous返回的元素索引 /** * The modCount value that the iterator believes that the backing * List should have. If this expectation is violated, the iterator * has detected concurrent modification. */ int expectedModCount = modCount;//获得外部类list对象的modCount成员,代表被修改的次数 public boolean hasNext() { return cursor != size();//调用了例子代码重写的size方法。size刚好为最大索引+1,所以等于size时应该返回false } public E next() { checkForComodification(); try { int i = cursor;//既然cursor是下一次next返回的元素索引,那么next里就使用这个索引 E next = get(i);//调用了例子代码重写的get方法。这句是可能抛出IndexOutOfBoundsException的,当然本文例子不会抛出 lastRet = i;//next执行后,cursor就会变成last return cursor = i + 1; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } } public void remove() { if (lastRet < 0)//如果自己已经remove过了,那么再次remove就会抛异常 throw new IllegalStateException(); checkForComodification();//如果别的线程也remove外部类list对象了,那么也会抛出异常 try { AbstractList.this.remove(lastRet);//调用外部类对象的remove方法,但AbstractList里只是默认实现直接抛异常 if (lastRet < cursor) cursor--; lastRet = -1;//删除后就置lastRet为-1 expectedModCount = modCount;//再次获得外部类对象的modCount成员,因为remove后modCount会发生改变 } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount)//比较迭代器自己存的count,和外部类对象存的count throw new ConcurrentModificationException(); } } ``` 发现原来有个成员内部类`Itr`已经实现了Iterator接口,并在`iterator()`方法返回`return new Itr()`,这样每个迭代器对象都会持有一个外部类list对象的引用,而且list对象和迭代器对象的关系是一对多的。 接下来我们再来看一下AbstractCollection中的toString()方法源码 ```java //AbstractCollection.java public String toString() { Iterator
it = iterator();//返回一个迭代器,用Iterator接口来接的 if (! it.hasNext())//如果迭代器刚开始就不能移动 return "[]"; StringBuilder sb = new StringBuilder(); sb.append('['); for (;;) { E e = it.next();//此时已经判断过hasNext可以返回true了 sb.append(e == this ? "(this Collection)" : e);//加入StringBuilder if (! it.hasNext())//如果hasNext可以返回false,那么退出该方法 return sb.append(']').toString(); sb.append(',').append(' ');//如果hasNext可以返回true,那么先提前加好逗号空格 } } ``` 通过上方的源码可以了解到在toString()方法中对输出进行循环了遍历操作。 > 通过上方的简单的小示例就可以想像出这一章的真正威力,和之前的持有对象相比难度大了很多 下面是包含经过预处初始化,并且都是唯一的Integer和String对的Map,它可以具有任意尺寸: ```java public class CountingMapData extends AbstractMap
{ private int size; private static String[] chars = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z".split(" "); public CountingMapData(int size) { if(size < 0) { this.size = 0; } this.size = size; } public Set
> entrySet() { LinkedHashSet entries = new LinkedHashSet(); for(int i = 0; i < this.size; ++i) { entries.add(new CountingMapData.Entry(i)); } return entries; } public static void main(String[] args) { System.out.println(new CountingMapData(60)); } private static class Entry implements java.util.Map.Entry
{ int index; Entry(int index) { this.index = index; } public boolean equals(Object o) { return Integer.valueOf(this.index).equals(o); } public Integer getKey() { return Integer.valueOf(this.index); } public String getValue() { return CountingMapData.chars[this.index % CountingMapData.chars.length] + Integer.toString(this.index / CountingMapData.chars.length); } public String setValue(String value) { throw new UnsupportedOperationException(); } public int hashCode() { return Integer.valueOf(this.index).hashCode(); } } } ``` > 运行结果 ```java {0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0, 9=J0, 10=K0, 11=L0, 12=M0, 13=N0, 14=O0, 15=P0, 16=Q0, 17=R0, 18=S0, 19=T0, 20=U0, 21=V0, 22=W0, 23=X0, 24=Y0, 25=Z0, 26=A1, 27=B1, 28=C1, 29=D1, 30=E1, 31=F1, 32=G1, 33=H1, 34=I1, 35=J1, 36=K1, 37=L1, 38=M1, 39=N1, 40=O1, 41=P1, 42=Q1, 43=R1, 44=S1, 45=T1, 46=U1, 47=V1, 48=W1, 49=X1, 50=Y1, 51=Z1, 52=A2, 53=B2, 54=C2, 55=D2, 56=E2, 57=F2, 58=G2, 59=H2} ``` 这里使用的是LinkedHashSet,而不是定制的Set类,因此享元并未完全实现。
标签:
Java
,
Java编程思想
,
容器深入研究
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
https://lilinchao.com/archives/397.html
上一篇
DockerFile介绍
下一篇
Docker私有仓库
评论已关闭
栏目分类
随笔
2
Java
326
大数据
229
工具
31
其它
25
GO
47
NLP
2
标签云
RSA加解密
GET和POST
线程池
SpringBoot
Elastisearch
哈希表
散列
Linux
Hadoop
ajax
NIO
VUE
数学
并发编程
Spark Streaming
栈
Jquery
Hive
前端
数据结构
Nacos
Http
国产数据库改造
Livy
Azkaban
Flume
LeetCode刷题
Yarn
JavaScript
二叉树
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞
评论已关闭