当前位置:文档之家› 第10章 集合

第10章 集合

第10章  集合
第10章  集合

第10章集合

第5章介绍了数组和Array类执行的接口。数组的大小是固定的。如果元素个数是动态的,就应使用集合类。

List和ArrayList是与数组相当的集合类。还有其他类型的集合:队列、栈、链表和字典。

本章介绍如何使用对象组。主要内容如下:

●集合接口和类型

●列表

●队列

●栈

●链表

●有序表

●字典

● Lookup

● HashSet

●位数组

●性能

10.1 集合接口和类型

集合类可以组合为集合,存储Object类型的元素和泛型集合类。在CLR 2.0之前,不存在泛型。现在泛型集合类通常是集合的首选类型。泛型集合类是类型安全的,如果使用值类型,是不需要装箱操作的。如果要在集合中添加不同类型的对象,且这些对象不是相互派生的,例如在集合中添加int和string对象,就只需基于对象的集合类。另一组集合类是专用于特定类型的集合,例如StringCollection类专用于string类型。

提示:

泛型的内容可参见第9章。

对象类型的集合位于System.Collections命名空间;泛型集合类位于System.Collections. Generic命名空间;专用于特定类型的集合类位于System.Collections.Specialized命名空间。

当然,组合集合类还有其他方式。集合可以根据集合类执行的接口组合为列表、集合和字典。接口及其功能如表10-1所示。.NET 2.0为集合类添加了新的泛型接口,例如IEnumerable和IList。这些接口的非泛型版本将一个对象定义为方法的参数,而其泛型版本使用泛型类型T。

提示:

接口IEnumerable、ICollection和IList的内容详见第5章。

对集合和列表非常重要的接口及其方法和属性如表10-1所示。

表10-1

接口方法和属性说明

IEnumerable,IEnumerable GetEnumerator() 如果将foreach语句用于集合,就需要接

口IEnumerable。这个接口定义了方法

GetEnumerator(),它返回一个实现了

IEnumerator的枚举。泛型接口

IEnumerable继承了非泛型接口

IEnumerable,定义了一个返回Enumerable的GetEnumerator方法。因为这两个接口具有继承关系,所以对于每个需要IEnumerable类型参数的方法,都可以传送Enumerable对象

ICollection Count,

IsSynchronized,

SyncRoot,

CopyTo() 接口ICollection由集合类实现。对于实现了这个接口的集合,可以获得元素个数,把集合复制到数组中

接口ICollection扩展了接口IEnumerable的功能

ICollection Count,IsReadOnly,

Add(),Clear(),

Contains(),CopyTo()

Remove() ICollection接口是ICollection接口的泛型版本。这个接口的泛型版本可以添加和删除元素,获得元素个数

IList IsFixedSize,

IsReadOnly,

Item,Add,Clear,

Contains,IndexOf,

Insert,Remove,

RemoveAt 接口IList派生于接口ICollection。IList 允许使用索引器访问集合,还可以在集合的任意位置插入或删除元素

IList Item,IndexOf

Insert,Remove 与接口ICollection类似,接口IList也继承了接口ICollection。

第5章提到,Array类实现了这个接口,但添加或删除元素的方法会抛出NotSupportedException异常。在大小固定的只读集合(如Array类)中,这个接口定义的一些方法会抛出NotSupportedExceptiuon异常

比较接口IList的非泛型版本和泛型版本,会发现新的泛型接口只为提供了索引的集合定义了重要的方法和属性。其他方法重构到ICollection接口中

IDictionary IsFixedSize,

IsReadOnly,Item,

Keys,Values,Add(),

Clear(),Contains(),

GetEnumerator(),

Remove() 接口IDictionary由其元素包含键和值的非泛型集合实现

IDictionary Item,Keys,Values,Add(),

ContainsKey (),

Remove(),

TyrGetValue()

IDictionary由其元素包含

键和值的泛型集合实现。这个接口比

IDictionary简单

ILookup Count, Item,

Contains()

ILookup是.NET 3.5中

的一个新接口,一个键对应多个值的集合

使用它。索引器为指定的键返回一个枚举IComparer Compare() 接口IComparer由比较器实现,通过

Compare()方法给集合中的元素排序

IEqualityComparer Equals(),

GetHashCode() 接口IEqualityComparer由一个比较器实现,该比较器可用于字典中的键。使用这个接口,可以对对象进行相等比较。

提示:

非泛型接口ICollection定义的一些属性用于同步不同线程对同一个集合的访问。这些属

性不再用于新的泛型接口。其原因是这些属性会导致与同步相关的安全性问题,因为集合通

常不仅仅必须同步。集合同步的内容可参见第19章。

表10-2列出了集合类和由这些类执行的集合接口。

表10-2

集合类集合接口

ArrayList IList, ICollection, IEnumerable

Queue ICollection, IEnumerable

Stack ICollection, IEnumerable

BitArray ICollection, IEnumerable

Hashtable IDictionary, ICollection, IEnumerable

SortedList IDictionary, ICollection, IEnumerable

List IList, ICollection, IEnumerable, IList, ICollection, IEnumerable Queue IEnumerable, ICollection, IEnumerable

Stack IEnumerable, ICollection, IEnumerable

LinkedList ICollection, IEnumerable, ICollection, IEnumerable

HashSet ICollection, IEnumerable, IEnumerable

Dictionary IDictionary, ICollection>, IEnumerable>,IDictionary, ICollection, IEnumerable

SortedDictionary IDictionary, ICollection>, IEnumerable>,IDictionary, ICollection, IEnumerable

SortedList IDictionary,ICollection>, IEnumerable>,IDictionary, ICollection, IEnumerable

Lookup ILookup, IEnumerable>, IEnumerable

10.2 列表

.NET Framework为动态列表提供了类ArrayList和List。System.Collections.Generic 命名空间中的类List的用法非常类似于System.Collections命名空间中的ArrayList类。这个类实现了IList、ICollection和IEnumerable接口。第9章讨论了这些接口的方法,所以本节只探讨如何使用List类。

下面的例子将Racer类中的成员用作要添加到集合中的成员,以表示一级方程式的一位赛手。这个类有4个字段:firstname、lastname、country和获胜次数。这些字段可以用属性

来访问。在该类的构造函数中,可以传送赛手的姓名和获胜次数,以设置成员。方法ToString()重写为返回赛手的姓名。类Racer也实现了泛型接口IComparer,为Racer元素排序。

[Serializable]

public class Racer : IComparable, IFormattable

{

public Racer()

: this(String.Empty, String.Empty, String.Empty) {}

public Racer(string firstname, string lastname, string country)

: this(firstname, lastname, country, 0) {}

public Racer(string firstname, string lastname, string country, int

wins)

{

this.firstname = firstname;

https://www.doczj.com/doc/0316495796.html,stname = lastname;

this.country = country;

this.wins = wins;

}

public string Firstname{ get; set; }

public string Lastname{ get; set; }

public string Country { get; set; }

public int Wins { get; set; }

public override string ToString()

{

return String.Format("{0} {1}",

FirstName, LastName);

}

public string ToString(string format, IFormatProvider

formatProvider)

{

switch (format.ToUpper())

{

case null:

case "N": //Name

return ToString();

case "F": //FirstName

return Firstname;

case "L": //LastName

return Lastname;

case "W": //Wins

return String.Format("{0}, Wins: {1}",

ToString(), Wins);

case "C": // Country

return String.Format(

"{0}, Country: {1}",

ToString(), Country);

case "A": // All

return String.Format(

"{0}, {1} Wins: {2}",

ToString(), Country, Wins);

default:

throw new FormatException(String.Format(

formatProvider,

"Format {0} is not supported",

format));

}

}

public string ToString(string format)

{

return ToString(format, null);

}

public int CompareTo(Racer other)

{

int compare = https://www.doczj.com/doc/0316495796.html,pareTo(

https://www.doczj.com/doc/0316495796.html,stName);

if (compare == 0)

return https://www.doczj.com/doc/0316495796.html,pareTo(

other.FirstName);

return compare;

}

}

10.2.1 创建列表

调用默认的构造函数,就可以创建列表对象。在泛型类List中,必须在声明中为列表的值指定类型。下面的代码说明了如何声明一个包含int的List和一个包含Racer元素的列表。ArrayList是一个非泛型列表,可以将任意Object类型作为其元素。

使用默认的构造函数创建一个空列表。元素添加到列表中后,列表的容量就会扩大为可接纳4个元素。如果添加了第5个元素,列表的大小就重新设置为包含8个元素。如果8个元素还不够,列表的大小就重新设置为16。每次都会将列表的容量重新设置为原来的2倍。

ArrayList objectList = new ArrayList();

List intList = new List();

List racers = new List();

