【概述】
1、通常,程序需要根据运行时才知道的某些条件去创建新对象。在此之前,不知道所需对象的数量,甚至不知道确切的类型。2、 容器:Java实用类库提供了一套相当完整的 容器类来解决这个问题。基本的类型包括: List、Set、Queue和Map,这些对象称为 集合类。 用 “容器”来称呼他们。
3、容器类都可以自动的调节自己的尺寸。编程时,可以将任意数量的对象放到容器中,而不需要担心放不下。
【泛型和类型安全的容器】
1、Java SE5之前,编译器允许向容器中插入不正确的类型。
2、以下例子会编译报警
1)Apple和Orange类除了都是Object外没有共性。因为ArrayList中保存的是Object,因此通过Add可以放Apple,也可以放Orange。
2)所以,通过 get()取出来的值只是Object引用, 必须强制转化为Apple。
//放Orange到Apple里的不良例子
import java.util.
*;
class Apple{
private
static
long counter;
private
final
long id
= counter
++;
public
long id() {
return id;}
}
class Orange{}
public
class ApplesAndOrangeWitchoutGenerics{
public
static
void main(
String[] args){
ArrayList apples
=
new ArrayList();
//定义一个Apple类型的ArrayList对象 Apples
for(
int i
=
0; i
<
3; i
++)
{
apples.add(
new Apples());
}
apples
.
add
(
new
Orange
()); //把一个桔子放进了苹果的类型的ArrayList中。
for (
int i
=
0; i
< apples.size(); i
++)
{
((
apple
)
apples
.
get
(
i
)).id();
}
}
}
3、使用预定的泛型,可以避免这种个问题。
定义保存Apple对象的ArrayList,申明形式为: ArrayList<Apple> 。其中尖括号括起来的是 类型参数。 指定了容器实例可以保存的类型。
4、虽然用泛型约束了类型,但不代表不能完全放其他类型,向上转型可以。
即:基类的泛型可以放子类的
定义保存Apple对象的ArrayList,申明形式为: ArrayList<Apple> 。其中尖括号括起来的是 类型参数。 指定了容器实例可以保存的类型。
//使用了泛型的例子
import java.util.
*;
class Apple{
private
static
long counter;
private
final
long id
= counter
++;
public
long id() {
return id;}
}
class Orange{}
public
class ApplesAndOrangeWitchoutGenerics{
public
static
void main(
String[] args){
ArrayList
<Apple
> apples
=
new ArrayList
<Apple
>();
//用泛型定义实例
for(
int i
=
0; i
<
3; i
++)
{
apples.add(
new Apples());
}
apples.add(
new Orange());
//----这里编译器会报错
for (
int i
=
0; i
< apples.size(); i
++)
{
//((apple)apples.get(i)).id();
System.out.println(apples.get(i).id());
//--这里没有用强制转换
}
for(Apple c:apples)
System.out.print(c.id());
//这里用for each形式,代替get
}
}
4、虽然用泛型约束了类型,但不代表不能完全放其他类型,向上转型可以。
即:基类的泛型可以放子类的
//使用泛型,但插入子类对象
impo java.util.
*
class GrannySmit
extends Apple{}
//继承
class Gala
extends Apple{}
//继承
class Fuji
extends Apple{}
//继承
class Braeburn
extends Apple{}
//继承
public
class GenericsAndUpcasting{
public
static
void main(
String[] args){
ArrayList
<Apple
> apples
=
new ArrayList
<Apple
>();
apples.add(
new GrannySmit());
//插入子类对象
apples.add(
new Gala());
apples.add(
new Fuji());
apples.add(
new Braeburn());
for(Apple c:apples)
{
System.out.println(c);
}
}
}
GrannySmith@7d772f
Gala@2323
Fuji@1134de
Braeburn@74eaf
System.out.println,输出的是 默认的Object的toString()方法产生的 ,打印类名+该对象的散列码的无符号16进制表示。
1)Collection: 一个独立的元素序列。都有一条或者多条规则,比如:
List必须按照插入的顺序保存元素
Set不能有重复元素
Queue按照排队规则来确定对象产生的顺序(通常与插入顺序相同)
2)Map:一组成对的“键值对”对象。 允许使用键来查找值。ArrayList允许使用数字来查找值。
映射表:允许使用另一个对象来查找某个对象,也叫做“ 关联数组”,或者被称为“ 字典”
2、唯一需要指定所使用的精确类型的地方就是在创建时候。
List<Apple> apples = new ArrayList<Apple>();
LIst<Apple> apples = new LinkedList<Apple>();
这里的List可以看做接口。ArrayList和LInkedList都向上转型了。但是: 他们所具备的特有方法将不能被使用。如果要使用,就不能向上转型。
3、 Collection概括了序列的概念。下面是个例子:
2、Arrays.asList()方法接受一个 数组或者是一个用逗号分开的元素列表,将其转换成一个List对象。
Collections.addAll()接受 一个Collection对象,以及一个数组或者一个逗号分开的列表。(比上面多一种)
3、以下例子所表达的关键信息
1)Collection的泛型,在定义时,可以在其初始化时,用Arrays.asList()来产生输入。
2)也可以通过对象调用addAll(),以Arrays.asList()为参数,为其添加元素。当然,Arrays.asList()的参数可以是一个数组,或者一组逗号分开的元素列表
3) Collections.addAll(),运行要快得多。作为首选方法。第一个参数是collection,另一个参数可以是元素列表或数组。
4)Arrays.asList()和Collection.addAll()都可以使用可变参数列表,比较方便。
5) 如果直接用Arrays.asList()的输出当做List,因为底层表示是数组,所以不能调整尺寸。
[Hippo1, seesaw1]
call Collection fill()--ArrayList
[Hippo2, Pig2, Dog2, Dog2]
call Collection fill()--LinkedList
[Hippo2, Pig2, Dog2, Dog2]
call Collection fill()--Hashset
[Pig2, Dog2, Hippo2]
call Collection fill()--TreeSet
[Dog2, Hippo2, Pig2]
call Collection fill()--LinkedHashSet
[Hippo2, Pig2, Dog2]
call Collection fill()--HashMap
{rat=Kitty, cat=Kitty, girl=Mili}
call Collection fill()--TreeMap
{cat=Kitty, girl=Mili, rat=Kitty}
call Collection fill()--LinkedHashMap
{rat=Kitty, cat=Kitty, girl=Mili}
注意在定义类时,要有toString,才能打印所需信息,否则就是打印类地址。
【PriorityQueue】
Gala@2323
Fuji@1134de
Braeburn@74eaf
System.out.println,输出的是 默认的Object的toString()方法产生的 ,打印类名+该对象的散列码的无符号16进制表示。
【基本概念】
1、容器类库可以分为两种概念1)Collection: 一个独立的元素序列。都有一条或者多条规则,比如:
List必须按照插入的顺序保存元素
Set不能有重复元素
Queue按照排队规则来确定对象产生的顺序(通常与插入顺序相同)
2)Map:一组成对的“键值对”对象。 允许使用键来查找值。ArrayList允许使用数字来查找值。
映射表:允许使用另一个对象来查找某个对象,也叫做“ 关联数组”,或者被称为“ 字典”
2、唯一需要指定所使用的精确类型的地方就是在创建时候。
List<Apple> apples = new ArrayList<Apple>();
LIst<Apple> apples = new LinkedList<Apple>();
这里的List可以看做接口。ArrayList和LInkedList都向上转型了。但是: 他们所具备的特有方法将不能被使用。如果要使用,就不能向上转型。
3、 Collection概括了序列的概念。下面是个例子:
//Collection 概括了序列概念的例子
import java.util.
*
public
static
void main(
String[] args)
{
Collection
<
Integer
> c
=
new ArrayList
<
Integer
>();
for(
int i
=
0; i
<
10; i
++)
c.add(i)
for(
Integer i:c)
System.out.println(i
+
",");
}
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
【添加一组元素】
1 、java.util的Arrays和Collection类都有一些可以添加 一组元素(意思是多个)的方法。2、Arrays.asList()方法接受一个 数组或者是一个用逗号分开的元素列表,将其转换成一个List对象。
Collections.addAll()接受 一个Collection对象,以及一个数组或者一个逗号分开的列表。(比上面多一种)
3、以下例子所表达的关键信息
1)Collection的泛型,在定义时,可以在其初始化时,用Arrays.asList()来产生输入。
2)也可以通过对象调用addAll(),以Arrays.asList()为参数,为其添加元素。当然,Arrays.asList()的参数可以是一个数组,或者一组逗号分开的元素列表
3) Collections.addAll(),运行要快得多。作为首选方法。第一个参数是collection,另一个参数可以是元素列表或数组。
4)Arrays.asList()和Collection.addAll()都可以使用可变参数列表,比较方便。
5) 如果直接用Arrays.asList()的输出当做List,因为底层表示是数组,所以不能调整尺寸。
package C_11;
import java.util.
*;
public
class AddinGroups {
public
static
void main(
String[] args) {
// TODO Auto-generated method stub
//用Arrays.asList,为新的ArrayList直接赋值
Collection
<
Integer
> collection
=
new ArrayList
<
Integer
>(Arrays.asList(
1,
2,
3,
4,
5));
Integer[] moreInts
= {
6,
7,
8,
9,
10};
collection.addAll(Arrays.asList(moreInts));
Collections.addAll(collection,
11,
12,
13,
14,
15);
Collections.addAll(collection,moreInts);
List
<
Integer
> list
= Arrays.asList(
16,
17,
18,
19,
20);
list.set(
1,
9);
//list.add(21); //运行时出错,不能resized
for(
Integer c:collection)
System.out.println(c);
}
}
4、Arrays.asList()方法有限制:
它对所产生的List的类型做出最理想的假设
,而不一定管赋值给它什么类型。
----这一段和感觉和书上讲的有点不一样。感觉书上想说的是,Arrays.asList参数应该和List<>的保持一致,不然编译不过。不过这里验证,没有出现这个问题。
怎么回事(先放着吧,后面更纯熟后回来看)
?
import java.util.Arrays;
import java.util.List;
class Snow{}
class Powder extends Snow{}
class Light extends Powder{}
class Heavy extends Powder{}
class Crusty extends Snow{}
class Slush extends Snow{}
public class AsListInference {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<Snow> snow1 = Arrays.asList(new Powder(),new Crusty(),new Slush());
List<Snow> snow2 = Arrays.asList(new Light(),new Heavy());
System.out.println("---snow1---");
for(Snow a:snow1) {
System.out.println(a);
}
System.out.println("---snow2---");
for (Snow c : snow2) {
System.out.println(c);
}
System.out.println("over");
}
}
输出是:
---snow1---
C_11.Powder@1909752
C_11.Crusty@1f96302
C_11.Slush@14eac69
---snow2---
C_11.Light@a57993
C_11.Heavy@1b84c92
over
C_11.Powder@1909752
C_11.Crusty@1f96302
C_11.Slush@14eac69
---snow2---
C_11.Light@a57993
C_11.Heavy@1b84c92
over
没毛病,没产生书上说的编译错误。
【容器的打印】
1、打印数组,需要用Arrays.toString(),但是容器不需要。直接把对象放进去,就打印出来了。
2、Collection每个槽中只能保存一个元素。
1)List:按顺序保存一组元素
2)Set:元素不能重复,好像重复添加也不报错。
3)Queue:一端进,另一端出
3、Map每个槽保存两个对象,即K-V
4、以下示例介绍了几个新的Collection
1)
ArrayList
是实现了
基于动态数组
的数据结构
2)
LinkedList基于链表
的数据结构。 LinkedList包含的操作多于ArrayList。
对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
他们都是按照插入顺序保存对象。
3)
HashSet
实现了Set接口,它
不允许集合中有重复的值
,使用HashSet时,需要重写equals()和hashCode()方法,这样才能比较对象的值是否相等,以确保set中没有储存相等的对象。如果没有重写这两个方法,将会使用这个方法的默认实现。
HashSet实现相对复杂,关注存续性能,不关注存储顺序
4)
TreeSet
是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。如果关注存取顺序用这个。
5)
LinkedHashSet
集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起 来像是以插入顺序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。
LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet。
LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet。
6)
HashMap
实现了Map接口,Map中不允许重复的键。查找最快。
5)
TreeMap
保存了对象的排列次序,而HashMap则不能
6)
LinkedHshMap
,是HashMap的一个子类,它保留插入的顺序,如果需要输出的顺序和输入时的相同,那么就选用LinkedHashMap
Map访问数据结构是get(Key),put(Key,Value)。
Map可自动扩展尺寸。而且插入顺序和保存顺序不一样。
import java.util.*;
public class PrintingContainers {
static Collection fill(Collection<String> strCollection) {
strCollection.add("Hippo2");
strCollection.add("Pig2");
strCollection.add("Dog2");
strCollection.add("Dog2");
return strCollection;
}
static Map fill(Map<String,String> map) {
map.put("rat", "Kitty");
map.put("cat", "Kitty");
map.put("girl", "Lisha");
map.put("girl", "Mili");
return map;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
/*这样写是错误的,需要在创建实例时,集体化Collection
Collection<String> collection1 = new Collection<String>();*/
Collection<String> collection1 = new ArrayList<String>();
collection1.add("Hippo1");
collection1.add("seesaw1");
System.out.println("---collection1---");
/*print也用不了
print(collection1);换成System.out.print可以了,书上的不好用。*/
System.out.println(collection1);
System.out.println("call Collection fill()--ArrayList");
System.out.println(fill(new ArrayList<String>()));
System.out.println("call Collection fill()--LinkedList");
System.out.println(fill(new LinkedList<String>()));
System.out.println("call Collection fill()--Hashset");
System.out.println(fill(new HashSet<String>()));
System.out.println("call Collection fill()--TreeSet");
System.out.println(fill(new TreeSet<String>()));
System.out.println("call Collection fill()--LinkedHashSet");
System.out.println(fill(new LinkedHashSet<String>()));
System.out.println("call Collection fill()--HashMap");
System.out.println(fill(new HashMap<String,String>()));
System.out.println("call Collection fill()--TreeMap");
System.out.println(fill(new TreeMap<String,String>()));
System.out.println("call Collection fill()--LinkedHashMap");
System.out.println(fill(new LinkedHashMap<String,String>()));
}
}
输出:
---collection1---
[Hippo1, seesaw1]
call Collection fill()--ArrayList
[Hippo2, Pig2, Dog2, Dog2]
call Collection fill()--LinkedList
[Hippo2, Pig2, Dog2, Dog2]
call Collection fill()--Hashset
[Pig2, Dog2, Hippo2]
call Collection fill()--TreeSet
[Dog2, Hippo2, Pig2]
call Collection fill()--LinkedHashSet
[Hippo2, Pig2, Dog2]
call Collection fill()--HashMap
{rat=Kitty, cat=Kitty, girl=Mili}
call Collection fill()--TreeMap
{cat=Kitty, girl=Mili, rat=Kitty}
call Collection fill()--LinkedHashMap
{rat=Kitty, cat=Kitty, girl=Mili}
上面的程序只是简单的一个演示。后面部分对每个容器开始详细介绍
【List】
1、两种类型的List
1)
ArrayList
,这是最基本的。可以理解成动态数组。
随机访问快,插入删除慢
。
2)
LinkedList
,一看这个Linked,就可以想到链表。
插入修改方便,随机访问慢
。而且
特性集比较大
。
2、这部分代码就不写了。主要的思想还比较好理解
1)List是可以
resize
的。在调用add()时,会扩大,新的元素会加入。remove()删除元素,删除不存在的元素返回false,否则是返回ture。可以是用元素,也可以是用下标,如:remove(p),remove(2)。
2)contains()函数,用来判断某个元素是否在数组中。返回true或false
3)get(下标)获取元素,indexof(元素)取得下标,不在返回-1。
4)用equals方法来判断是否相同。每个元素都被定义为唯一的对象。所以即使列表中有两个Cymric,再创建一个Cymric,并传递给indexOf(),还是找不到。同样remove()也一样。所以equals()很关键。
5)add(下标,元素),在指定位置插入对象。
6)subList(x,y)截取一段。[x,y)。对
subList的修改,都会反映到初始列表,反之亦然。
7)Collections.sort()对List排序。containsAll()判断一个对象序列是否在一个大的序列里面,而且与顺序无关。
8)retainAll()是交集,注意判断还是一句equals()。
9)removeAll,删除参数中多个元素。removeAll(sub)。
10)addAll是加入多个元素。addAll(sub)。
11)isEmpty(),判断是否为空
12)clear(),清空所有元素
13)toArray转化为一个数组。
【迭代器】
1、从这个例子看它要解决的问题
原来对着List编码的,后来发现能把相同代码用于Set,会更好。为避免重新写通用代码,使用迭代器。
2、
迭代器
:是一个对象,它的工作是遍历并选择序列中的对象,而程序员不必知道或关系该序列底层的结构 。
迭代器也被称为轻量级对象,
创建它的代价小
。
3、Java的Iterator只能单向移动,只能用来:
1)用next()方法获取下一个元素
2)hasNext()检查序列中是否还有元素。
3)用remove()将迭代器新返回的元素删除
import java.util.*;
public class SimpleIteration {
public static void main(String[] args) {
// TODO Auto-generated method stub
//没有导入书上的例子,Pets类,现在不能运行,主要看语法。
List<Pet> pets = Pets.arrayList(12);
//地名义Iterator
Iterator<Pet> it= pets.iterator();
//循环判断是否有元素
while(it.hasNext())
{
pet p = it.next();
System.out.println(p.id()+":"+p+" ");
}
//因为单向移动,所以又得放到头上
it=pets.iterator();
for(int i=0;i<6;i++) {
//移动到下一个
it.next();
//删除
it.remove();
}
}
}
4、只要是容器的Iterator即可,
如:
ArrayList<Pet> pets = XX;
LinkedList<Pet> pestLL = XX;
HashSet<Pet> petsHS = X;
TresSet<Pet> petsTS = XX;
注意,这里有List,也有Set
调用函数时,可以
display(pets.iterator());
display(pestLL.iterator());
display(petsHS.iterator());
display(petsTS.iterator());
这里分别传递了不同容器类型的iterator();
而在具体的处理函数里面,无所谓什么容器类型。
public static void display(Iterator<Pet> it){
在这里面就可以用Iterator的函数,如hasNext(),next()进行操作。
}
从而实现了display()方法与具体的序列类型分离
。
5、再看一个叫做
ListIterator
1)不像Iterator只能单向移动,ListIterator可以双向移动。
2)有hasPrevious()和hasNext()两个方法判断前后
3)有set()可以替换它访问过的最后一个元素
4)listIterator(),创建指向List开始处的ListIterator,也可以带参数n从特定位置开始,listIterator(n)。
5)最重要,它只能用于List,Set不可以了。
【LinkedList】
前面提了很多与ArrayList的对比了,这里介绍一下它其他一些方法
1、getFirst()与element()一样,返回第一个元素。如果List为空,返回NoSuchElementException。peek()方法功能类似,只是在List为空时,返回null。
2、removeFirst()与remove()完全一样。移除并返回列表的头。在列表为空时返回NoSuchElementException。同样,也有个poo(),此时返回null。
3、addFisrt()/add()/addLast()都是将元素插入到列表的尾(端)部
4、removeLast()移除最后一个元素
5、在LinkedList基础上添加一系列方法,可以成为Queue的实现。说明这两个关系很紧密。
【Stack】
1、后进先出
2、LinkedList能直接实现栈的所有功能,可以直接作为栈使用。
这个实现还是很巧妙的,值得围观。
package C_11;
import java.util.LinkedList;
//基于LinkedList实现一个Stack的类
public class Stack<T> {
private LinkedList<T> storage = new LinkedList<T>();
public void push(T v) {
System.out.println("C_11 push()"+v);
storage.addFirst(v); //这里添加到第一个,意味着先进去的,被推到后面
}
public T peek() {return storage.getFirst();}
public T pop() {
System.out.println("C_11 pop"+ storage.peek()); //取排在第一个(用push()最后加入的)
return storage.removeFirst(); //移除第一个,则后进先出
}
public boolean empty() {return storage.isEmpty();}
public String toString() {return storage.toString();}
}
public class
Stack<T>:这个<T>告诉编译器,这将是一个参数化类型
。在类被使用时,会将实际的类型替换该参数。
在这个申明中:定义一个可以持有T类型的对象的Stack。Stack内部用LinkedList实现,LinkedList也被告知是T类型。
3、
实际在java.util中,已经包含了一个Stack的类。书上说认为java.util.Stack实现不好。所以,如果想使用自己创建的这个,使用时要指定完整包名。在我的例子里面,就是C_11。下面代码分别用了自己定义的Stack,还有java.util.Stack。
package C_11;
import java.io.PushbackInputStream;
public class StackCollision {
public static void main(String[] args) {
// TODO Auto-generated method stub
//指定包名C_11,用自己定义的Stack
C_11.Stack<String> stack01 = new C_11.Stack<String>();
for(String s:"Test the new Stack class.".split(" ")) {
stack01.push(s);
System.out.println(stack01);
}
while(!stack01.empty()) {
stack01.pop();
}
System.out.println("---------Test the java.util.Stack.-------push()------");
//用java.util.Stack
java.util.Stack<String> stack02=new java.util.Stack<String>();
for(String s1:"Test the java.util.Stack.".split(" ")) {
stack02.push(s1);
System.out.println(s1);
}
System.out.println("---------Test the java.util.Stack.-------pop()------");
while(!stack02.empty()) {
System.out.println(stack02.pop());
}
}
}
执行效果如下:
C_11 push()Test
[Test]
C_11 push()the
[the, Test]
C_11 push()new
[new, the, Test]
C_11 push()Stack
[Stack, new, the, Test]
C_11 push()class.
[class., Stack, new, the, Test]
C_11 popclass.
C_11 popStack
C_11 popnew
C_11 popthe
C_11 popTest
---------Test the java.util.Stack.-------push()------
Test
the
java.util.Stack.
---------Test the java.util.Stack.-------pop()------
java.util.Stack.
the
Test
【Set】
1、Set里面无重复。最常用的场景是,判断某个对象是否在Set中。
查找是Set最重要的操作。通常会选择HashSet,查询快。
package C_11;
import java.util.*;
public class SetOfInteger {
public static void main(String[] args) {
// TODO Auto-generated method stub
//构建一个随机数发生器,20是种子
Random rand = new Random(47);
Integer int01;
/*
for(int j = 0; j<100;j++)
{
System.out.println(rand.nextInt(30));
}*/
Set<Integer> set01=new HashSet<Integer>();
for(int i = 0; i <100; i++) {
int01 = rand.nextInt(20);
//从打印来看,可以确认,数字还是随机的,
System.out.print(int01+",");
//用随机数发生器,取[0,30)的数字,并放到Set中。虽然循环了1000次,但实际插入的并不多,因为有重复的
set01.add(int01);
}
System.out.println("");
System.out.println("Print Set ----");
//单最后打印set时,是按照顺序来排的
System.out.println(set01);
}
}
输出
18,15,13,1,1,9,8,0,2,7,8,8,11,9,9,18,18,1,0,18,16,0,11,2,4,3,6,15,10,2,14,4,10,6,2,16,13,4,4,10,3,1,18,12,6,9,5,12,6,16,7,8,14,15,5,14,16,2,16,7,17,14,2,17,4,2,0,18,1,19,12,5,14,9,2,9,18,18,3,13,6,14,10,4,17,13,1,8,8,8,7,19,15,2,10,17,19,11,13,17,
Print Set ----
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
2、但在打印HashSet时,
是排序(升序)了的。和书上说的为了性能,不排序的说法不一样。查了一下,
这只是巧合,不能依赖这里的有序。知乎上有分析:
https://www.zhihu.com/question/28414001/answer/40734236
大概就是:
插入HashSet的是Integer,其hashCode()实现就返回int值本身。所以在对象hashCode这一步引入了巧合的“按大小排序”。
这关系到Set的实现细节和Hash算法的问题。
3、
如果确实想获取可信的排序形式Set,请使用TreeSet
。大概差不多。这里就不看代码了
4、用
contains()
来判断Set中是否包含某个元素。
5、TreeSet()缺省排序是把大、小写字母区分开的。比如,会排成
Aa B a b这样的。
如果不想用大小写区分开,只是看字母顺序,则需要向TreeSet的构造器传入String.CASE_INSENTIVE_ORDER比较器。
6、对比代码如下
package C_11;
import java.util.Set;
import java.util.TreeSet;
public class UniqueWordAlphabetic {
public static void main(String[] args) {
// TODO Auto-generated method stub
Set<String> strSet = new TreeSet<String>();
// Set<String> strSet02 = new TreeSet<String>();
for(String c:"Input some words and some Aa df ec L0 b de Ka KC".split(" "))
strSet.add(c);
//这个的输出是按字母顺序,但是大小写字母区分的
System.out.println(strSet);
System.out.println("-------------using String.CASE_INSENSITIVE_ORDER----------");
Set<String> strSet02 = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
for(String d:"Input some words and some Aa df ec L0 b de Ka KC".split(" "))
strSet02.add(d);
System.out.println(strSet02);
}
}
可以看出,输出上的差异:
[Aa, Input, KC, Ka, L0, and, b, de, df, ec, some, words]
-------------using String.CASE_INSENSITIVE_ORDER----------
[Aa, and, b, de, df, ec, Input, Ka, KC, L0, some, words]
【Map】
1、可以看做是一个KV的数据库一样的效果。
2、get(Key)获得对应的Value;put(K,V)写Value
3、Map以及其他的Collection,很容易扩展到多维,从而组织强大的数据结构。如:
HashMap<Integer,Integer>:K和V都是整数
HashMap<String,Pet>:K是字符串,V是对象
Hashmap<Person, List<Pet>>:Key是对象,V是对象列表
4、书上有一句这样写:
public static Map<Person, List<? extends Pet>>
?是java泛型中的通配符,它代表java中的某一个类,那么<? extends T>就代表类型T的某个子类,<? super T>就代表类型T的某个父类
5、下面是一个Key为对象,V为对象列表的例子,表示一个人下面有多个宠物
package C_11;
import java.util.*;
//构建自己的人员的类
class myOwnPerson{
private String name;
public myOwnPerson(String strName) {
name = new String(strName);
}
//如果不定义类的toString,则会按照格式显示,如:C_11.myOwnPerson@1909752
public String toString() {
return name;
}
}
//构建宠物的类
class myOwnPet{
private String petKind;
private String petName;
public myOwnPet(String valPetKind, String valPetName)
{
petKind = new String(valPetKind);
petName = new String(valPetName);
}
public String GetPetKind()
{
return this.petKind;
}
public String GetPetName() {
return this.petName;
}
//@Override
public String toString() {
// TODO Auto-generated method stub
return "PetKind:"+petKind+","+"PetName:"+petName;
//return super.toString();
}
}
public class MapOfList {
//顶一个一个HasMap,用来保存相关人和宠物列表的关系
public static Map<myOwnPerson,List<myOwnPet>> MapOfListExample = new HashMap<myOwnPerson,List<myOwnPet>>();
//添加关联关系
static {
MapOfListExample.put(new myOwnPerson("Person1"),Arrays.asList(new myOwnPet("Cat","pet1"),new myOwnPet("Cat", "pet2")));
MapOfListExample.put(new myOwnPerson("Person2"),Arrays.asList(new myOwnPet("Cat","pet1"),new myOwnPet("Dog","Pet2")));
//哈可以添加很多
}
public static void main(String[] args) {
// TODO Auto-generated method stub
//调用keySet()显示所有的Key
System.out.println("MapOfListExample.KeySet = "+ MapOfListExample.keySet());
//调用values(),显示所有的value
System.out.println("MapOfListExample.valueSet = "+ MapOfListExample.values() );
//循环,在每个key下面,把相应的value列出来。
for(myOwnPerson p:MapOfListExample.keySet()) {
System.out.println(p+ " has:");
for(myOwnPet pet:MapOfListExample.get(p)) {
System.out.println(pet+",");
}
}
}
}
MapOfListExample.KeySet = [Person1, Person2]
MapOfListExample.valueSet = [[PetKind:Cat,PetName:pet1, PetKind:Cat,PetName:pet2], [PetKind:Cat,PetName:pet1, PetKind:Dog,PetName:Pet2]]
Person1 has:
PetKind:Cat,PetName:pet1,
PetKind:Cat,PetName:pet2,
Person2 has:
PetKind:Cat,PetName:pet1,
PetKind:Dog,PetName:Pet2,
通常不用对象做Key,不好通过containsKey()来判断,因为判断的是一个对象是否相同,除非重新定义比较方法()。
【Queue】
1、队列特点是FIFO,通常用作可靠的将对象从程序某个区域传输到另一个区域。
2、FIFO可以用LinkedList来实现。看来LinkedList很万能啊。
3、说是LinkedList实现了Queue的接口,所以创建Queue,实际还是用LinkedList来定义。
Queue<Integer> Queue01 = new LinkedList<Integer>();
4、向Queue添加数据,如果插入不成功,返回false。
Queue01.offer(rand.nextInt());
5、peek()和element()都在不移除的情况下返回队头。只是队列为空时,peek()返回null,而element()抛出NoSuchElementException异常。
6、pool()和remove()移除并返回队头。队列为空时,pool()返回null,而remove()抛出NoSuchElementException异常。
这里有个小技巧
for(char c:"dfienadf".toCharArray())
类似于前面的在字符串上用".split()",这里把前面的一串字符转化程单个的字母数组。
【PriorityQueue】
1、FIFO是先进先出,这里谈一下优先级队列。
2、当在PriorityQueue上调用offer()方法插入一个对象时,这个对象在队列中会被排序。可以通过修改Comparator来修改排队规则。
3、以下举例PriorityQueue为Integer、String、Character,并可以通过offer()函数插入,也可以用List、Set等容器来进行填充。
package C_11;
import java.util.*;
public class PriorityQueueDemo {
private static void PrintQueueDemo(Queue queue) {
while(queue.peek()!=null)
{
System.out.print(queue.remove()+",");
}
System.out.println();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
PriorityQueue<Integer> priorityQueue01 = new PriorityQueue<Integer>();
int num = 0;
Random rand01 = new Random(67);
System.out.println("Insert to priorityQueue01:");
for(int i = 0; i < 10; i++) {
//打印一下插入数据
num = rand01.nextInt(i+10);
System.out.print(num+",");
priorityQueue01.offer(num);
}
System.out.println();
//打印是按顺序的,从小到大
System.out.println("Print value from priorityQueue01:");
PrintQueueDemo(priorityQueue01);
//---------------------------------------------------//
//用Integer List给priorityQueue赋值
List<Integer> ints = Arrays.asList(25,44,12,56,323,22,68,43,14,6,1,77);
System.out.println("List :"+"25,44,12,56,323,22,68,43,14,6,1,77");
priorityQueue01 = new PriorityQueue<Integer>(ints);
System.out.println("Print value from priorityQueue01:");
PrintQueueDemo(priorityQueue01);
//用字符串给priorityQueue赋值,字符串排序
System.out.println("List: ADCE< KDFEL HF EDA DLFKD , dfdf");
PriorityQueue<String> priorityQueue02 = new PriorityQueue<String>();
for(String s: "ADCE< KDFEL HF EDA DLFKD , dfdf".split(" "))
priorityQueue02.offer(s);
PrintQueueDemo(priorityQueue02);
//可以用List来初始化Queue
String abc = "dfdk zfdk afd dfkdf d";
List<String> abcStr = Arrays.asList(abc.split(" "));
PriorityQueue priorityQueue03 = new PriorityQueue<String>(abcStr);
PrintQueueDemo(priorityQueue03);
//也可以用Set来初始化
Set<Character> charSet = new HashSet<Character>();
for(char c:abc.toCharArray())
charSet.add(c);
PriorityQueue<Character> priorityQueue04 = new PriorityQueue<Character>(charSet);
PrintQueueDemo(priorityQueue04);
}
}
输出:
Insert to priorityQueue01:
9,9,2,9,10,8,13,0,10,4,
Print value from priorityQueue01:
0,2,4,8,9,9,9,10,10,13,
List :25,44,12,56,323,22,68,43,14,6,1,77
Print value from priorityQueue01:
1,6,12,14,22,25,43,44,56,68,77,323,
List: ADCE< KDFEL HF EDA DLFKD , dfdf
,,,ADCE<,DLFKD,EDA,HF,KDFEL,dfdf,
afd,d,dfdk,dfkdf,zfdk,
,a,d,f,k,z,
4、Integer、String和Character这些类已经内建了自然排序。如果其他类,需要提供自己的COmparator才行。
【Collection和Iterator】
1、Collection是描述所有序列容器的共性根接口。
2、使用接口描述的一个理由是能创建
更通用的代码。通过针对接口而非具体实现来编写代码,代码可以用于更多的对象类型。
3、
一个方法可以接受一个Collection,那么该方法就可以应用于任何实现Collection的类。
4、实现Collection还需要提供Iterator()方法。
如:
public static void display(Collection<myOwnPet> pets)
{
for(myOwnPet p:pets)
{
System.out.println(p);
}
}
这里用Collection做入参。则可以支撑输入其他各种具体容器序列,如:
package C_11;
import java.util.*;
import C_11.myOwnPet;//从MapOfList.java中导入
public class InterfaceVsIterator {
public static void display(Collection<myOwnPet> pets)
{
for(myOwnPet p:pets)
{
System.out.println(p);
}
System.out.println();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
List<myOwnPet> petList = Arrays.asList(new myOwnPet("k0", "bb"),new myOwnPet("k1", "cc"),new myOwnPet("k2", "cc"));
Set<myOwnPet> petSet = new HashSet<myOwnPet>();
petSet.add(new myOwnPet("k3","ad"));
petSet.add(new myOwnPet("k4","ade"));
//List和Set都可以作为display的输入。
display(petList);
display(petSet);
}
}
5、C++中是用迭代器来实现的。Java里面也可以有一种用法。作为参数,调用底层的Iterator。如:display还可以输入一个Iterator。只要容器实现了Iterator这里就可以调用,达到逐个访问的效果。
如:
public static void display(Iterator<myOwnPet>,it)
{
while(it.hasNext())
{
myOwnPet p = it.next();
System.out.println(p);
}
}
调用时可以是:
display(petList.iterator());
display(petSet.iterator());
6、上面是两种方法,认为用Collection更好一些。
7、但是,当实现的不是一个Collection类时,想让它用Collection接口,就会比较麻烦。用Iterator会相对好一些。
如果继承一个持有Pet对象的类来创建一个Collection类的实现,则必须实现所有的Collection方法。可以通过继承AbstractCollection实现,但还是需要实现Iterator()和size()。因为AbstractCollection内其他方法会调用。
public class CollectionSequence extends
AbstractCollection{
private Pet[] ptes = Ptes.createArray(8);
public
Iterator<Pet> iterator(){
public boolean
hasNext()
{
......
}
public Pet
next(){return pets[index++];}
}
public int
size(){return pets.length;}
}
这里简单示意一下如何用一个持有Pet对象的类,构建一个Collection。通过扩展AbstractCollection实现。重点是重载实现了Iterator()和size()。
如果不能继承AbstractCollection(比如已经继承了其他类),则需要实现Collection的所有方法。
【foreach与迭代器】
1、foreach语法可以用于任何Collection对象。
2、核心是实现了Iterable的类。它内部有Iterator()。
基本上这章就结束了。容器应用很广,后续再找机会实践的时候再进一步琢磨吧。
------2018.1.10 23:20 深圳
内容为:学习笔记,加一写补充。总体来源,都还是书上的《Java编程思想》