初來乍到 Java 和 .Net 迭代器功能
最近有一個需求是這樣的,
根據鍵值對儲存型別資料,也算是資料快取塊模組功能設計。
一個鍵對應多個值。每一個鍵的值型別相同,但是每個不同的鍵之間型別不一定相同。
Java 設計如下
HashMap<String, ArrayList<Object>>
java把資料新增到集合中
TestIterator tIterator = new TestIterator(); ArrayList<Object> objs = new ArrayList<>(); objs.add("sdfsfdsfdsf"); objs.add("sdfsfdsfdsf"); objs.add("sdfsfdsfdsf"); objs.add("sdfsfdsfdsf"); tIterator.getList().put("Key1", objs); objs = new ArrayList<>(); objs.add(1); objs.add(2); objs.add(3); objs.add(4); tIterator.getList().put("Key2", objs); objs= new ArrayList<>(); objs.add(new String[]{"1", ""}); objs.add(new String[]{"2", ""}); objs.add(new String[]{"3", ""}); objs.add(new String[]{"4", ""}); tIterator.getList().put("Key3", objs);
新增進資料快取後,然後讀取資料,我們先忽略,快取集合的執行緒安全性問題,
{ ArrayList<Object> getObjs = tIterator.getList().get("Key1"); for (Object getObj : getObjs) { System.out.println("My is String:" + (String) getObj); } } { ArrayList<Object> getObjs = tIterator.getList().get("Key2"); for (Object getObj : getObjs) { System.out.println("My is int:" + (int) getObj); } } { ArrayList<Object> getObjs = tIterator.getList().get("Key3"); for (Object getObj : getObjs) { String[] strs = (String[]) getObj; System.out.println("My is String[]:" + strs[0] + " : " + strs[1]); } }
我們發現。使用的時候,每個地方都需要轉換。
(String[]) getObj; (int) getObj (String) getObj
同樣程式碼需要重複寫,那麼我們是否可以封裝一次呢?
public <T> ArrayList<T> getValue(String keyString, Class<T> t) { ArrayList<T> rets = new ArrayList<>(); ArrayList<Object> getObjs = _List.get(keyString); if (getObjs != null) { for (Object getObj : getObjs) { //if (getObj instanceof T) { rets.add((T) getObj); //} } } return rets; }
這裡我發現一個問題,不支援泛型檢查,據我很淺的知識瞭解到,java算是動態型別資料。
並且是偽泛型型別所以不支援泛型型別判定
這點很不爽了,為啥不能泛型型別判定。也許是我知識淺薄~!望前輩指點;
再次檢視呼叫
{ ArrayList<String> value = tIterator.getValue("Key1", String.class); for (String value1 : value) { } } { ArrayList<Integer> value = tIterator.getValue("Key1", Integer.class); for (Integer value1 : value) { } } { ArrayList<String[]> value = tIterator.getValue("Key1", String[].class); for (String[] value1 : value) { } }
稍稍覺得清爽了一點吧。當然,我這裡都是用到基礎型別,如果用到複雜型別,和滿篇呼叫的時候才能體現出這段程式碼的優越性。
更加的符合面向物件程式設計的重構行和複用性;
可是上面程式碼,不曉得大家注意沒,出現一個問題,那就是每一次呼叫都再一次的聲明瞭
ArrayList<T> rets = new ArrayList<>();
物件,如果是需要考慮效能問題的時候,我們肯定不能不能這樣。每次呼叫都需要重新分配ArrayList的記憶體空間。並且在 ArrayList.add() 的時候每一次都在檢查ArrayList的空間夠不夠,不夠,再次開闢新空間。重組。
雖然這個動作很快,可是如果我們快取的資料過多。那麼情況可就不一樣了。且伴隨著每一次的呼叫都是一個消耗。訪問次數過多的話。那麼程式的的效能勢必會變的低下。
再次考慮,是否可以用迭代器實現功能呢?
查看了一下迭代器實現方式,我無法完成我需求的迭代器功能。只能依葫蘆畫瓢,實現了一個自定義的迭代器功能。
class TestIterator { HashMap<String, ArrayList<Object>> _List = new HashMap<>(); public TestIterator() { } public <T> ArrayList<T> getValue(String keyString, Class<T> t) { ArrayList<T> rets = new ArrayList<>(); ArrayList<Object> getObjs = _List.get(keyString); if (getObjs != null) { for (Object getObj : getObjs) { //if (getObj instanceof T) { rets.add((T) getObj); //} } } return rets; } public HashMap<String, ArrayList<Object>> getList() { return _List; } public void setList(HashMap<String, ArrayList<Object>> _List) { this._List = _List; } public <T> TestIterator.ArrayIterator<T> iterator(String keyString, Class<T> t) { return new ArrayIterator<T>(keyString); } public class ArrayIterator<T> { private String key; int index = -1; private T content; public ArrayIterator(String key) { this.key = key; } public void reset() { index = -1; } public T getContent() { //忽略是否存在鍵的問題 Object get = TestIterator.this._List.get(key).get(index); return (T) get; } public boolean next() { //忽略是否存在鍵的問題 if (index >= TestIterator.this._List.get(key).size()) { reset(); return false; } index++; return true; } } }
呼叫方式
{ TestIterator.ArrayIterator<String> iterator1 = tIterator.iterator("Key1", String.class); while (iterator1.next()) { String content = iterator1.getContent(); } } { TestIterator.ArrayIterator<Integer> iterator1 = tIterator.iterator("Key2", Integer.class); while (iterator1.next()) { Integer content = iterator1.getContent(); } } { TestIterator.ArrayIterator<String[]> iterator = tIterator.iterator("Key3", String[].class); while (iterator.next()) { String[] content = iterator.getContent(); } }
總結了一些問題,
Java的泛型是偽泛型,底層其實都是通過object物件,裝箱拆箱完成的。
/** * Shared empty array instance used for empty instances. */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. */ transient Object[] elementData; // non-private to simplify nested class access
這個從我目前程式碼設計思路我能理解。如果讓我自己設計。也行也會設計如此。
但是無法理解為什麼使用泛型無法型別判定;
我這個自定義的迭代器無法使用 for each 功能;
說了這麼多。接下來我們看看.net;
C# 設計如下
Dictionary<String, List<Object>>
TestIterator tIterator = new TestIterator(); List<Object> objs = new List<Object>(); objs.Add("sdfsfdsfdsf"); objs.Add("sdfsfdsfdsf"); objs.Add("sdfsfdsfdsf"); objs.Add("sdfsfdsfdsf"); tIterator["Key1"] = objs; objs = new List<Object>(); objs.Add(1); objs.Add(2); objs.Add(3); objs.Add(4); tIterator["Key2"] = objs; objs = new List<Object>(); objs.Add(new String[] { "1", "" }); objs.Add(new String[] { "2", "" }); objs.Add(new String[] { "3", "" }); objs.Add(new String[] { "4", "" }); tIterator["Key3"] = objs;
由於有了以上 Java 部分的程式碼和思路,那麼我們直接建立自定義迭代器就可以了;
public class TestIterator : Dictionary<String, List<Object>> { public IEnumerable<T> CreateEnumerator<T>(String name) { if (this.ContainsKey(name)) { List<Object> items = this[name]; foreach (var item in items) { if (item is T) { Console.WriteLine(item); yield return (T)item; } } } } }
檢視呼叫方式
foreach (var item in tIterator.CreateEnumerator<String>("tt1")) { Console.WriteLine(item + "艹艹艹艹"); }
輸出結果:
yield return : sdfsfdsfdsf foreach : sdfsfdsfdsf yield return : sdfsfdsfdsf foreach : sdfsfdsfdsf yield return : sdfsfdsfdsf foreach : sdfsfdsfdsf yield return : sdfsfdsfdsf foreach : sdfsfdsfdsf
檢視對比一下呼叫方式
foreach (var item in tIterator.CreateEnumerator<String>("Key1")) { Console.WriteLine("foreach : " + item); } Console.WriteLine("===============分割線=============="); IEnumerable<String> getObjs = tIterator.CreateEnumerator<String>("Key1").ToList(); foreach (var item in getObjs) { Console.WriteLine("foreach : " + item); }
主要上面的兩張呼叫方式。輸出結果完全不同
yield return : sdfsfdsfdsf foreach : sdfsfdsfdsf yield return : sdfsfdsfdsf foreach : sdfsfdsfdsf yield return : sdfsfdsfdsf foreach : sdfsfdsfdsf yield return : sdfsfdsfdsf foreach : sdfsfdsfdsf ===============分割線============== yield return : sdfsfdsfdsf yield return : sdfsfdsfdsf yield return : sdfsfdsfdsf yield return : sdfsfdsfdsf foreach : sdfsfdsfdsf foreach : sdfsfdsfdsf foreach : sdfsfdsfdsf foreach : sdfsfdsfdsf
可以看出第二種呼叫方式是全部返回了資料,那麼就和之前java的設計如出一轍了.
public <T> ArrayList<T> getValue(String keyString, Class<T> t) { ArrayList<T> rets = new ArrayList<>(); ArrayList<Object> getObjs = _List.get(keyString); if (getObjs != null) { for (Object getObj : getObjs) { //if (getObj instanceof T) { rets.add((T) getObj); //} } } return rets; }
雖然表面看上去沒有宣告List物件,可實際底層依然做好了底層物件的分配對效能也是有所消耗;
C# 迭代器實現了泛型型別判定檢測;
且沒有多餘的開銷,
總結。
Java的自定義迭代。多出了一下定義
private String key; int index = -1; private T content;
不支援泛型的型別判別;
C# 的自定義迭代器 沒什麼多餘的程式碼開銷,但其實底層依然做了我們類使用Java的自定義程式碼段。只是我們無需再定義而已。
C# 支援 泛型型別的判別。
其實這些都是語法糖的問題。沒有什麼高明或者不高明之處。但是在面對快速開發和高效能程式的基礎上,優勢劣勢。自己判別了。
以上程式碼不足之處,還請各位看客之處。
不喜勿碰~!~!~!