如果列表的容量改变了,整个集合就要重新分配到一个新的内存块中。在List的实现代码中,使用了一个T类型的数组。通过重新分配内存,创建一个新数组,Array.Copy()将旧数组中的元素复制到新数组中。为节省时间,如果事先知道列表中元素的个数,就可以用构造函数定义其容量。下面创建了一个容量为10个元素的集合。如果该容量不足以容纳要添加的元素,就把集合的大小重新设置为20,或40,每次都是原来的2倍。

ArrayList objectList = new ArrayList(10);

List intList = new List(10);

使用Capacity属性可以获取和设置集合的容量。

objectList.Capacity = 20;

intList.Capacity = 20;

容量与集合中元素的个数不同。集合中元素的个数可以用Count属性读取。当然,容量总是大于或等于元素个数。只要不把元素添加到列表中,元素个数就是0。

Console.WriteLine(intList.Count);

如果已经将元素添加到列表中,且不希望添加更多的元素,就可以调用TrimExcess()方法,去除不需要的容量。但是,重新定位是需要时间的,所以如果元素个数超过了容量的90%,TrimExcess()方法将什么也不做。

intList.TrimExcess();

提示:

对于新的应用程序,通常可以使用泛型类List替代非泛型类ArrayList,而且ArrayList 类的方法与List非常相似,所以本节将只介绍List

1. 集合初始化器

C# 3.0允许使用集合初始化器给集合赋值。集合初始化器的语法类似于第5章介绍的数组初始化器。使用集合初始化器,可以在初始化集合时,在花括号中给集合赋值:

List < int > intList = new List < int > () {1, 2};

List < string > stringList =

new List < string > () {"one", "two"};

提示:

集合初始化器是C# 3.0编程语言的一个特性,没有反映在已编译的程序集的IL代码中。编译器会把集合初始化器变成给初始化列表中的每一项调用Add()方法。

2. 添加元素

使用Add()方法可以给列表添加元素,如下所示。实例化的泛型类型定义了Add()方法的参数类型:

List intList = new List();

intList.Add(1);

intList.Add(2);

List stringList = new List();

stringList.Add("one");

stringList.Add("two");

变量racers定义为类型List。使用new操作符创建相同类型的一个新对象。因为类List 用具体类Racer来实例化,所以现在只有Racer对象可以用Add()方法添加。在下面的例子中,创建了5个一级方程式赛手,并添加到集合中。前3个用集合初始化器添加,后两个通过显式调用Add()方法来添加。

Racer graham = new Racer("Graham", "Hill", "UK", 14);

Racer emerson = new Racer("Emerson", "Fittipaldi", "Brazil", 14);

Racer mario = new Racer("Mario", "Andretti", "USA", 12);

List racers = new List(20);

{graham, emerson, mario};

racers.Add(new Racer("Michael", "Schumacher",

"Germany", 91));

racers.Add(new Racer("Mika", "Hakkinen",

"Finland", 20));

使用List类的AddRange()方法,可以一次给集合添加多个元素。AddRange()方法的参数是IEnumerable类型的对象,所以也可以传送一个数组,如下所示:

