转自http://javamvp.iteye.com/blog/476758
(本章主要讲解Java里面会遇到的所有集合类以及相关用法,还有JDK1.5里面出来的一些关于集合和算法的新内容,主要是方便我们在开发过程中适当地注意选择,而且本章的内容相对前边章节比较少,但是代码量比较大,但是大部分内容都是个人的一些总结。当然这一个章节会涉及到JDK本身提供的一些数据结构的相关内容,以及最基本几个数据结构的相关性能分析。本来是打算先写Java的IO相关的内容,刚好最近有个项目需要经常性使用到Java里面的东西,所以先提供这一个章节给大家。很多人来Email说Java的基础数据类型一个章节的内容太少了,这点我必须说一句抱歉,因为基础数据类型是我第一次写,后来才想到使用后边这样的方式来写BLOG的内容,基础章节的东西我后边会提供专程的章节来讲解控制流程、以及各种关键字的清单。该系列教程的主要目的是为了整理一份个人使用的开发文档,如果有人转载,请来Email告知,谢谢!在整个过程里面如果发现有笔误的地方希望来Email提点:silentbalanceyh@126.com)
本章目录
1.基本概念
2.常用集合——列表、队列、栈
3.常用集合——Set集合、哈希表
4.泛型、枚举
3.常用集合——Set集合、哈希表
i.AbstractSet<E>(1.2)类和AbstractMap<K,V>(1.2)相关:
Set和Map部分需要进行比较方式讲解可能更能够理解里面每一种集合的用法
1)HashSet<E>(1.2)、HashMap<K,V>(1.2)和HashTable<K,V>(1.0)
HashSet<E>(1.2)类:
public classHashSet<E>extendsAbstractSet<E>implementsSet<E>,Cloneable,Serializable
该类实现了接口Set,由哈希表支持[实际是一个HashMap实例],它不保证集合的迭代顺序,特别是不保证顺序恒久不变,但是允许null元素。对该集合迭代使用的时间和HashSet实例的大小(元素的数量)和底层的HashMap实例(桶的数量)的“容量”的和成比例,如果迭代性在程序设计的时候很重要,不要将初始化容量设置得太高。该实现不是同步的,而且其遍历方法等同于数组,都是快速失败的,若需要对该集合进行同步方法就需要调用下边的方法:
Set s = Collections.synchronizedSet(newHashSet(...));
HashMap<K,V>(1.2)类:
public classHashMap<K,V>extendsAbstractMap<K,V>implementsMap<K,V>,Cloneable,Serializable
该类是基于哈希表的Map接口实现,此实现提供了所有可选的映射操作,并且允许使用null值和null键。(除了非同步和允许使用null之外,HashMap类和Hashtable类大致相同。),同样的该类不保证映射的顺序,特别是它不保证该顺序恒久不变。HashMap的实例有两个影响性能的参数:初始容量和加载因子
容量是哈希表中的桶的数量,初始容量就是哈希表在创建的时候的容量。
加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度。
当哈希表中的条目数超出了加载因子和当前容量的乘积的时候,需要针对哈希表结构进行重建操作,而哈希表内部具有大约两倍的桶数目。
Hashtable<K,V>(1.0)类:
public classHashtable<K,V>extendsDictionary<K,V>implementsMap<K,V>,Cloneable,Serializable
该类是HashMap的前身,主要特点和HashMap<K,V>差不多,但是有一点点区别就是:键和值都不允许使用null,而且该类的实现是线程同步的。
先看几个简单的例子,再来考虑它们相互之间的一些区别和联系
——[1]HashSet<E>的基本例子——
packageorg.susan.java.collection;
importjava.util.Arrays;
importjava.util.HashSet;
importjava.util.Iterator;
importjava.util.Set;
/**
*一个使用HashSet的简单例子
**/
public classHashSetDemo {
public static voidmain(Stringargs[]){
HashSet<String> set =newHashSet<String>();
set.add("A");
set.add("B");
set.add("C");
set.add("B");
set.add("D");
printSet(set);
System.out.println();
String[] strArray = {"E","F","G"};
set.addAll(Arrays.asList(strArray));
printSet(set);
}
private static voidprintSet(Set<String> set){
Iterator<String> iterator = set.iterator();
while(iterator.hasNext()){
System.out.print("["+ iterator.next() +"],");
}
}
}
根据上边这段代码可以得到下边的输出:【*:因为集合本身是无序的,而且迭代不保证顺序,有可能输出的顺序不一样,但是集合元素的内容应该是相同的。】
[D],[A],[B],[C],
[D],[E],[F],[G],[A],[B],[C],
——[2]从HashSet<E>里面删除某个元素——
packageorg.susan.java.collection;
importjava.util.HashSet;
importjava.util.Iterator;
importjava.util.Set;
/**
*HashSet的删除方法remove(Object obj)
**/
public classHashSetDemo {
public static voidmain(Stringargs[]){
HashSet<Integer> set =newHashSet<Integer>();
set.add(newInteger(12));
set.add(newInteger(33));
set.add(55);
set.remove("B");
set.remove(12);
printSet(set);
}
private static voidprintSet(Set<Integer> set){
Iterator<Integer> iterator = set.iterator();
while(iterator.hasNext()){
System.out.print("["+ iterator.next() +"],");
}
}
}
先看上边这段代码的结果:
[33],[55],
【*:与ArrayList的结果有区别的是,该remove方法只有一个参数Object,而且没有重载形式,因为HashSet本身是无序的,所以和ArrayList不一样的是,不支持索引,所以在删除的时候,进行了一次自动拆解箱的操作。注意代码set.remove(12),这个代码传入的是12的原始类型,但是在这个过程里面转换成为了Integer的类型。如果这一段是ArrayList类型而不是HashSet类型,就会抛出异常,因为ArrayList有一个带int类型的重载方法,是删除某个索引位置的元素,而且HashSet的remove方法如果删除失败的话不会抛出异常,因为是返回的一个特殊值false,这一点可以直接在代码中修改set.remove("B")为System.out.println(set.remove("B"))来验证】
——[3]HashMap<K,V>的不同遍历——
packageorg.susan.java.collection;
importjava.util.Collection;
importjava.util.HashMap;
importjava.util.Iterator;
importjava.util.Set;
importjava.util.Map.Entry;
/**
*使用HashMap的基本例子
**/
public classHashMapDemo {
public static voidmain(Stringargs[]){
HashMap<Integer,String> map =newHashMap<Integer,String>();
map.put(1,"One");
map.put(2,"Two");
map.put(3,"Three");
map.put(4,"Four");
//键遍历
System.out.println("------Key Iterator------");
Set<Integer> keys = map.keySet();
Iterator<Integer> iterator = keys.iterator();
while(iterator.hasNext()){
Integer key = iterator.next();
System.out.print("[Key:"+ key +"--");
System.out.println("Value:"+ map.get(key) +"]");
}
// 值遍历
System.out.println("------Value Iterator------");
Collection<String> values = map.values();
Iterator<String> vIterator = values.iterator();
while(vIterator.hasNext()){
Stringvalue = vIterator.next();
System.out.println("[Value:"+ value +"]");
}
//映射关系遍历
System.out.println("------Entry Iterator------");
Set<Entry<Integer,String>> set = map.entrySet();
Iterator<Entry<Integer,String>> sIterator = set.iterator();
while(sIterator.hasNext()){
Entry<Integer,String> entry = sIterator.next();
System.out.print("[Key:"+ entry.getKey() +"--");
System.out.println("Value:"+ entry.getValue() +"]");
}
}
}
这里提供了三种不同的方式遍历,有几点需要说明:
通过键遍历的时候,键使用的返回是Set<E>,因为键是一个不重复的,所以为了保证键的不重复性,设计的时候使用的Set<E>
通过值遍历的时候,值使用的时候返回是Collection<E>,因为该集合是可以重复的,而且不需要保证值的不重复性
通过关系遍历的时候,使用的格式需要注意Entry是Map的内部类Map.Entry,而且每一个项的类型需要注意
只能通过Map的键去获取值,不能通过Map的值去获取键
下边是这段程序的输出:
------Key Iterator------
[Key:1--Value:One]
[Key:2--Value:Two]
[Key:3--Value:Three]
[Key:4--Value:Four]
------Value Iterator------
[Value:One]
[Value:Two]
[Value:Three]
[Value:Four]
------Entry Iterator------
[Key:1--Value:One]
[Key:2--Value:Two]
[Key:3--Value:Three]
[Key:4--Value:Four]
——[4]Hashtable<K,V>的简单用法——
packageorg.susan.java.collection;
importjava.util.Enumeration;
importjava.util.Hashtable;
public classHashtableDemo {
public static voidmain(Stringargs[]){
Hashtable<Integer,String> tables =newHashtable<Integer,String>();
tables.put(1,"One");
tables.put(2,"Two");
tables.put(3,"Three");
Enumeration<Integer> iterator =null;
for(iterator = tables.keys();iterator.hasMoreElements();){
Integer key = iterator.nextElement();
System.out.print("[Key:"+ key +"--");
System.out.println("Value:"+ tables.get(key) +"]");
}
}
}
上边代码的程序输出:
[Key:3--Value:Three]
[Key:2--Value:Two]
[Key:1--Value:One]
【*:从上边的代码段可以看出,HashMap和Hashtable没有太多的区别,所以这段代码仅仅提供Hashtable的键遍历方式就可以了,至于遍历是使用for还是使用while,这个可以根据本身的情况自行选择。而且需要注意一点是HashMap在遍历的时候返回的迭代器是Iterator,而Hashtable返回的迭代器是Enumeration。】
上边的代码段都是1.5过后带泛型的代码段,下边提供一段老版本(1.4或者1.4以下的版本)的写法:
packageorg.susan.java.collection;
importjava.util.HashMap;
importjava.util.Iterator;
importjava.util.Map.Entry;
public classOleMap {
public static voidmain(Stringargs[]){
HashMap map =newHashMap();
map.put(1,"One");
map.put(2,"Two");
Iterator iterator = map.entrySet().iterator();
while(iterator.hasNext())
{
Entry entry = (Entry)iterator.next();
System.out.print("[Key:"+ entry.getKey() +"--");
System.out.println("Value:"+ entry.getValue()+"]");
}
}
}
这段代码的输出这里不列出来了,老版本的写法没有泛型概念,就会出现一个类型的强制转换过程,在理解的时候可能简单很多,不过这种写法用1.5编译的时候会存在很多类型检查的警告。
上边已经基本接触了Hashtable和HashMap,总结一下这两个的特点和相互之间的区别,因为HashSet本身不是一个映射表的概念,就可以当作一个普通的Set集合的实现来使用,所以HashSet加入比较是没有太大的实际意义:
返回的迭代器不一样:Hashtable返回的迭代器是Enumeration,而HashMap返回的迭代器是Iterator
HashMap不是同步的,而Hashtable是线程同步的,意思就是说前者是线程不安全的,系统开销比较小;后者是线程安全的,开销相对大点点
HashTable不允许null键和null值,而HashMap是允许null键和null值的
HashTable有一个contains(Object value)方法和containsValue(Object value)的功能是一模一样的
最后再介绍一下上边代码没有涉及到的常用方法:
HashMap<K,V>类:
booleancontainsKey(Object key):如果该映射里面包含了指定的一个键的映射关系返回为true,反之返回为false
booleancontainsValue(Object value):如果该映射里面包含了指定的一个或者多个值的映射关系,返回为true,反之返回为false
voidputAll(Map<?extendsK,?extendsV> m):不单独复制某个映射关系,而是直接把一个映射关系复制到某个映射里面,类似集合中的addAll方法
Hashtable<K,V>类:
booleancontains(Object value):如果该映射里面包含了指定的一个或者多个值的映射关系,返回为true,反之返回为false
booleancontainsKey(Object key):如果该映射里面包含了指定的一个键的映射关系返回为true,反之返回为false
booleancontainsValue(Object value):如果该映射里面包含了指定的一个或者多个值的映射关系,返回为true,反之返回为false
Enumeration<V>elements():返回哈希表中的值的枚举
Collection<V>values():返回哈希表中的值的集合
Enumeration<K>keys():返回哈希表中的键的枚举
Set<K>keySet():返回哈希表中的键的集合
protected voidrehash():重组该hash表。
这里注意区分返回为键枚举以及键集合的方法和返回为值枚举和值集合的方法【elements() && values();keys() && keySet()】
2)TreeSet<E>(1.2)类TreeMap(K,V>(1.2)类:
上边介绍的HashSet和HashMap都是无序的,接下来介绍两个有序的映射:
TreeSet<E>(1.2)类:
public classTreeSet<E>extendsAbstractSet<E>implementsNavigableSet<E>,Cloneable,Serializable
基于TreeMap的NavigableSet实现,使用元素的自然顺序对元素进行排序,或者根据创建的Comparator进行排序。
——[$]TreeSet简单例子——
packageorg.susan.java.collection;
importjava.util.Iterator;
importjava.util.TreeSet;
/**
*TreeSet集合的一个例子
**/
public classTreeSetDemo {
public static voidmain(Stringargs[]){
TreeSet<Integer> tree = new TreeSet<Integer>();
tree.add(22);
tree.add(11);
tree.add(33);
tree.add(44);
System.out.println(tree);
Iterator<Integer> iterator = tree.iterator();
while(iterator.hasNext()){
System.out.print(iterator.next() +",");
}
System.out.println();
iterator = tree.descendingIterator();
while(iterator.hasNext()){
System.out.print(iterator.next() +",");
}
}
}
上边这段代码演示了TreeSet的基本用法,其实TreeSet有一个很大的特点,不论用什么样子的方式将元素添加到这个集合里面,这个集合里面的元素总是通过某个比较器进行了排序操作的,可以看下边的输出:
[11, 22, 33, 44]
11,22,33,44,
44,33,22,11,
从上边的结果可以知道,TreeSet本身就是已经排好序的集合,所以正序遍历结果和本身内部的结果一样的顺序,虽然添加的时候是22,11,33,44。至于TreeSet的方法,大部分和Set差不多,这里不做详细介绍,有兴趣的可以去查阅以下API。
——[$]TreeMap简单例子——
介于红黑树(Red-Block tree)的NavigableMap实现,该映射根据键的自然排序进行排序,或者根据键的Comparator进行排序
packageorg.susan.java.collection;
importjava.util.Iterator;
分享到:
相关推荐
Java集合框架是Java编程语言中不可或缺的一部分,它提供了...总的来说,Java集合、泛型和枚举是构建复杂数据结构和实现业务逻辑的关键工具。熟练掌握这些概念和它们的使用方法,将极大地提升Java编程的效率和代码质量。
Java集合框架是Java编程语言中一个至关重要的组成部分,它为数据存储和操作提供了丰富的类库。泛型是Java 5引入的一项创新特性,极大地增强了集合框架的安全性和效率。本讲解将深入探讨这两个主题,以及与之相关的...
在Java编程语言中,枚举和泛型是两种非常重要的特性,它们分别提供了对特定数据类型和类型安全的集合操作的支持。下面我们将详细探讨这两个主题。 **枚举(Enum)** 枚举类型是Java 5.0引入的新特性,它允许开发者...
枚举(Enum)和泛型(Generic)是Java编程语言中的两种强大特性,它们极大地提升了代码的可读性、安全性和复用性。本篇文章将深入探讨这两种概念及其在实际应用中的示例。 首先,我们来看枚举。枚举是一种预定义的...
6. **协变和逆变**:理解如何在泛型中实现类型参数的协变和逆变。 7. **比较和equals**:在泛型上下文中正确地实现Comparable和equals()方法。 8. **枚举类型与泛型**:结合使用枚举和泛型来增强类型安全。 通过...
这使得枚举成为定义常量集合的最佳选择。 示例1展示了如何定义枚举类`Action`,并且在`doAction`方法中使用枚举值作为参数。这样,编译器会自动检查传入的参数是否是枚举中的有效值,避免了无效输入导致的错误。...
泛型可以应用于集合框架、自定义类、接口和方法等场景,是Java编程中的高级特性。本部分内容将详细介绍泛型的基本概念、定义、用法和规则限制等。 首先,泛型的基本概念是“参数化类型”,它允许程序员在不明确指定...
泛型枚举是Java中的一种枚举类型,它允许在枚举类型中定义泛型参数。泛型枚举可以使枚举类型更加灵活和通用。 在Java中,枚举类型可以使用泛型参数,例如: ```java public enum Color<T> { RED, GREEN, BLUE; ...
2. **通配符**:泛型中使用通配符可以增加类型参数的灵活性。例如,`?`表示任何类型,`? extends Number`则限制为Number或其子类。这在处理多种类型的集合时非常有用,如方法参数的定义。 3. **类型擦除**:由于...
《网络程序设计:Java中的泛型与枚举》 在编程世界中,高效且可复用的代码是软件开发的关键。Java作为一种广泛使用的面向对象编程语言,提供了泛型和枚举两种强大的特性,以增强代码的灵活性、安全性和可读性。本篇...
### Java语言基础入门教程知识点详解 ...枚举和泛型是Java语言中非常重要的两个概念。枚举提供了一种定义固定集合的有效方式,而泛型则提高了代码的灵活性和安全性。掌握这两种特性能够帮助开发者写出更高质量的代码。
"Java核心基础技术卷详解" ...Java 核心基础技术卷涉及到 Java 的基础语法、面向对象、异常、泛型、IO、枚举、反射机制、集合、内部类等多方面的知识点,为 Java 开发者提供了一个全面的学习指南。
3. **边界类型**:在泛型中,我们可以指定类型参数的上限或下限,如`<T extends Number>`,这表示`T`必须是`Number`或其子类。 4. **通配符**:在某些情况下,我们可能不关心具体类型,而是关注它是某个类型的子类型...
通过这些实例,你可以探索如何在实际编程中应用这些概念,如创建泛型集合,编写泛型方法,理解类型擦除的影响,以及在边界和通配符上的运用。学习和掌握Java泛型对于编写高效、类型安全的代码至关重要。在这个压缩包...
Java反射和泛型是两种强大的编程特性,它们在开发中有着广泛的应用,特别是在数据库操作的动态化场景下。本文将详细介绍如何结合这两种技术实现在Java中对MySQL、SQL Server或Oracle数据库进行动态的增删改查操作。 ...
Java5.0泛型编程是Java开发中的一个重要里程碑,它引入了一种强大的类型系统机制,大大增强了代码的类型安全性,并减少了在编译期间和运行时出现的类型转换错误。这一特性使得开发者能够在编写代码时就能明确指定...
枚举在Java中不仅是一个类,还是一种特殊的类型,允许我们定义自己的常量集合。接下来,我们将深入探讨Java枚举类型的使用方法及其相关知识点。 1. **枚举的声明与初始化** 枚举类型的声明方式类似于类,但以`enum...
在Java中,集合框架的泛型操作包括添加、删除、查找和遍历元素。这些操作在运行时会进行类型检查,确保插入的元素与集合的泛型类型匹配。在"Demo"中,可能演示了如何通过泛型安全地操作Set等集合类。 7. **通配符*...
3. **枚举**:虽然枚举在 Java 5 中就已经引入,但 Java 8 对其进行了扩展,允许枚举实现接口并包含方法,增强了枚举的灵活性和可扩展性。 4. **泛型**:Java 8 对泛型的支持进一步加强,引入了类型推断,使代码更...