javaコレクション(1) - データ構造の詳細な説明: http://www.php.cn/java-article-354226.html
フレームワークはクラスのセットを指します。多くのスーパー クラスとインターフェイス、多くの高度なメカニズム、機能、戦略がこれらのスーパー クラスに実装されています。フレームワークのユーザーは、これらの基本メカニズムを再作成することなく、サブクラスを作成してスーパークラスを実装および拡張できます。私たちが日常業務で使用するテクノロジーは基本的にフレームワークであり、それらのパッケージを使用したり、それらの機能を呼び出すときは、このフレームワークの概念を使用します。コレクション (1) でコレクションのデータ構造を分析した後、今日は引き続きコレクションのフレームワークについて説明します。
Basic | type | 実装インターフェース | description |
---|---|---|---|
List | LinkedList | Deque、List、Queue | 前後を収納することでノード参照、二重リンクリスト実装 |
ArrayList | List、RandomAccess | データを動的配列に渡し、配列サイズを自動拡張 | |
Set | HashSet | Set | ハッシュメソッドStoreデータ、順序付けされていないが検索時に効率的 |
TreeSet | NavigableSet、Set、SortedSet | 特定の方法に従ってソートし、順序付きセットを出力 | |
Queue | Priority QueuePriorityQueue | Queue | に従ってソートされたキューツリーヒープへのソート方法 |
両端キュー ArrayDeque | Deque、Queue | は両端で追加・削除可能ですが、真ん中のキューは操作できません | |
Map | ハッシュテーブルHashMap | Map | ハッシュ方式で格納されたキーと値のマッピングのテーブル |
ツリーテーブルTreeMap | Map、NavigableMap、SortedMap | キーを特定の方法でソートするテーブル | |
注: 最初の 6 種類 ( (Map を除く) はすべて Collection インターフェイスと Iterator インターフェイスを実装していますが、スペースの制限により書き出されません。 |
コレクション クラス ライブラリでは、クラスを実装するインターフェイスの構築に非常に大きな割合を使用しましたが、これらのインターフェイスは何に使用されるのでしょうか?実装クラスのインターフェースにメソッドを直接書いても同じ効果が得られるのではないでしょうか?実際の状況は私たちが思っているほど単純ではありません。これらのインターフェイスを介して操作する必要がある複雑なことがたくさんあります。たとえば、特定の クラスのパブリック インターフェイス タイプのオブジェクト を ビュー オブジェクト と呼びます。これらのオブジェクトの型は特定の実装クラスではなく、一部の実装クラスのパブリック インターフェイスです。ビューには多くの機能があり、主なものは次のとおりです:
メソッドを通じて通常の配列を List 型のビュー オブジェクトにラップできます。このビュー オブジェクトのタイプは List ですが、List はインターフェイスでありインスタンス化できないため、奇妙に聞こえるかもしれません。しかし、これがビュー オブジェクトの利点です。このビュー オブジェクトは、一般的な ArrayList や LinkedList などの特定のメソッドを通じて List インターフェイスを実装する任意のコレクション クラスに割り当てることができるため、通常のクラスをラップすることができます。コレクションクラスの目的。
package SetViews;import java.util.Arrays;import java.util.Collections;import java.util.LinkedList;/** * * @author QuinnNorris * 视图是一个轻量级的集包装器,下面有两个例子 */public class Views { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub //-----------------------例1---------------------- int[] arr = new int[10]; //创建一个普通的int数组 Arrays.asList(arr); //通过asList方法,我们将一个普通的数组转换成List类型的视图对象。 //-----------------------例2----------------------- List<String> ll = new LinkedList<>(); //创建一个LinkedList对象ll。 ll.addAll(Collections.nCopies(10, "ok")); //nCopies是Collections类的静态方法,可以将“ok”复制10次,并返回一个List类型的视图对象。 //在这里我们不可以直接把Collections.nCopies的类型强转成LinkedList,因为语法不能上转下 //我们通过addAll方法将这个内容填充到LinkedList对象ll中。 ll.set(2, "no"); //我们看看它是否能正常工作,我们试着将第三个链表中的String换成no。 //请注意,链表最好不要使用get和set方法,这里只是为了实验。 for(String s : ll){ System.out.println(s); } //输出结果第三个是no,其他全是ok,没有任何问题。 } }
これは、ビューの大きな利点の 1 つです。これは、配列をコレクション クラス ライブラリのクラス オブジェクトにラップする軽量の配列ラッパーです。同様に、これらのキーと値のペアとセットに保存する必要があるデータをビューを通じてラップし、対応するクラスに渡すことができます。したがって、このビューの関数を軽量セット ラッパーと呼びます。
これらのサブ範囲が変更された場合、これらのサブ範囲は、元のコレクションへの参照になります。元のコレクションが影響を受けます。影響を与えたくない場合は、addAll などの他のメソッドを使用してコピーする必要があります。リストの場合、例の範囲を分割する方法は簡単です。
package SetViews;import java.util.ArrayList;import java.util.Collections;import java.util.List;/** * * @author QuinnNorris * 在列表中分例子范围,并进行操作 */public class SubViews { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub List<Integer> al = new ArrayList<>(); //创建ArrayList对象al al.addAll(Collections.nCopies(15, 1)); //在al中设置15个1 List subal = al.subList(5, 10); //subList取出从第6个元素到第10个元素作为一个子范围,返回一个视图对象,我们将它存放到subal中。 subal.set(0, 0); //第6个数字改成0 for(Integer i :al) System.out.println(i); //除了第6个数字为0其他全为1 subal.clear();//清除子范围 for(Integer i :al) System.out.print(i); //al列表中只有10个1,中间的五个被清除了 } }
これに加えて、他のソートセットやマッピングテーブルでは、要素の位置ではなくソート順を使用して部分範囲を作成できます。これらのメソッドは、SortedSet および SortedMap で宣言されます。
SortedSet1a4db2c2c2313771e5742b6debf617a1headSet(E toElement)
要素が厳密に toElement より小さいこのセットの部分ビューを返します。SortedSet
サブセット(E fromElement, E toElement)
fromElement (これを含む) から toElement (これを含まない) までの要素を含むこのセットの部分ビューを返します。SortedSet1a4db2c2c2313771e5742b6debf617a1 tailSet(E fromElement)
要素が fromElement 以上であるこのセットの部分ビューを返します。
上記の 3 つのメソッドは、SortedSet で宣言されたメソッドであり、TreeSet で使用できます。同様に、メソッド内のすべての Set が Map に置き換えられる場合、TreeMap でこれら 3 つの同様のメソッドを使用できます。Map 内のすべての操作は値ではなくキーを使用して実行されることに注意してください。
実際、同僚のメソッドにコレクションオブジェクトを渡して、同僚があなたのオブジェクトを誤って変更したことがわかったら、間違いなく問題が発生します。では、この種の問題を解決し、データ構造をより安全にするにはどうすればよいでしょうか?ビューでは、変更不可能なビューを使用してオブジェクトをロックできます。コレクションを変更しようとすると、例外がスローされ、コレクションは未変更の状態に復元されます。
Collections クラスには、変更不可能なビューを取得するためのいくつかのメソッドが提供されています。
unmodifiableCollection : 返回指定 collection 的不可修改视图(下同)。 unmodifiableList unmodifiableMap unmodifiableSet unmodifiableSortedMap unmodifiableSortedSet
上記のメソッドは非常にシンプルで明確です。これらのメソッドがどのように機能するかを確認するために、具体的な例を見てみましょう。の。
package SetViews;import java.util.ArrayList;import java.util.Collections;import java.util.List;/** * * @author QuinnNorris * 不可修改的安全视图举例 */public class UnmodifaiableViews { //这个是调用列表的方法 private static void changeList(List<String> al) { al.set(0, "change"); //error Exception in thread "main" java.lang.UnsupportedOperationException } public static void main(String[] args) { // TODO Auto-generated method stub List<String> al = new ArrayList<>(); //创建ArrayList对象al al.addAll(Collections.nCopies(10, "final")); //前十个元素填充“final”字符串 changeList(al); //先直接调用changeList方法,没问题 changeList(Collections.unmodifiableList(al)); //报错,说明在changeList中对al进行了修改,被阻止了,而且抛出了异常 } }
変更不可とは、コレクション オブジェクトが変更できなくなったことを意味するのではなく、コレクションの静的メソッドを適用するときに変更できないことを意味します。ただし、これには暗黙的な問題があります。静的メソッドを使用すると、その型がビュー型になり、より詳細なメソッドが使用できなくなるからです。たとえば、ArrrayList オブジェクトが Collections.unmodifiableList() によってパッケージ化されると、その型が List に変更され、一部の ArrayList 固有のメソッドが使用できなくなることに注意してください。
日常業務では複数のスレッドが使用されることがよくありますが、コレクションが誤って破棄されないようにする必要があります。ここではマルチスレッドの問題についてはあまり議論したくありません。コレクション クラス ライブラリでは、設計者はビュー メカニズムを使用して、通常のコレクションのスレッド セーフを確保します。たとえば、Collections クラスの静的メソッド synchronizedMap は、パラメーターとして渡された Map 実装クラスのオブジェクトをスレッドセーフな Map に変換できます。
Map<String,Integer> map = Collections.synchronizedMap(new HashMap<String,Integer>());
3 番目のポイントの変更不可能なビューと同様のメソッドが多数あるため、ここでは説明しません。
有的时候,我们在使用类似ArrayList等数据结构时并不使用泛型,从而会导致一些难以发现的类型错误。这个时候,如果我们使用了视图机制,用一种方法来不断地检查我们的代码就可以直接判断出错误的原因。
package SetViews;import java.util.ArrayList;import java.util.Collections;import java.util.Date;import java.util.List;/** * * @author QuinnNorris * 动态类型安全视图是如何避免错误的发生举例 */public class CheckViews { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub //--------------------发生错误的例子----------------------- ArrayList<String> al = new ArrayList<>(); //创建ArrayList对象al ArrayList errList = al; //声明一个没有泛型的ArrayList,并把al赋值给它 errList.add(new Date()); //这个列表本应该是String类型的,但是现在增加一个Date类型数据在编译运行时都不报错。 //只有当对内部的数据进行操作时,编译器才有可能发现这个错误并抛出异常 //-----------------动态类型安全视图机制---------------------- List<String> safeAl = Collections.checkedList(al, String.class); //调用Collections的静态方法checkedList,第一个参数是列表名,第二个参数是类型 //这个方法返回一个List视图对象,如果要进行错误的操作,会抛出异常 List errSafeAl = safeAl; //再用无泛型的列表去引用检查视图的对象,因为al原本是ArrayList errSafeAl.add(new Date()); //现在运行时会报错,Attempt to insert class java.util.Date element into collection //with element type class java.lang.String } }
视图是个好机制,它为我们提供了很多方便快捷的转换,安全等方面的操作,让我们对整个集合类库的构架有了个初步的理解。那么谈完了宏观的概念,我们来看一下实际操作中集合框架的一些细节的特性和机制。
集合就是许许多多的数据在一起构成的。在实际应用中,处理这些海量的数据让我们非常的头痛,而且一旦需要操作的复杂一些,几遍for循环的时空复杂度直接以次方形式增长,这是我们绝对不想看到的。为了方便集合类中数据的操作,集合框架提供类一种叫做批操作的概念。
package SetViews;import java.util.HashSet;import java.util.Set;/** * * @author QuinnNorris * 批操作查找两个集合的交集 */public class BulkViews { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Set<String> setA = new HashSet<>(); Set<String> setB = new HashSet<>(); //创建集合A和集合B这是我们要比较的两个集合 Set<String> result = new HashSet<>(setA); //声明集合result存放结果,先用集合A来实例化这个集合 result.retainAll(setB); //调用retainAll方法,将B中与A相同的所有元素留下,达到查找交集的目的 } }
这是一个比较简单的例子,它省略了for循环,而是采用这种批操作的概念来完成交集的查找。但是方法毕竟有限,我们能处理的问题还是在少数,如果真的想广泛的运用批操作,那么结合视图机制是必须的。因为视图机制可以在一定程度上,可以非常简单的进行类型的转换,通过子视图等方法在复杂度较低的情况下达到自己的目的。
由于java平台API中的大部分内容都是在集合框架创建之前设计的,所以,有的时候的确需要在传统的数组和现代的集合之间进行转换。我们可以通过asList方法和toArray方法来解决这个问题。
package SetViews;import java.util.Arrays;import java.util.HashSet;/** * * @author QuinnNorris * 集合与数组之间的转换 */public class TypeChange { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub //---------------------数组转集合--------------------------- String[] values = {"a","b"}; //创建一个普通数组 HashSet<String> hsValues = new HashSet<>(Arrays.asList(values)); //通过Arrays类中静态方法asList,将普通数组转换成List类型的视图对象并实例化 //---------------------集合转数组--------------------------- HashSet<String> hsValue = new HashSet<>(); //创建一个集合 Object[] objValues = hsValue.toArray(); //如果调用无参toArray方法,则返回一个Object数组,此时不能类型强转否则出错 String[] strValues = hsValue.toArray(new String[0]); //有参数的toArray方法如果参数数组的长度小于所需长度的话,重新分配一个能放得下大小的数组 //我们通过有参数的toArray方法巧妙的将集合对象转换成相应类型的 } }
集合框架并没有把Map作为一个集合来看待,然而,我们可以获得映射表的视图,这个视图是一组实现了Collection接口的对象。大家都知道,Collection和Map是两个不相干的接口,如今Map的视图是Collection的对象,无疑是更加方便了我们的操作。Map接口为我们提供了三个方法,这三个方法返回三个不同的视图:
Set44bf986331d5d9c3931140ad55669b0c> entrySet()
返回此映射中包含的映射关系的 Set 视图。
Set245c3adc26563b673f7297c0b3777639 keySet()
返回此映射中包含的键的 Set 视图。
Collectiond94943c0b4933ad8cac500132f64757f values()
返回此映射中包含的值的 Collection 视图。
这其中值得注意的是Map.Entry,很显然这是一个静态内部接口,这个接口中有几个最简单的方法:
K getKey()
返回与此项对应的键。
V getValue()
返回与此项对应的值。
V setValue(V value)
用指定的值替换与此项对应的值。
看完了这些方法,我们也就知道如何去将Map转型为其他,或者如何用V来获取K。
package SetViews;import java.util.HashMap;import java.util.Map;import java.util.Set;/** * * @author QuinnNorris * 集合和映射表之间的转换 */public class SetMap { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Map<String, Integer> map = new HashMap<String, Integer>(); //创建一个Map对象 Set<String> set = map.keySet(); //set是包含着map中所有键的内容的集合 Set<Map.Entry<String, Integer>> entrySet = map.entrySet(); //entrySet是包含着Map中静态内部接口Entry的类对象的集合 for (Map.Entry<String, Integer> es : entrySet) System.out.println(es.getKey() + ":" + es.getValue()); //循环输出,或者也可以进行,按照Key查Value的操作 } }
一提到算法大家想到的可能是c语言中各式各样的排序,查找,二分等算法。但是在java集合类库中,并不需要这么麻烦。我们在Collections这个类中实现了几乎所有的简单算法,为了让程序员不会因为每次要实用算法时,都要自己编写一遍而感到苦恼。Collections这个类在上面也多次出现过了,这个类全部都是静态方法,而且它也确实只是在做一些包装性质的工作。这个类和视图密不可分。我们就来看一下Collections中的方法。
static <T> Queue<T> asLifoQueue(Deque<T> deque) //以后进先出 (Lifo) Queue 的形式返回某个 Deque 的视图。 static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c) //使用二分搜索法搜索指定列表,以获得指定对象。 如果没有找到对象,则返回一个i,应该插入的位置是-i-1static <T> void copy(List<? super T> dest, List<? extends T> src) //将第二个参数列表的内容复制到第一个参数列表中,要保证第一个参数列表的长度是足够的static boolean disjoint(Collection<?> c1, Collection<?> c2) //如果两个指定 collection 中没有相同的元素,则返回 true。 static <T> void fill(List<? super T> list, T obj) //使用指定元素替换指定列表中的所有元素。 static int frequency(Collection<?> c, Object o) //返回指定 collection 中等于指定对象的元素数。 static int indexOfSubList(List<?> source, List<?> target) //返回指定源列表中第一次出现指定目标列表的起始位置;如果没有出现这样的列表,则返回 -1。 static int lastIndexOfSubList(List<?> source, List<?> target) //返回指定源列表中最后一次出现指定目标列表的起始位置;如果没有出现这样的列表,则返回 -1。 static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp) //根据指定比较器产生的顺序,返回给定 collection 的最大元素。 static <T> T min(Collection<? extends T> coll, Comparator<? super T> comp) //根据指定比较器产生的顺序,返回给定 collection 的最小元素。 static <T> List<T> nCopies(int n, T o) //返回由指定对象的 n 个副本组成的不可变列表。 static <T> boolean replaceAll(List<T> list, T oldVal, T newVal) //使用另一个值替换列表中出现的所有某一指定值。 static void reverse(List<?> list) //反转指定列表中元素的顺序。 static <T> Comparator<T> reverseOrder(Comparator<T> cmp) //返回一个比较器,它强行逆转指定比较器的顺序。 static void shuffle(List<?> list) //使用默认随机源对指定列表进行置换。 static <T> void sort(List<T> list, Comparator<? super T> c) //根据指定比较器产生的顺序对指定列表进行排序。 static void swap(List<?> list, int i, int j) //在指定列表的指定位置处交换元素。
两篇集合的文章,从前到后系统的总结了一遍java集合类库的林林总总,感觉学习了很多。尤其是java这种框架的概念,框架把原本分散的一些有关联的东西整合起来,并且给了很多实用的方法,整体上给人一种有血有肉很饱满的感觉。我们在日常使用框架的时候也很有必要去研究一下框架是如何搭建起来的,不仅可以让我们更加熟练的运用,而且能学到设计者的一些优秀的思想。
以上就是java集合(二)—集合框架与算法详解的内容,更多相关内容请关注PHP中文网(www.php.cn)!