racers.AddRange(new Racer[] {

new Racer("Niki", "Lauda", "Austria", 25)

new Racer("Alian", "Prost", "France", 51 } );

提示:

集合初始化器只能在声明集合时使用。AddRange()方法则可以在初始化集合后调用。

如果在实例化列表时知道集合的元素个数,也可以将执行IEnumerable的任意对象传送给类的构造函数。这非常类似于AddRange()方法:

List racers = new List (new Racer[] {

new Racer("Jochen", "Rindt", "Austria", 6)

new Racer("Ayrton", "Senna", "Brazil", 41 } );

3. 插入元素

使用Insert()方法可以在指定位置插入元素:

racers. Insert(3, new Racer("Phil", "Hill", "USA", 3));

方法InsertRange()提供了插入大量元素的容量,类似于前面的AddRange()方法。

如果索引集大于集合中的元素个数,就抛出ArgumentOutOfRangeException类型的异常。

4. 访问元素

执行了IList和IList接口的所有类都提供了一个索引器,所以可以使用索引器,通过传送元素号来访问元素。第一个元素可以用索引值0来访问。指定racers[3],可以访问列表中的第4个元素:

Racer r1 = racers[3];

可以用Count属性确定元素个数,再使用for循环迭代集合中的每个元素,使用索引器访问每一项:

for (int i=0; i

{

Console.WriteLine(racers[i]);

}

提示:

可以通过索引访问的集合类有ArrayList、StringCollection和List。

List执行了接口IEnumerable,所以也可以使用foreach语句迭代集合中的元素。

foreach (Racer r in racers)

{

Console.WriteLine(r);

}

注意:

编译器解析foreach语句时,利用了接口IEnumerable和IEnumerator,参见第5章。

除了使用foreach语句之外,List类还提供了ForEach()方法,它用Action参数声明。

public void ForEach(Action action);

ForEach()的执行代码如下。ForEach()迭代集合中的每一项,调用传送作为每一项的参数的方法。

public class List < T > : IList < T >

{

private T[] items;

//...

public void ForEach(Action < T > action)

{

if (action == null) throw new ArgumentNullException("action");

foreach (T item in items)

{

action(item);

}

}

//...

}

为了给ForEach传送一个方法,Action声明为一个委托,它定义了一个返回类型为void、参数为T的方法。

public delegate void Action(T obj);

在Racer项的列表中,ForEach()方法的处理程序必须声明为以Racer对象作为参数,返回类型是void。

public void ActionHandler(Racer obj);

Console.WriteLine()方法的一个重载版本将Object作为参数,所以可以将这个方法的地址传送给ForEach()方法,把集合中的每个赛手写入控制台:

racers.ForEach(Console.WriteLine);

也可以编写一个匿名方法,它将Racer对象作为参数。这里,格式A由IFormattable接口的ToString()方法用于显示赛手的所有信息:

racers.ForEach(

delegate(Racer r)

{

Console.WriteLine("{0:A}", r);

});

在C# 3.0中,还可以使用表达式和以委托作为参数的方法。使用匿名方法执行的迭代也可以用表达式定义:

racers.ForEach(

r = > Console.WriteLine("{0:A}", r));

注意:

匿名方法和表达式详见第7章。

5. 删除元素

删除元素时,可以利用索引,或传送要删除的元素。下面的代码把3传送给RemoveAt(),删除第4个元素:

racers. RemoveAt(3);

也可以直接将Racer对象传送给Remove()方法,删除这个元素。按索引删除比较快,因为必须在集合中搜索要删除的元素。Remove()方法先在集合中搜索,用IndexOf()方法确定元素的索引,再使用该索引删除元素。IndexOf()方法先检查元素类型是否执行了IEquatable 接口。如果是,就调用这个接口中的Equals()方法,确定集合中的元素是否等于传送给Equals()方法的元素。如果没有执行这个接口,就使用Object类的Equals()方法比较元素。Object类中Equals()方法的默认实现代码对值类型进行按位比较,对引用类型只比较其引用。

注意:

第6章介绍了如何重写Equals()方法。

这里从集合中删除了变量graham引用的赛手。变量graham是前面在填充集合时创建的。接口IEquatable和Object.Equals()方法都没有在Racer类中重写,所以不能用要删除的元素内容创建一个新对象,再把它传送给Remove()方法。

If(!racers. Remove(graham))

{

Console.WriteLine("object not found in collection");

}

方法RemoveRange()可以从集合中删除许多元素。它的第一个参数指定了开始删除的元素索引,第二个参数指定了要删除的元素个数。

int index = 3;

int count = 5;

racers. RemoveRange (index, count)

要从集合中删除有指定特性的所有元素,可以使用RemoveAll()方法。这个方法在搜索元素时使用下面将讨论的Predicate参数。要删除集合中的所有元素,可以使用ICollection接口定义的Clear()方法。

6. 搜索

有不同的方式在集合中搜索元素。可以获得要查找的元素的索引,或者搜索元素本身。可以使用的方法有IndexOf()、LastIndexOf()、FindIndex()、FindLastIndex()、Find()和FindLast()。如果只检查元素是否存在,List类提供了Exists()方法。

方法IndexOf()需要将一个对象作为参数,如果在集合中找到该元素,这个方法就返回该元素的索引。如果没有找到该元素,就返回-1。IndexOf()方法使用IEquatable接口来比较元素。

int index1 = racers. IndexOf(mario);

使用方法IndexOf(),还可以指定不需要搜索整个集合,但必须指定从哪个索引开始搜索以及要搜索的元素个数。

除了使用IndexOf()方法搜索指定的元素之外,还可以搜索有某个特性的元素,该特性可以用FindIndex()方法来定义。FindIndex()方法需要一个Predicate类型的参数:

public int FindIndex(Predicate match);

Predicate类型是一个委托,它返回一个布尔值,需要把类型T作为参数。这个委托的用法与ForEach()方法中的Action委托类似。如果Predicate返回true,就表示有一个匹配,找到了一个元素。如果它返回false,就表示没有找到元素,搜索将继续。

public delegate bool Predicate(T obj);

在List类中,把Racer对象作为类型T,所以可以将一个方法(该方法将类型Racer

定义为参数、且返回bool)的地址传送给FindIndex()方法。查找指定国家的第一个赛手时,可以创建如下所示的FindCountry类。Find()方法的签名和返回类型是通过Predicate委托定义的。Find()方法使用变量country搜索用FindCountry类的构造函数定义的一个国家。

public class FindCountry

{

public FindCountry(string country)

{

this.country = country;

}

private string country;

public bool FindCountryPredicate(Racer racer)

{

if(racer == null) throw new ArgumentNullException("racer");

return r.Country == country;

}

}

使用FindIndex()方法可以创建FindCountry()类的一个新实例,把一个国家字符串传送给构造函数,再传送Find方法的地址。FindIndex()方法成功完成后,index2就包含集合中Country属性设置为Finland的第一项的索引。

int index2 = racers. FindIndex(new FindCountry("Finland").

FindCountryPredicate);

除了用处理程序方法创建一个类之外,还可以在这里创建一个表达式。结果与前面完全相同。现在表达式定义了实现代码,来搜索Country属性设置为Finland的元素。

int index3 = racers. FindIndex(r=> r.Country == "Finland");

与IndexOf()方法类似,使用FindIndex()方法也可以指定搜索开始的索引和要迭代的元素个数。为了从集合中最后一个元素开始向前搜索,可以使用FindLastIndex()方法。

方法FindIndex()返回所搜索元素的索引。除了获得索引之外,还可以直接获得集合中的元素。方法Find()需要一个Predicate类型的参数,这与FindIndex()方法类似。下面的方法Find()搜索列表中Firstname属性设置为Niki的第一个赛手。当然,也可以执行FindLast()方法,查找与Predicate匹配的最后一项。

Racer r = racers. Find(r=> r.Firstname == "Niki");

要获得与Predicate匹配的所有项,而不是一项,可以使用FindAll()方法。FindAll()方法使用的Predicate委托与Find()和FindIndex()方法相同。FindAll()方法在找到第一项后,不会停止搜索,而是迭代集合中的每一项,返回Predicate是true的所有项。

这里调用了FindAll()方法,返回Wins属性设置超过20的所有racer项。所有赢得20多场比赛的赛手都从bigWinners列表中引用。

List bigWinners = racers.FindAll(r=> r.Wins > 20);

用foreach语句迭代bigWinners变量,结果如下:

foreach(Racer r in bigWinners)

{

Console.WriteLine("{0:A}", r);

}

Michael Schumacher, Germany Wins: 91

Niki Lauda, Austria Wins: 25

Alain Prost, France Wins: 51

这个结果没有排序,但这是下一步要做的工作。

7. 排序

List类可以使用Sort()方法对元素排序。Sort()方法使用快速排序算法,比较所有的元素,直到对整个列表排好序为止。

Sort()方法定义了几个重载方法。可以传送给它的参数有泛型委托Comparison、泛型接口IComparer、泛型接口IComparer和一个范围值。

public void List. Sort();

public void List. Sort(Comparison);

public void List. Sort(IComparer);

public void List. Sort(Int32, Int32, IComparer);

只有集合中的元素执行了接口IComparable,才能使用不带参数的Sort()方法。

类Racer实现了IComparable接口,可以按姓氏对赛手排序:

racers. Sort();

racers.ForEach(Console.WriteLine);

如果需要按照元素类型不默认支持的方式排序,就应使用其他技术,例如传送执行了IComparer接口的对象。

类RacerComparer给Racer类型执行了接口IComparer。这个类允许按名字、姓氏、国籍或获胜次数排序。排序的种类用内部枚举类型CompareType定义。CompareType用类RacerComparer的构造函数设置。接口IComparer定义了排序所需的方法Compare()。在这个方法的实现代码中,使用了string和int类型的CompareTo()方法。

public class RacerComparer : IComparer

{

public enum CompareType

{

Firstname,

Lastname;

Country;

Wins

}

private CompareType compareType;

public RacerComparer(CompareType compareType)

{

this. compareType = compareType;

}

public int Compare(Racer x, Racer y)

{

if (x == null) throw new ArgumentNullException("x");

if (y == null) throw new ArgumentNullException("y");

int result;

switch (compareType)

{

case compareType.Firstname:

return https://www.doczj.com/doc/0316495796.html,pareTo(y.Firstname);

case https://www.doczj.com/doc/0316495796.html,stname:

return https://www.doczj.com/doc/0316495796.html,pareTo(https://www.doczj.com/doc/0316495796.html,stname);

case compareType.Country:

if ((result = https://www.doczj.com/doc/0316495796.html,pareTo(y. Country) == 0)

return https://www.doczj.com/doc/0316495796.html,pareTo(https://www.doczj.com/doc/0316495796.html,stname);

else

return res;

case compareType.Wins:

return https://www.doczj.com/doc/0316495796.html,pareTo(y.Wins);

default:

throw new ArgumentNullException("Invalid Compare Type");

}

}

}

现在,可以对类RacerComparer的一个实例使用Sort()方法。传送枚举RacerComparer. CompareType.Country,按属性Country对集合排序:

racers.Sort(new RacerComparer(RacerComparer .CompareType.

Country));

racers.ForEach(Console.WriteLine);

排序的另一种方式是使用重载的Sort()方法,它需要一个Comparison委托:

public void List Sort (Comparison);

Comparison是一个方法的委托,该方法有两个T类型的参数,返回类型为int。如果参数值相等,该方法就返回0。如果第一个参数比第二个小,就返回小于0的值;否则,返回大于0的值。

public delegate int Comparison(T x, T y);

现在可以把一个表达式传送给Sort()方法,按获胜次数排序。两个参数的类型是Racer,在其实现代码中,使用int方法CompareTo()比较Wins属性。在实现代码中,r2和r1以逆序方式使用,所以获胜次数以降序方式排序。方法调用完后,完整的赛手列表就按赛手的获胜次数排序。

racers.Sort(r1, r2) => https://www.doczj.com/doc/0316495796.html,pareTo(r1.Wins));

也可以调用Reverse()方法,倒转整个集合的顺序。

8. 类型转换

使用List类的ConvertAll()方法,可以把任意类型的集合转换为另一种类型。ConvertAll()方法使用一个Converter委托,其定义如下:

public sealed delegate TOutput Converter

TOutput>(TInput from);

泛型类型TInput和TOutput用于转换。TInput是委托方法的变元,TOutput是返回类型。

在这个例子中,所有的Racer类型都应转换为Person类型。Racer类型包含姓、名、国籍和获胜次数,而Person类型只包含姓名。为了进行转换,可以忽略赛手的国籍和获胜次

数,但姓名必须转换:

[Serializable]

public class Person

{

private string name;

public Person(string name)

{

https://www.doczj.com/doc/0316495796.html, = name;

}

public override string ToString()

{

return name;

}

}

转换时调用了racers.ConvertAll()方法。这个方法的变元定义为一个表达式,其变元的类型是Racer,返回类型是Person。在表达式的实现代码中,创建并返回了一个新的Person对象。对于Person对象,把Firstname和Lastname传送给构造函数:

List persons = racers. ConvertAll(

r => new Person(r.Firstname + " " +https://www.doczj.com/doc/0316495796.html,stname));

转换的结果是一个列表,其中包含转换过来的Person对象:类型为List的persons列表。

10.2.2 只读集合

集合创建好后,就是可读写的。当然,集合必须是可读写的,否则就不能给它填充值了。但是,在填充完集合后,可以创建只读集合。List集合的方法AsReadOnly返回ReadOnlyCollection类型的对象。ReadOnlyCollection类执行的接口与List相同,但所有修改集合的方法和属性都抛出NotSupportedException异常。

10.3 队列

队列是其元素以先进先出(FIFO)的方式来处理的集合。先放在队列中的元素会先读取。队列的例子有在机场排的队、人力资源部中等待处理求职信的队列、打印队列中等待处理的打印任务、以循环方式等待CPU处理的线程。另外,还常常有元素根据其优先级来处理的队列。例如,在机场的队列中,商务舱乘客的处理要优先于经济舱的乘客。这里可以使用多个队列,一个队列对应一个优先级。在机场,这是很常见的,因为商务舱乘客和经济舱乘客有不同的登记队列。打印队列和线程也是这样。可以为一组队列建立一个数组,数组中的一项代表一个优先级。在每个数组项中,都有一个队列,其处理按照FIFO的方式进行。

注意:

本章的后面将使用链表的另一种实现方式,来定义优先级列表。

在.NET的System.Collections命名空间中有非泛型类Queue,在System.Collections.Generic命名空间中有泛型类Queue。这两个类的功能非常类似,但泛型类是强类型化的,定义了类型T,而非泛型类基于Object类型。

在内部,Queue类使用T类型的数组,这类似于List类型。另一个类似之处是它们都执行ICollection和IEnumerable接口。Queue类执行了ICollection、IEnumerable和

ICloneable接口。Queue类执行了IEnumerable和ICloneable接口。Queue泛型类没有执行泛型接口ICollection,因为这个接口用Add()和Remove()方法定义了在集合中添加和删除元素的方法。

队列与列表的主要区别是队列没有执行IList接口。所以不能用索引器访问队列。队列只允许添加元素,该元素会放在队列的尾部(使用Enqueue()方法),从队列的头部获取元素(使用Dequeue()方法)。

图10-1显示了队列的元素。Enqueue()方法在队列的一端添加元素,Dequeue()方法在队列的另一端读取和删除元素。用Dequeue()方法读取元素,将同时从队列中删除该元素。再调用一次Dequeue()方法,会删除队列中的下一项。

图10-1

Queue和Queue 类的方法如表10-3所示。

表10-3

说明

Queue和Queue

类的成员

Enqueue() 在队列一端添加一个元素

Dequeue() 在队列的头部读取和删除一个元素。如果在调用Dequeue()方法

时,队列中不再有元素,就抛出InvalidOperationException异常Peek() 在队列的头部读取一个元素,但不删除它

Count 返回队列中的元素个数

TrimExcess() 重新设置队列的容量。Dequeue()方法从队列中删除元素,但不

会重新设置队列的容量。要从队列的头部去除空元素,应使用

TrimExcess()方法

Contains() 确定某个元素是否在队列中,如果是,就返回true

CopyTo() 把元素从队列复制到一个已有的数组中

ToArray() ToArray()方法返回一个包含队列元素的新数组

在创建队列时,可以使用与List类型类似的构造函数。默认的构造函数会创建一个空队列,也可以使用构造函数指定容量。在把元素添加到队列中时,容量会递增,包含4、8、16和32个元素。与List类型类似,队列的容量也总是根据需要成倍增加。非泛型类Queue的默认构造函数与此不同,它会创建一个包含32项的空数组。使用构造函数的重载版本,还可以将执行了IEnumerable接口的其他集合复制到队列中。

下面的文档管理应用程序示例演示了Queue类的用法。使用一个线程将文档添加到队列中,用另一个线程从队列中读取文档,并处理它们。

存储在队列中的项是Document类型。Document类定义了标题和内容:

public class Document

{

private string title;

public string Title

{

get

{

return title;

}

}

private string content;

public string Content

{

get

{

return content;

}

}

public Document(string title, string content)

{

this.title = title;

this.content = content;

}

}

DocumentManager类是Queue类外面的一层。DocumentManager类定义了如何处理文档:用AddDocument()方法将文档添加到队列中,用GetDocument()方法从队列中获得文档。

在AddDocument()方法中,用Enqueue()方法把文档添加到队列的尾部。在GetDocument()方法中,用Dequeue()方法从队列中读取第一个文档。多个线程可以同时访问DocumentManager,所以用lock语句锁定对队列的访问。

提示:

线程和lock语句参见第19章。

IsDocumentAvailable是一个只读布尔属性,如果队列中还有文档,它就返回true,否则返回false。

public class DocumentManager

{

private readonly Queue documentQueue = new

Queue;

public void AddDocument(Document doc)

{

lock(this)

{

documentQueue.Enqueue(doc);

}

}

public Document GetDocument()

{

Document doc = null;

lock(this)

{

doc = documentQueue.Dequeue();

}

return doc;

}

public bool IsDocumentAvailable

{

get

{

return documentQueue.Count > 0;

}

}

}

类ProcessDocuments在一个单独的线程中处理队列中的文档。能从外部访问的唯一方法是Start()。在Start()方法中,实例化了一个新线程。创建一个ProcessDocuments对象,来启动线程,定义Run()方法作为线程的启动方法。ThreadStart是一个委托,它引用了由线程启动的方法。在创建Thread对象后,就调用Thread.Start()方法来启动线程。

使用ProcessDocuments类的Run()方法定义一个无限循环。在这个循环中,使用属性IsDocumentAvailable确定队列中是否还有文档。如果还有,就从DocumentManager中提取文档并处理。这里的处理仅是把信息写入控制台。在真正的应用程序中,文档可以写入文件、数据库,或通过网络发送。

public class ProcessDocuments

{

public static void Start(DocumentManager dm)

{

new Thread(new ProcessDocuments(dm).Run).Start();

}

protected ProcessDocuments(DocumentManager dm)

{

documentManager = dm;

}

private DocumentManager documentManager;

protected void Run()

{

while (true)

{

if (documentManager.IsDocumentAvailable)

{

Document doc = documentManager.GetDocument();

Console.WriteLine("Processing document {0}", doc.Title);

}

Thread.Sleep(new Random().Next(20));

}

}

}

在应用程序的Main()方法中,实例化一个DocumentManager对象,启动文档处理线程。接着创建1000个文档,并添加到DocumentManager中:

class Program

{

static void Main

{

DocumentManager dm = new DocumentManager();

ProcessDocuments.Start(dm);

// Create documents and add them to the

DocumentManager

for (int i = 0; i < 1000; i++)

{

Document doc = new Document("Doc " + i.ToString(), "content");

dm.AddDocument(doc);

Console.WriteLine("added document {0}", doc.Title);

Thread.Sleep(new Random().Next(20));

}

}

}

在启动应用程序时,会在队列中添加和删除文档,输出如下所示:

Added document Doc 279

Processing document Doc 236

Added document Doc 280

Processing document Doc 237

Added document Doc 281

Processing document Doc 238

Processing document Doc 239

Processing document Doc 240

Processing document Doc 241

Added document Doc 282

Processing document Doc 242

Added document Doc 283

Processing document Doc 243

完成示例应用程序中描述的任务的真实程序可以处理用Web服务接收到的文档。10.4 栈

栈是与队列非常类似的另一个容器,只是要使用不同的方法访问栈。最后添加到栈中的元素会最先读取。栈是一个后进先出(LIFO)容器。

图10-2表示一个栈,用Push()方法在栈中添加元素,用Pop()方法获取最近添加的元素。

图10-2

与Queue类相同,非泛型类Statck也执行了ICollection、IEnumerable和ICloneable接口;泛型类Statck实现了IEnumerable、ICollection和IEnumerable接口。

Statck和Statck类的成员如表10-4所示。

表10-4

Statck和Statck类的成员说明

Push() 在栈顶添加一个元素

Pop() 从栈顶删除一个元素,并返回该元素。如果栈是空的,就抛出InvalidOperationException异常

Peek() 返回栈顶元素,但不删除它

Count 返回栈中的元素个数

Contains() 确定某个元素是否在栈中,如果是,就返回true CopyTo() 把元素从栈复制到一个已有的数组中。

ToArray() ToArray()方法返回一个包含栈中元素的新数组

在下面的例子中,使用Push()方法把三个元素添加到栈中。在foreach方法中,使用IEnumerable接口迭代所有的元素。栈的枚举器不会删除元素,只会逐个返回元素。

Stack alphabet = new Stack();

alphabet.Push('A');

alphabet.Push('B');

alphabet.Push('C');

foreach (char item in alphabet)

{

Console.Write(item);

}

Console.WriteLine();

因为元素的读取顺序是从最后一个添加到栈中的元素开始到第一个元素,所以得到的结果如下:

CBA

用枚举器读取元素不会改变元素的状态。使用Pop()方法会从栈中读取每个元素,然后删除它们。这样,就可以使用while循环迭代集合,检查Count属性,确定栈中是否还有元素:

Stack alphabet = new Stack();

alphabet.Push('A');

alphabet.Push('B');

alphabet.Push('C');

Console.Write("First iteration: ");

foreach (char item in alphabet)

{

Console.Write(item);

}

Console.WriteLine();

Console.Write("Second iteration: ");

while (alphabet.Count > 0)

{

Console.Write(alphabet.Pop());

}

Console.WriteLine();

结果是两个CBA。在第二次迭代后,栈变空,因为第二次迭代使用了Pop()方法:

First iteration: CBA

Second iteration: CBA

10.5 链表

LinkedList集合类没有非泛型集合的类似版本。LinkedList是一个双向链表,其元素指向它前面和后面的元素,如图10-3所示。

图10-3

链表的优点是,如果将元素插入列表的中间位置,使用链表会非常快。在插入一个元素时,只需修改上一个元素的Next引用和下一个元素的Previous引用,使它们引用所插入的元素。在List和ArrayList类中,插入一个元素,需要移动该元素后面的所有元素。

当然,链表也有缺点。链表的元素只能一个接一个地访问,这需要较长的时间来查找位于链表中间或尾部的元素。

链表不仅能在列表中存储元素,还可以给每个元素存储下一个元素和上一个元素的信息。这就是LinkedList包含LinkedListNode类型的元素的原因。使用LinkedListNode 类,可以获得列表中的下一个元素和上一个元素。表10-5描述了LinkedListNode的属性。

表10-5

LinkedListNode的属性说明

List 返回与节点相关的LinkedList

Next 返回当前节点之后的节点。其返回类型是LinkedListNode Previous 返回当前节点之前的节点

Value 返回与节点相关的元素,其类型是T

类LinkedList执行了IEnumerable、ICollection、ICollection、IEnumerable、ISerializable 和IDeserializationCallback接口。这个类的成员如表10-6所示。

表10-6

LinkedList的成员说明

Count 返回链表中的元素个数

First 返回链表中的第一个节点。其返回类型是LinkedListNode。使用这个返回的节点,可以迭代集合中的其他节点

Last 返回链表中的最后一个元素。其返回类型是LinkedListNode。使用这个节点,可以逆序迭代集合中的其他节点

AddAfter() AddBefore() AddFirst() AddLast() 使用AddXXX方法可以在链表中添加元素。使用相应的Add方法,可以在链表的指定位置添加元素。AddAfter()需要一个LinkedListNode对象,在该对象中可以指定要添加的新元素后面的节点。AddBefore()把新元素放在第一个参数定义的节点前面。AddFirst()和AddLast()把新元素添加到链表的开头和结尾重载所有这些方法,可以添加类型LinkedListNode或类型T的对象。如果传送T对象,则创建一个新LinkedListNode对象

Remove() RemoveFirst() RemoveLast() Remove()、RemoveFirst()和RemoveLast()方法从链表中删除节点。RemoveFirst()删除第一个元素,RemoveLast()删除最后一个元素。Remove()需要搜索一个对象,从链表中删除匹配该对象的第一个节点

Clear() 从链表中删除所有的节点

Contains() 在链表中搜索一个元素,如果找到该元素,就返回true,否则返回false

Find() 从链表的开头开始搜索传送给它的元素,并返回一个LinkedListNode FindLast() 与Find()方法类似,但从链表的结尾开始搜索

示例应用程序使用了一个链表LinkedList和一个列表List。链表包含文档,这与上一个例子相同,但文档有一个额外的优先级。在链表中,文档按照优先级来排序。如果多个文档的优先级相同,则这些元素就按照文档的插入时间来排序。

图10-4描述了示例应用程序中的集合。LinkedList是一个包含所有Document对象的链表,该图显示了文档的标题和优先级。标题指出了文档添加到链表中的时间。第一个添加的文档的标题是One。第二个添加的文档的标题是Two,依此类推。可以看出,文档One和Four有相同的优先级8,因为One在Four之前添加,所以One放在链表的前面。

图10-4

在链表中添加新文档时,它们应放在优先级相同的最后一个文档后面。集合LinkedList 包含LinkedListNode类型的元素。类LinkedListNode添加Next和Previous属性,使搜索过程能从一个节点移动到下一个节点上。要引用这类元素,应把List定义为List。为了快速访问每个优先级的最后一个文档,集合List应最多包含10个元素,每个元素都引用不同优先级的最后一个文档。在后面的讨论中,对不同优先级的最后一个文档的引用称为优先级节点。

在上面的例子中,Document类扩展为包含优先级。优先级用类的构造函数设置:

public class Document

{

private string title;

public string Title

{

get

{

return title;

}

}

private string content;

public string Content

{

get

{

return content;

}

}

private byte priority;

public byte Priority

{

get

{

return priority;

}

实验十 泛型与集合框架

实验十泛型与集合框架 1.实验目的 1、掌握LinkedList类和Collections类提供的用于排序和查找链表中 的数据的方法 2、掌握用散列映射来存储数据 3、掌握TreeSet类的使用 2.实验内容 1、根据附录里的源代码,按照注释要求,完成代码填空,使程序能够运行 得出结果。 1)实验1 按身高排序 2)实验2 英汉小字典 3)实验3 演出节目单 4)实验4输出args[]中的单词 2、设计编写程序完成以下任务。 1)仿照实验1编写TV类,要求通过实现Comparable接口规定该类的对象的大小关系,按price值得大小确定大小关系,即电视机按其价格确定之间的大小关系。 2)从控制台输入若干个单词(输入回车结束)放入集合中,将这些单词排序后(忽略大小写)打印出来。 知识点:List接口的实现类、String常用方法 3)请使用LinkedList来模拟一个队列(先进先出的特性): (1)拥有放入对象的方法void put(Object o) (2)取出对象的方法Object get() (3)判断队列当中是否为空的方法boolean isEmpty();并且,编写测试代码,验证你的队列是否正确。 知识点:List接口的实现类LinkedList常用方法 4)在一个列表中存储以下元素:apple,grape,banana,pear (1)返回集合中的最大的和最小的元素 (2)将集合进行排序,并将排序后的结果打印在控制台上 知识点:Collections类中的方法 3.实验步骤 略 4.评分标准 1.A——内容功能完善,编程风格好,人机接口界面好; 2.B——内容功能完善,编程风格良好,人机接口界面良好;

JAVA实验报告-集合框架与泛型机制

Java 语言程序设计 C 实验报告 集合框架及泛型机制 学生姓名 专业、班级 指导教师 成绩 计算机与信息工程学院 年月日

一、实验目的 学习课程相关章节知识,通过上机练习,掌握以下知识: 1.掌握 List 接口下 ArrayList 及 LinkedList 的使用方法。 2.掌握 Map 接口下 HashMap 及 HashTable的使用方法 3.掌握集合中泛型的使用 二、实验内容 利用集合完成象数据库那样存储数据,并且可以简单查询,利用 map 存储学生信息,字段如下: id ,name,age,实现步骤: (1)创建类,类图如下: (2)在 main 方法编写逻辑代码 (3)运行程序并测试结果 package https://www.doczj.com/doc/0316495796.html,; public class Student { private String name ; private int age ; private String id ;

public String getName() { return name ; } public void setName(String name ) { this . name =name ; } public int getAge() { return age ; } public void setAge(int age ) { this. age=age ; } public String getId() { return id; } public void setId(String id) { this. id=id; } public Student(String name ,int age , String id ) { super(); this. name =name ; this. age=age ; this. id=id; } public void sayHi() { System.out.println("name=" +this.getName()+"age=" + this .getAge()+" " + "id=" + this.getId()); } }

实验11泛型与集合

实验内容: (1)定义一个泛型类Instrument,其中包括一个泛型方法void play(E x)。定义两种乐器类:Cello、Violin 可以进行演奏。定义一个测试类进行测试。 package ex111; public class Violin { public String toString() { System.out.println("violin........."); return ""; } } package ex111; public class Test_Main { public static void main(String[] args) { instrumentmodel = new instrument(); instrumentmodel1 = new instrument(); Cello cello = new Cello(); Violin violin = new Violin(); model.play(cello); model1.play(violin); } } package ex111; class instrument {//泛型类 void play(E x) { x.toString(); } } package ex111; public class Cello { public String toString(){ System.out.println("cello........."); return ""; } } (2)输入10 个数字保存到List 中,并按倒序显示出来。 package ex112; import java.util.ArrayList; import java.util.Collections;

实验11泛型与集合

实验内容: (1)定义一个泛型类In strume nt,其中包括一个泛型方法void play(E x) 定义两种乐器类:Cello、Violin可以进行演奏。定义一个测试类进行测试。 package ex111; public class Violin { public String toStri ng() { System.out.println("violin ....... "); return ""; } } package ex111; public class Test_Ma in { public static void main( Stri ng[] args) { instrumentmodel = new instrument(); in strume ntvVioli n> model1 = new in strume ntvVioli n>(); Cello cello = new Cello(); Violin violin = new Violi n(); model.play(cello); model1.play(violi n); } } package ex111; class in strume nt {//泛型类void play(E x) { x.toStri ng(); } } package ex111; public class Cello { public Stri ng toStri ng(){ System.out.println("cello ....... "); return ""; } } (2)输入10个数字保存到List中,并按倒序显示出来 package ex112; import java.util.ArrayList; import java.util.Collectio ns; import java.util.List; import java.util.Sca nner; public class Test{ public static void main(final String[] args) { final List< In teger> in tegers = new ArrayListvl nteger>(); final Scanner sca nner = new Scann er(System.i n); for (int i = 1; i <= 3; i++) {

Java集合框架实验报告

浙江大学城市学院实验报告 课程名称Java高级程序设计 实验项目名称Java集合框架实验 学生姓名专业班级学号 一、实验目的 1.理解Java集合框架的特点、接口与类之间的关系 2.掌握Java集合框架的List接口,以及List接口的重要实现类LinkedList、ArrayList 3.掌握Java集合框架的Set、SortedSet接口,以及重要实现类HashSet与TreeSet 4.掌握Java集合框架的Map、SortedMap接口及其重要实现类HashMap、TreeMap 5.掌握Java集合框架的Collection与Iterator接口的特点与使用方式 二、实验内容 1、使用List管理对象集合 2、使用Map管理对象集合 3、使用Set管理对象集合 4、设计一个自定义的集合类 三、实验步骤 1、在Eclipse中新建工程(即项目) 2、使用List管理对象集合 1)新建一个包listExample 2)在这个包中新建三个类:Student类,StudentList类,StudentListTest类。 参考代码: Student、java, StudentList、java,StudentListTest、java 3)完善上面三个类,相关要求参考源代码程序的注释,即根据要求修改源代码程序, 给出具体的实现代码(不使用泛型类)。

void addStudent(Student student){//添加一个学生对象 boolean a=true; for(int i=0;i

JAVA集合框架(精校版本)

集合框架 ?Java平台提供了一个全新的集合框架。“集合框架”主要由一组用来操作对象的接口组成。不同接口描述一组不同数据类型。它们都封装在java.util包中 核心接口 ?Java集合框架的核心接口有两种:Collection(聚集)和Map(映射) ?Collection 接口是一组允许重复的对象。 ?Set 中的数据对象没有顺序且不可以重复。接口 ?List中的数据对象有顺序且可以重复。接口 ?Map接口是一组成对的键-值对象,即所持有的是key-value pairs。Map中不能有重复的key。拥有自己的内部排列机制。不能有重复的键 Collection ?类java.util.Collections 提供了一些静态方法实现了基于List容器的一些常用算法?void sort(List l) 对List容器内的元素排序 ?void shuffle(List l) 对List容器内的对象进行随机排列 ?void reverse(List l) 对List容器内的对象进行逆续排列 ?void fill(List l, Object o) ?用一个特定的对象重写整个List容器 ?void copy(List dest,List src) ?将src List容器内容拷贝到dest List容器 ?int binarySearch(List l, Object o) ?对于顺序的List容器,采用折半查找的方法查找特定对象

例题: List l1 = new LinkedList(); List l2 = new LinkedList(); for(int i=0; i<=9; i++) { l1.add("a"+i); } System.out.println(l1); Collections.shuffle(l1); //随机排列 System.out.println(l1); Collections.reverse(l1); //逆续 System.out.println(l1); Collections.sort(l1); //排序 System.out.println(l1); System.out.println(Collections.binarySearch(l1,“a5”)); //折半查找List

实验11泛型与集合

实验内容: (1) 定义一个泛型类Instrument<E 〉,其中包括一个泛型方法void pla y(E x)。 定义两种乐器类:Cello、Violin 可以进行演奏。定义一个测试类进行测试. package ex111; public class Violin{ publicString toString() { System、out、println("violin、、、、、、、、、"); return ””; } } package ex111; public class Test_Main{ public staticvoid main(String[]args){ instrument〈Cello>model = new instrument〈Cello〉(); instrument

集合知识框架

内容 基本要求 集合的含义 会使用符号“∈”或“?”表示元素与集合之间的关系; 集合的表示 能选择自然语言、图形语言、集合语言描述不同的具体问题; 理解集合的特征性质,会用集合的特征性质描述一些集合,如常用数集,方程或不等式的解集等 集合间的基本关系 理解集合之间包含与相等的含义,及子集的概念.在具体情景中,了解空集和全集的含义; 理解两个集合的交集和并集的含义,会求两个简单集合的交集与并集.理解在给定集合中一个子集的补集的含义,会求给定子集的补集 集合的基本运算 掌握有关的术语和符号,会用它们表达集合之间的关系和运算.能使用维恩图表达集合之间的关系和运算. 1.集合:某些指定的对象集在一起成为集合。 (1)集合中的对象称元素,若a 是集合A 的元素,记作A a ∈;若b 不是集合A 的元素, 知识内容 高考要求 模块框架 集合

记作A b ?; (2)集合中的元素必须满足:确定性、互异性与无序性; 确定性:设A 是一个给定的集合,x 是某一个具体对象,则或者是A 的元素,或者不是A 的元素,两种情况必有一种且只有一种成立; 互异性:一个给定集合中的元素,指属于这个集合的互不相同的个体(对象),因此,同一集合中不应重复出现同一元素; 无序性:集合中不同的元素之间没有地位差异,集合不同于元素的排列顺序无关; (3)表示一个集合可用列举法、描述法或图示法; 列举法:把集合中的元素一一列举出来,写在大括号内; 例如:{1,2,3,4,5},{1,2,3,4,5,} 描述法:把集合中的元素的公共属性描述出来,写在大括号{}内。 例如:大于3的所有整数表示为:{Z |3}x x ∈> 方程2250x x --=的所有实数根表示为:{R x ∈|2250x x --=} 具体方法:在大括号内先写上表示这个集合元素的一般符号及取值(或变化)范围,再画一条竖线,在竖线后写出这个集合中元素所具有的共同特征。 注意:列举法与描述法各有优点,应该根据具体问题确定采用哪种表示法,要注意,一般集合中元素较多或有无限个元素时,不宜采用列举法。 (4)常用数集及其记法: 非负整数集(或自然数集),记作N ; 正整数集,记作N *或N +; 整数集,记作Z ; 有理数集,记作Q ; 实数集,记作R 。 <教师备案>⑴集合是数学中最原始的概念之一,不能用其他的概念给它下定义,所以集合是 不定义的概念,只能做描述性的说明. ⑵构成集合的元素除了常见的数、式、点等数学对象之外,还可以是其他任何..对象. 例:{小明,机器猫,哈里波特} ⑶正确认识一个集合的关键是理解集合中的元素特征. ①任何一个对象都能确定它是不是某一个集合的元素,这是集合中元素的最基本的特征——确定性,反例:“很小的数”,“个子较高的同学”; ②集合中的任何两个元素都是不同的对象,即在同一集合里不能重复出现相同元素——互异性,事实告诉我们,集合中元素的互异性常被忽略,从而导致解题出错.例:方程2(1)(2)0x x --=的解集不能写成{1,1,2},而应写成{1,2} ③在同一集合里,通常不考虑元素之间的顺序——无序性 例:集合{,,}a b c 与集合{,,}b c a 是相同集合 ⑷用描述法表示集合,对其元素的属性要准确理解. 例如:集合{}2x y x =表示自变量x 值的全体,即{}x x ∈R ;集合{} 2y y x =表示函数值y 的全体,即{}0y y ≥;集合{}2()x y y x =, 表示抛物线2y x =上的点的全体,是点的集合(一条抛物线);而集合{}2y x =则是用列举法表示

实验七:Java集合与泛型

实验七Java集合与泛型 一、实验目的 1)掌握集合的概念、体系结构、分类及使用场景 2)了解Set接口及主要实现类(HashSet、TreeSet) 3)了解List接口及主要实现类(ArrayList、LinkedList、Vector) 4)掌握ArrayList的使用 5)掌握ArrayList与Vector的区别 6)了解Map接口及主要实现类(HashMap、TreeMap、HashTable) 7)掌握HashMap的使用 8)掌握HashMap与HashTable的区别 二、实验环境 JDK1.6+Eclpise3.2 三、实验准备 1)复习课件中理论知识 2)练习课堂所讲的例子 四、实验内容 1、编写程序练习List集合的基本使用: 1) 创建一个只能容纳String对象名为names的ArrayList集合; 2)按顺序往集合中添加5个字符串对象:“张三”、“李四”、“王五”、“马六”、“赵七”; 3)对集合进行遍历,分别打印集合中的每个元素的位置与内容; 4)首先打印集合的大小,然后删除集合中的第3个元素,并显示删除元素的内容,然后再打印目前集合中第3个元素的内容,并再次打印集合的大小。 2、编写程序练习Map集合的基本使用: 1)创建一个只能值只能容纳String对象的person的HashMap集合; 2)往集合中添加5个“键-值”对象:id—>”1”、name—>”张三”、sex—>”男”、age—>”25”、love—>”爱学Java” 3)对集合进行遍历,分别打印集合中的每个元素的键与值; 4)首先打印集合的大小,然后删除集合中的键为age的元素,并显示删除元素的内容,并再次打印集合的大小。 五、验过程及结果 第1题调试结果如下图:

集合框架简介

结合框架体系应该最重要的是如何灵活利用三种接口,set,map,list,他们如何遍历,各有什么特征,怎么样去处理,这是关键,在这个基础上再去掌握在什么场合用什么类型的接口。比如说list和set,set是无序的一个空袋子,当我们只需要放入取出,这个接口当然是最实用的,但是如果我们需要按序取出,这个方法就不能用了,而要用到list,map接口就有个特点,它有一个特定的key值,而一个key值又对应着一个value,这个value值中就可以存入很多的东西了,比如姓名,出生年月,性别等,而且可以指定key取出对应的value!(自写) 如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。 如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类。 要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。 尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。 一、系统简述: 1.集合框架总体结构 Java中集合类定义主要是java.util.*包下面,常用的集合在系统中定义了三大接口,这三类的区别是: java.util.Set接口及其子类,set提供的是一个无序的集合; java.util.List接口及其子类,List提供的是一个有序的集合; java.util.Map接口及其子类,Map提供了一个映射(对应)关系的集合数据结构; 另外,在JDK5中新增了Queue(队列)接口及其子类,提供了基于队列的集合体系。每种集合,都可以理解为用来在内存中存放一组对象的某种”容器“---就像数组,就像前面我们自己定义的队列。 2.Set接口和List接口 Set是最简单的一种集合,它的对象不按特定方式排序,只是简单的把对象加入集合中,就像往口袋里放东西。对集中成员的访问和操作是通过集中对象的引用进行的,所以集中不能有重复对象;而LIst的主要特征是其对象以线性方式存储,没有特定顺序,只有一个开头和一个结尾,当然,它与根本没有顺序的集是不同的。列表在数据结构中分别表现为:数组和向量、链表、堆栈、队列。关于实现列表的集合类,是我们日常工作中经常用到的;从类图中可以看到,Set 和List都是继承了Collection接口的子接口,而这类集合都有自己的实现;

