您的位置:首页 > 编程语言 > Java开发

Java — 集合(1)(Core Java I)

2015-09-19 10:50 429 查看
13.1 集合接口

本节将介绍Java结合框架的基本设计,展示使用他们的方法,并解释一些颇具争议的特性背后的考虑。

13.1.1 将集合的接口与实现分离

队列接口指出可以在队列的尾部添加元素,在队列的头部删除元素,并且可以查找队列中元素的个数。

队列的实现方式一般有两种:一种是循环数组;另一种的链表实现。

接口集合与实现在概念上的不同:如果需要一个循环数组队列->ArrayDeque类来实现;如果需要一个

链表队列,就直接使用->LinkeList类。这两个类实现了Queue接口。

我们说,接口的本身并不能说明哪种实现的效率高,循环数组要比链表更高效,但是容量有限,链表

相反。如果程序中要收集的对象数量没有上限,最好使用链表来实现。

13.1.2 Java类库中的集合接口和迭代器接口

集合类的基本接口就是Collection接口。

public interface Collection<E>{
boolean add(E element);
Iterator<E> iterator();
...
}



add 方法用于向集合中添加元素。添加元素成功返回ture,未成功【集合没有发生变化】返回false。例如

向集合中添加一个对象,而这个对象在集合中已经存在,这个添加没有成功,因为集合中不允许有重复的对象。

iterator 方法用于返回一个 实现了Iterator接口的对象。可以使用这个迭代器对象依次访问集合中的元素。

1)迭代器

Iterator接口包含3个方法:

public interface Iterator<E>{
E next();
boolean hasNext();
void remove();
}



next 方法可以逐个访问集合中的每个元素,但是到了集合的末尾,next方法将抛出一个NoSuchElementException。

因此,需要在调用next之前调用hasNext方法。

hasNext 方法 用于检查序列中是否还有元素。

Collection<String> c = ...;
Iterator<String> iter = c.iterator();
while(iter.hasNext()){
String element = iter.next();
do something with element
...
}




注:

从Java SE 5.0 起,这个循环可以采用一种更加优雅的缩进方法。

for(String element :c){
do something with element
...
}



编译器简单地将"for each" 循环翻译为带有迭代器的循环。

Collection接口扩展了Iterable接口。因此,对于标准库中的任何集合都可以使用"for each" 循环。

元素的访问的顺序取决于集合类型。ArrayList迭代,迭代器从索引 0 开始,每迭代一次,索引值 加1。

HashSet迭代之后,每个元素将会按照 某种随机的次序出现。但是对于计算总和 或者 统计某个条件的

元素个数这类与顺序无关来说不是什么问题。

2)删除元素

remove 方法用于删除上次调用next方法是返回的元素。如果想要删除指定位置上的元素,仍然需要越过这个元素。

if.remove();
it.remove();//错误
//删除两个相邻的元素
it.remove();
it.next();
it.remove();//正确



3)泛型实用方法

下面十一检测任意集合是否包含指定元素的泛型方式

//这样做的好处就是不必自己重新构建这些方法了。contains就是这样一个方法
public static <E> boolean contains(Collection<E> c,Object obj){
for(E element: c)
if(element.equals(obj))
return true;
return false;
}


Collection接口声明了很多有用的方法,所有实现类都必须提供这些方法。下面例举一部分:

int size();
boolean isEmpty();
boolean contains(Object obj);
boolean containsAll(Collection<?> c)
boolean equals(Object other);
boolean addAll(Collection<? extends E> from);
boolean remove(Object obj);
boolean removeAll(Collection<?> c>;
void clear();<pre name="code" class="java">





如果我们在实现每个类都要提供如此多的例行方法将是很麻烦的。为了实现者能够更容易地实

现这个接口,java库类提供了一个类AbstractCollection,它将基础方法size() 、iterator()抽象化。

public abstract class AbstractCollection<E> implements Collection<E>{
...
public abstract Iterator<E> iterator();
public boolean contains(Object obj){
for(E element: this)//迭代器 iterator
if(element.equals(obj))
return true;
return false;
}
...
}

boolean retainAll(Collection<?> c);
Object[] toArray();
<T> T[] toArray(T[] arrayToFill);



一个具体的集合类可以扩展AbstractCollection类。现在要由具体的集合类提供iterator方法,contains方法

是由AbstractCollection超类提供。如果子类有更加有效的方式去实现contains方法也可以由子类提供,这点

没有任何限制。

对于类框架来说,这个设计很好。集合类用户可以使用泛型接口(Collection 、Iterator等)中一组更加丰富的方法,而实际的数据结构

实现者并没有需要实现所有例行方法的负担。

13.2.1 链表

由于最近数据结构老师在将链表,对于c实现的链表的创建、查找、删除、略微懂,这里不说了。

链表 和 顺序表 相比,优势在于在一组数据中间添加和删除一个元素的代价。

下面代码实例中,先添加3个元素,再将第二个元素删除:

package com.test1;

import java.util.*;
/**
* linkedList 链表
* 一些需要注意的点:
* 1.链表与泛型集合的重要区别:链表是ordered collection 每个对象的位置很重要
* 2.链表的缺点是不能高效的随机访问对象元素,虽然LinkedList类提供了get方法,但其效率不敢恭维
* 3.使用链表的唯一理由就是 减少在列表中间插入或删除元素所付出的代价
* 4.List 中只有少数几个元素 我们可以使用ArrayList
* @author PeersLee
*	该代码首先创建两个链表,合并,然后从第二个链表中每隔一个元素删除一个元素,最后测试removeAll
*/
public class test2 {

public static void main(String[] args) {
// TODO Auto-generated method stub
List<String> staff = new LinkedList<>();
staff.add("A");
staff.add("B");
staff.add("C");
ListIterator<String> iter = staff.listIterator();
String first = iter.next();
System.out.println("first:"+first);
String second = iter.next();
System.out.println("second:"+second);
//注意此时挡板在 AB|C
iter.previous();//此方法和next()一样,返回刚越过的对象
//A|BC
String testString = iter.previous();
//|ABC
System.out.println("testString:"+testString);
int ni = iter.nextIndex();
System.out.println("ni:"+ni);
int pi = iter.previousIndex();
System.out.println("pi:"+pi);
//ni = 0
//pi =-1
//说明游标在初始位置

}

}




链表与泛型集合之间有一个重要的区别。链表有序,对象的位置很重要。LinkedList.add() 方法将对象添加到链表的尾

部。如果将元素添加到链表的中间时,由于迭代器是描述集合中位置的,所以这种依赖位置的add()方法将由迭代器负

责。只有对自然有序的集合使用迭代器添加元素才有实际意义。集(set)类型,其中元素完全无序,因此,在Iterator

接口中就没有add()方法。然而子接口ListIterator包含add()方法[这个方法不返回boolean类型的值,它假定添加操作总

会改变链表],并且还可以反向的遍历链表。

下面的代码实现ListIterrator中的一些方法:

package com.test1;

import java.util.*;
public class test2 {

public static void main(String[] args) {
// TODO Auto-generated method stub
List<String> staff = new LinkedList<>();
staff.add("A");
staff.add("B");
staff.add("C");
ListIterator<String> iter = staff.listIterator();
String first = iter.next();
System.out.println("first:"+first);
String second = iter.next();
System.out.println("second:"+second);
//注意此时挡板在 AB|C
iter.previous();//此方法和next()一样,返回刚越过的对象
//A|BC
String testString = iter.previous();
//|ABC
System.out.println("testString:"+testString);
int ni = iter.nextIndex();
System.out.println("ni:"+ni);
int pi = iter.previousIndex();
System.out.println("pi:"+pi);
//ni = 0
//pi =-1
//说明游标在初始位置

}

<span style="font-size:18px;">}</span>


小结:
虽然链表随机访问元素的效率十分低,但是LinkedList还是提供了一个 get();方法,当然,如果你使用了这个方法

就说明你选择错了数据结构。

使用链表的唯一理由就是尽可能降低 在列表中间插入或者删除元素 的代价,如果链表只有几个元素,完全可以

使用ArrayList()。

下面的代码是通过调用AbstractCollection类中的toString方法打印出链表a中的所有元素:

package com.test1;

import java.util.*;

public class test3 {

public static void main(String[] args) {
// TODO Auto-generated method stub
List<String> a = new LinkedList<>();
a.add("A");
a.add("B");
a.add("C");

List<String> b = new LinkedList<>();
b.add("D");
b.add("E");
b.add("F");
b.add("G");
//将b中的对象移动到 a中
ListIterator<String> aIter = a.listIterator();
Iterator<String> bIter = b.iterator();

while(bIter.hasNext()){
if(aIter.hasNext()) aIter.next();
//个人理解 游标在新插入对象右边
aIter.add(bIter.next());
}
System.out.println(a);
System.out.println(b);
//删除位置代号是偶数的对象(坐标从‘1’开始)
bIter = b.iterator();
while(bIter.hasNext()){
bIter.next();
if(bIter.hasNext()){
bIter.next();
bIter.remove();
}
}
System.out.println(b);
//从链表a中将b中的所有元素删除
a.removeAll(b);
System.out.println(a);
}

}


13.2.2 数组列表

ArraysList 封装了一个动态再分配的对象数组。
ArraysList方法不是同步的,在需要同步时建议使用Vector。

13.2.3 散列表

散列表可以用于实现几个重要的数据结构,例如 set类型。
set 是没有重复元素的元素集合。
set的add方法首先在集合中查找要添加的元素,若不存在则将其加入

package com.test3;

import java.util.*;
/**
* 散列集
* 如果不在乎元素的顺序,则有几种能够快速查找元素的数据结构,他们是按照有利于其操作目的的原则组织数据
* HashTable 散列表 快速查找所需要的对象
* 散列码 由 hashCode 函数得出  由对象的实例域产生的一个整数
* 每个列表被称为一个 bucket 桶
* 要计算出对象的位置就要就算出散列码,与桶的总数取余,所得的结果就是保存这个元素的一个引索
*
* @author PeersLee
*
*该程序实现 读取输入的所有单词,将他们添加到散列集中,遍历,打印出数量
*/
public class SetTest {

public static void main(String[] args) {
// TODO Auto-generated method stub
//HashSet 实现Set 其底层实现为HashMap
Set<String> words = new HashSet<>();
long totalTime = 0;

Scanner in = new Scanner(System.in);
/*
* hasNext()
* Returns true if this scanner has another token in its input.
*/
int n = 1;
while(in.hasNext() && n <= 20) {
/*
* next()
* Finds and returns the next complete token from this scanner.
*/
String word = in.next();
/*
* currentTimeMillis()
* Returns the current time in milliseconds.
*/
long callTime = System.currentTimeMillis();
words.add(word);
callTime = System.currentTimeMillis() - callTime;
totalTime += callTime;
n++;
}
Iterator<String> iter = words.iterator();
for(int i = 1; i <= 20 && iter.hasNext(); i++) {
System.out.println(iter.next());
System.out.println("...");
System.out.println(words.size() + " distinct words(不同的词) " + totalTime + " milliseconds.");
}

}

}


13.2.4 树集 TreeSet

树集 TreeSet 也是个有序集合,其排序是树(red-black tree)机构完成的。
红黑树(/article/1362943.html)-> 每次将一个元素添加到树中时,都将其放在正确的排序位置。
将元素加入到TreeSet中要比HashSet中慢,不过TreeSet可以自动的对元素进行排序。

13.2.5 对象的比较

树集假定插入的元素实现了Comparable接口。
树的排序是全序的,任意两个元素必须是可比的。

目录:



TreeSetTest:

package com.test1;

import java.util.*;
/**
* 1.创建了两个Item对象的树集
* 2.第一个按照部件的编号排序,这是 Item 对象的默认顺序
* 3.第二个是通过定制的比较器来按照描述信息排序
* @author PeersLee
*
*/
public class TreeSetTest {

public static void main(String[] args) {
// TODO Auto-generated method stub
/**
* SortedSet是个接口,它里面的(只有TreeSet这一个实现可用)中的元素一定是有序的。
*  TreeSet类实现Set 接口,该接口由TreeMap 实例支持。此类保证排序后的 set 按照升序排列元素
*/
SortedSet<Item> parts = new TreeSet<>();
parts.add(new Item("Toaster[烤面包片机]", 1234));
parts.add(new Item("Widget[装饰品]", 4567));
parts.add(new Item("Modem[调制解调器]", 6789));
System.out.println("parts: " + parts);

SortedSet<Item> sortByDescription = new TreeSet<>(new Comparator<Item>() {
public int compare(Item a, Item b) {
String descrA = a.getDescription();
String descrB = b.getDescription();
return descrA.compareTo(descrB);
}
});

sortByDescription.addAll(parts);
System.out.println(sortByDescription);
}

}


Item

package com.test1;

import java.util.*;

public class Item implements Comparable<Item> {

//partNumber -> 零件号码
private String description;
private int partNumber;

// construct an item.
public Item(String aDescription, int aPartNumber) {
description = aDescription;
partNumber = aPartNumber;
}

//Gets
public String getDescription() {
return description;
}

public String toString() {
return "[description=" + description +", partNumber=" + partNumber +"]";
}

public boolean equals(Object otherObject) {

if(this == otherObject) return true;
if(otherObject == null) return false;
if(getClass() != otherObject.getClass()) return false;

Item other = (Item)otherObject;
return Objects.equals(description, other.description) && partNumber == other.partNumber;
}

public int hashCode() {
return Objects.hash(description, partNumber);
}

public int compareTo(Item other) {
return Integer.compare(partNumber, other.partNumber);
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: