李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
Java
正文
容器深入研究--使用Abstract类
Leefs
2020-01-09 AM
3482℃
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 条评论
茂林
还有一周到家
回复
2020-01-10 09:41
世纪风
@茂林
加油,熬过这一周
回复
2020-01-11 23:46
栏目分类
随笔
2
Java
326
大数据
229
工具
31
其它
25
GO
47
标签云
Kafka
Yarn
Zookeeper
Git
高并发
BurpSuite
并发线程
Elastisearch
nginx
LeetCode刷题
二叉树
Golang基础
Spark Streaming
Shiro
Java
Linux
Beego
数据结构
国产数据库改造
ajax
设计模式
SpringBoot
数学
递归
Typora
Python
Netty
散列
栈
MyBatis-Plus
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞
还有一周到家
加油,熬过这一周