第10章机械加工工艺规程制定作业

第10章机械加工工艺规程制定作业 1.试述生产过程、工序、工步、走刀、安装、工位的概念。 答:生产过程是指由原材料到成品之间各个相互关联的劳动过程中的总和。 工序是批一人(或一组)工人,在一个工作地点(或一台机床),对同一个(或同时对几个)工件进行加工所连续完成的那一部分工艺过程。 工步是指在一个工序中,当加工表面不变、切削工具不变、切削用量中的进级量和切削速度不变的情况下所完成的那部分工艺过程。 走刀是指在一个工步内,如果被加工表面需切去的金属层很厚,一次切削无法完成,则应分几次切削,每进行一次切削就是一次走刀。 安装是指工件在加工之前,在机床或夹具上占据正确的位置(即为定位),然后加以夹紧的过程。 工位是指为了减少安装次数,常采用转位(移位)夹具、回转工作台,使工件在一次安装中先后处于几个不同的位置进行加工。工件在机床上所占据的每一个待加工位置称为工位。 2.什么是机械加工工艺过程?什么是机械加工工艺规程?工艺规程在生产中起什么作用? 答:机械加工工艺过程是指生产过程中直接改变生产对象的形状、尺寸、相对位置和性能等到,使其成为半成品或成品的过程,可分为铸造、锻造、冲压、焊接、机械加工、热处理、电镀、装配等工艺过程。 机械加工工艺规程是把工艺过程的各项内容用表格的形式固定焉,并用于指导和组织生产的工艺文件。 机械加工工艺规程作用如下:是指导生产的主要技术文件;是组织和管理生产的基本依据;是新建、扩建企业或车间的基本资料。 3.拟定机械加工工艺规程的原则与步骤有哪些? 答:制订机械加工工艺规程的原则是:充分考虑采取各种措施保证产品质量,以最低的成本保证所要求的生产率和年生产纲领,在制定工艺规程时,应尽力做到技术上先进,经济上合理并具有良好的生产条件。 步骤如下:分析零件图和产品装配图;选择毛坯;选择定位基准;拟定工艺路线;确定加工余量和工序尺寸;确定切削用量和工时定额;确定各工序的设备、刀具量具和辅助工具;

java集合详解

集合 版本号:1.0 作者:huangdos 日期:2006年6月06日

摘要 摘要内容 Java里面最重要,最常用也就是集会一部分了。能够用好集合和理解好集合对于做Java程序的开发拥有无比的好处。本文详细解释了关于Java中的集合是如何实现的,以及他们的实现原理。 关键字: Collection , List ,Set , Map , 集合,框架。

目录 1集合框架 (2) 1.1集合框架概述 (2) 1.1.1容器简介 (2) 1.1.2容器的分类 (3) 1.2C OLLECTION (5) 1.2.1常用方法 (5) 1.2.2迭代器 (8) 1.3L IST (10) 1.3.1概述 (10) 1.3.2常用方法 (10) 1.3.3实现原理 (14) 1.4M AP (18) 1.4.1概述 (18) 1.4.2常用方法 (18) 1.4.3Comparable 接口 (23) 1.4.4实现原理 (24) 1.4.5覆写hashCode() (29) 1.5S ET (32) 1.5.1概述 (32) 1.5.2常用方法 (33) 1.5.3实现原理 (36) 1.6总结:集合框架中常用类比较 (38) 2练习 (38) 3附录:排序 (40)

集合 1集合框架 1.1集合框架概述 1.1.1容器简介 到目前为止,我们已经学习了如何创建多个不同的对象,定义了这些对象以后,我们就可以利用它们来做一些有意义的事情。 举例来说,假设要存储许多雇员,不同的雇员的区别仅在于雇员的身份证号。我们可以通过身份证号来顺序存储每个雇员,但是在内存中实现呢?是不是要准备足够的内存来存储1000个雇员,然后再将这些雇员逐一插入?如果已经插入了500条记录,这时需要插入一个身份证号较低的新雇员,该怎么办呢?是在内存中将500条记录全部下移后,再从开头插入新的记录? 还是创建一个映射来记住每个对象的位置?当决定如何存储对象的集合时,必须考虑如下问题。 对于对象集合,必须执行的操作主要以下三种: ◆添加新的对象 ◆删除对象 ◆查找对象 我们必须确定如何将新的对象添加到集合中。可以将对象添加到集合的末尾、开头或者中间的某个逻辑位置。 从集合中删除一个对象后,对象集合中现有对象会有什么影响呢?可能必须将内存移来移去,或者就在现有对象所驻留的内存位置下一个“洞”。 在内存中建立对象集合后,必须确定如何定位特定对象。可建立一种机制,利用该机制可根据某些搜索条件(例如身份证号)直接定位到目标对象;否则,便需要遍历集合中的每个对象,直到找到要查找的对象为止。 前面大家已经学习过了数组。数组的作用是可以存取一组数据。但是它却存在一些缺点,使得无法使用它来比较方便快捷的完成上述应用场景的要求。 1.首先,在很多数情况下面,我们需要能够存储一组数据的容器,这一点虽然数组可以实现,但是如果我们 需要存储的数据的个数多少并不确定。比如说:我们需要在容器里面存储某个应用系统的当前的所有的在线用户信息,而当前的在线用户信息是时刻都可能在变化的。也就是说,我们需要一种存储数据的容器,它能够自动的改变这个容器的所能存放的数据数量的大小。这一点上,如果使用数组来存储的话,就显得十分的笨拙。 2.我们再假设这样一种场景:假定一个购物网站,经过一段时间的运行,我们已经存储了一系列的购物清单 了,购物清单中有商品信息。如果我们想要知道这段时间里面有多少种商品被销售出去了。那么我们就需要一个容器能够自动的过滤掉购物清单中的关于商品的重复信息。如果使用数组,这也是很难实现的。 3.最后再想想,我们经常会遇到这种情况,我知道某个人的帐号名称,希望能够进一步了解这个人的其他的 一些信息。也就是说,我们在一个地方存放一些用户信息,我们希望能够通过用户的帐号来查找到对应的该用户的其他的一些信息。再举个查字典例子:假设我们希望使用一个容器来存放单词以及对于这个单词的解释,而当我们想要查找某个单词的意思的时候,能够根据提供的单词在这个容器中找到对应的单词的解释。如果使用数组来实现的话,就更加的困难了。 为解决这些问题,Java里面就设计了容器集合,不同的容器集合以不同的格式保存对象。

学号姓名--集合框架与泛型实验报告

浙江大学城市学院实验报告 课程名称面向对象程序设计 实验项目名称集合框架与泛型 学生姓名专业班级学号 一. 实验目的和要求 1. 了解Java集合框架的接口和实现类 2. 理解泛型类、泛型接口、泛型方法的特点 3. 掌握List接口及其实现类LinkedList、ArrayList 4. 了解Set接口及其实现类HashSet、TreeSet 5. 了解Map及其实现类HashMap、TreeMap 二. 实验内容 1. 分析Java集合框架的接口和实现类的组成 2. 分析泛型类、泛型接口、泛型方法的特点 3. 编程实现:设计学生管理类StudentManager(用List集合管理学生对象) 4. 选作-编程实现:设计学生管理类StudentManager(用Set集合管理学生对象) 5. 选作-编程实现:设计学生管理类StudentManager(用Map管理学生对象) 三. 实验结果与分析(可将程序运行结果截屏,也可分析运行结果) 1. 分析Java集合框架的接口和实现类的组成 请查阅书籍和Java帮助文档,说明Java集合框架的接口组成以及它们的继承关系,并针对每个接口给出具体的实现类。 答: 2. 分析泛型类、泛型接口、泛型方法的特点 请查阅书籍和Java帮助文档,举例说明泛型类、泛型接口、泛型方法的特点。 答: 3. 编程实现:设计学生管理类StudentManager(用List集合管理学生对象)。 StudentManager类的功能包括添加学生、查询学生、删除学生、统计学生成绩等。需要设计表示学生对象的Student类,并用LinkedList或ArrayList集合来管理可被数量的学生对象。另外还需要设计测试类Test来验证StudentManager的功能。 4. 编程实现:设计学生管理类StudentManager(用Set集合管理学生对象)。具体功能 要求同第3题,但是需要用Set的实现类(比如HashSet、TreeSet)管理学生对象。

Java集合框架的线程安全

集合框架的线程安全Java Java集合框架的线程安全 周庆岳 Java集合框架是由Java平台标准版1.2 (Java SE 1.2)引入的通用数据结构与算法框架。其灵活的面对对象设计受到了广大Java程序员的一致青睐,为Java

平台的成熟奠 定了坚实的基础。 一个集合(也称容器)是指将一组元素组合成一个单元的简单对象。集合用于存储、取回、操作和传递这些聚合的元素。集合框架是指一个统一的用来表示和操作集合的体系结构[Bloch,1999]。最简单的集合如数组、列表和队列等,集合框架最著名的例子如C++ 标准库(STL)。 线程安全不是一个全有或全无的问题,难以对其进行精确的定义。线程安全笼统地讲是指程序在多线程环境下运行时的正确性。Java集合框架的设计者Bloch Joshua在他著名的《Java高效编程》一书中对Java线程安全的等级做出了相对精确的定义[Bloch,2001]:非可变、线程安全、条件线程安全、线程兼容和线程不友好。 本文将结合上述Bloch关于线程安全等级的定义,对Java集合框架中的集合类进行线程安全性分析,并指出各个集合类在现实的编程环境中需要注意的并发

编程的陷阱;同时对集合框架中通用算法对线程安全性的影响进行分析。所涉及的集合类不仅包括Java SE 1.2引入的集合类,还包括旧集合类(Java SE 1.2前引入)和新集合类(Java SE 5引入)。从而帮助Java程序员在进行并发编程时更加高效地利用Java集合框架。 Java线程安全的等级定义 根据Bloch的定义,将线程安全分为五个等级,下面将给出这五个等级的描述和部分示例。 1、非可变 如果一个类的所有实例对于调用它们的客户端对象总是恒定不变的,而无需外部同步,则称为非可变的。字符串类和整数类都是非可变的,但在集合框架中并没有提供直接的非可变类,而是通过对可变类进行封装而得到非可变类。 非可变集合不可修改,因而它可以在各个线程间安全共享而无需额外的同步。作为一个好的实践准则,一旦生成非可变类之后,不要再持有被其封装的集合类的 引用,这样才可以完全保证其非可变性。2、线程安全 类的实例是可变的,但它的所有方法已经通过使用足够的内部同步使其实例可以被并发的使用而无需外部同步。并发的调用将会以某种全局一致的方式连续地执行。随机类和 定时器类都是线程安全类。集合框架中线程安全的类并发哈希映射类在Java SE 5 中被引入,它并不包含在原来的集合框架中,但它实现了集合框架Map接口。并发哈希映射类实现了并发和效率之间的高效平衡,已被作为哈希表类和同步映射表封装在并发环境下的高效替代品。 3、条件线程安全 - 2 - 除了某些方法需要在没有其它线程的干扰的情况下顺次执行之外,条件线程安全类和线程安全类类似。为了消除线程干扰的可能性,客户端对象在调用这类方法的过程中需要获得该集合类对象的锁来进行同步。一些旧集合类如Vector和Hashtable都是条件线程安全类,对这些集合类进行遍历操作时需要对其进行外部同步。 4、线程兼容 对其对象实例的所有方法调用都通过外部同步之后再进行,线程兼容类可以安全

实验五 集合类与泛型

实验五集合类与泛型 1.实验目的 (1)掌握ArrayList类与LinkedList类的用法; (2)掌握TreeSet类的用法; (3)掌握Map接口及其实现类的用法 (4)掌握泛型的用法。 2.实验内容 实验题1 有四个类,主类Store在包https://www.doczj.com/doc/0316495796.html,.nwsuaf.jp.p4中,Mobile、Mp3Player、Product在包https://www.doczj.com/doc/0316495796.html,.nwsuaf.jp.p4.data中,Mobile、Mp3Player是Product的子类, Product类实现Comparable接口,重写了Comparable接口中方法compareTo,实现了product对象按照价格排序。 基本要求: (1)在主类Store中实例化多个Mobile类与Mp3Player的实例,分别将这些实例用ArrayList与LinkedList存放,最后用StringBuiler存放并将其输出。 (2)用迭代器(iterator)将实例对象输出(要求用加强型for循环)。 Store类 package https://www.doczj.com/doc/0316495796.html,.nwsuaf.jp.p44; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import javax.swing.JOptionPane;

import https://www.doczj.com/doc/0316495796.html,.nwsuaf.jp.p4.data.Mobile; import https://www.doczj.com/doc/0316495796.html,.nwsuaf.jp.p4.data.Mp3Player; import https://www.doczj.com/doc/0316495796.html,.nwsuaf.jp.p4.data.Product; public class Store { public static int count=0; public static void main(String[] args) { Mobile m1=new Mobile("E365 on china Mobile",1780); Mobile m2=new Mobile("E3330 on china Mobile",1450); Mp3Player mp1=new Mp3Player("Meiz0 X3 (256MB)",399); Mp3Player mp2=new Mp3Player("Meiz0 E5 (512MB)",580); Mp3Player mp3=new Mp3Player("Xlive XM Mp3Player(256MB)",930); Product p[]={mp2,mp1,mp3,m1,m2}; Arrays.sort(p); StringBuilder text=new StringBuilder(); for(Product i:p){ text.append(i).append("\n"); } System.out.println("用StringBuiler输出:\n"+text); List list1=new ArrayList();//用到泛型因为Product是mp3和mobil的总类,ArrayList只能放一种类型 List list2=new LinkedList(); for(int i=0;i

Java 集合框架类图

Collection <>Deque <>Queue <>List <>Set <>SortSet <> Iterable <> ArrayDeque <> LinkedList PriorityQueue ArrayList <> Stack Vector <> EnumSet> <> HashSet LinkedHashSet NavigableSet <> TreeSet <> <> Map <>EnumMap,V> <> HashMap <> LinkedHashMap TreeMap <> SortedMap <>NavigableMap <> <> <> <> Hashtable <> IdentityHashMap <> WeakHashMap <> Java 集合框架类图

介绍Collection框架的结构

介绍Collection框架的结构;Collection 和 Collections的区别。 集合框架:Collection:List列表,Set集 Map:Hashtable,HashMap,TreeMap Collection 是单列集合 List元素是有序的、可重复 有序的 collection,可以对列表中每个元素的插入位置进行精确地控制。可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。 可存放重复元素,元素存取是有序的。 List接口中常用类 ●?Vector:线程安全,但速度慢,已被ArrayList替代。 底层数据结构是数组结构 ●?ArrayList:线程不安全,查询速度快。底层数据结构是数组结构 ●?LinkedList:线程不安全。增删速度快。底层数据结构是列表结构 Set(集)元素无序的、不可重复。 取出元素的方法只有迭代器。不可以存放重复元素,元素存取是无序的。 Set接口中常用的类 ●?HashSet:线程不安全,存取速度快。 它是如何保证元素唯一性的呢?依赖的是元素的hashCode方法和euqals方法。 ●?TreeSet:线程不安全,可以对Set集合中的元素进行排序。 它的排序是如何进行的呢?通过compareTo或者compare方法中的来保证元素的唯一性。元素是以二叉树的形式存放的。 Map是一个双列集合 |--Hashtable:线程安全,速度快。底层是哈希表数据结构。是同步的。 不允许null作为键,null作为值。 |--Properties:用于配置文件的定义和操作,使用频率非常高,同时键和值都是字符串。 是集合中可以和IO技术相结合的对象。(到了IO在学习它的特有和io相关的功能。) |--HashMap:线程不安全,速度慢。底层也是哈希表数据结构。是不同步的。 允许null作为键,null作为值。替代了Hashtable. |--LinkedHashMap: 可以保证HashMap集合有序。存入的顺序和取出的顺序一致。 |--TreeMap:可以用来对Map集合中的键进行排序. Collection 和 Collections的区别 Collection是集合类的上级接口,子接口主要有Set 和List、Map。 Collections是针对集合类的一个帮助类,提供了操作集合的工具方法:一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。

相关主题
文本预览
相关文档 最新文档