Java中的容器,程序员必须要了解的知识点

public static void main(String[] args) {

ArrayList apples=new ArrayList();

for(int i=0;i<3;i++){

apples.add(new Apple());

}

for(Apple apple:apples){

System.out.println(apple.name());

}

}

}

Output:

apple0

apple1

apple2

当你指定了某个类型作为泛型参数时,你并不仅限于只能将确切类型的对象放置到容器中。向上转型也可以像作用于其他类型一样作用于泛型。因此,可以将Apple的子类型添加到被指定为保存Apple对象的容器中。

1.1 基本概念

Java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念:

  • Collection: 一个独立元素的序列,这些元素都服从一条或多条规则。List必须按照插入的顺序保存元素,而Set不能有重复元素,Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)

  • Map:一组成对的"键值对(key-value)"对象,允许你使用键来查找值。ArrayList允许你使用数字来查找值,因此在某种意义上将,它将数字与对象关联在了一起。映射表允许我们使用另一个对象来查找对象,它也被称为"关联数组",因为它将某些对象与另外一些对象关联在了一起;或者被称为"字典",因为你可以使用键对象来查找值对象,就像在字典中使用单词来定义一样。Map是强大的编程工具。

理想情况下,编写的代码都是在与这些接口打交道,并且你唯一需要指定所使用精确类型的地方就是在创建的时候。下面创建一个List: List<Apple> apples =new ArrayList<Apple>(); 这里,ArrayList被向上转型为List。使用接口的目的在于如果你决定去修改你的实现,所需做的只是在创建处修改它,就像下面这样: List<Apple> apples =new LinkedList<Apple>();

所以一般实际中,我们应该创建一个具体类的对象,将其转型为对应的接口,然后在其余的代码中都使用这个接口。

1.2 添加一组元素:

在java.util的Arrays和Collection类中都有很多实用方法,可以在一个Collection中添加一组元素。

  • Arrays.asList()方法: 接受一个数组或是一个用逗号分隔的元素列表,并将其转换为一个List对象。

  • Collection.addAll()方法: 接受一个Collection对象,以及一个数组或是一个用逗号分割的列表,并将元素添加到Collection中。

public class AsListInference {

public static void main(String[] args) {

Collection collection=new ArrayList(Arrays.asList(1,2,3));

Integer []arr={4,5,6};

collection.addAll(Arrays.asList(arr));

Collections.addAll(collection,7,8,9);

Collections.addAll(collection,arr);

List list= Arrays.asList(1,2,3);

list.set(1,99);

//容器可以在无需任何帮助下打印

System.out.println(collection);

System.out.println(list);

}

}

Output:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 4, 5, 6]

[1, 99, 3]

Collection.addAll() 方法运行起来很快,而且构建一个不包含元素的Collection,同时调用Collections.addAll() 这种方式很方便,因此它是首选方式。但是 Collection.addAll() 成员方法只能接受另一个Collection对象作为参数,因此它不如 Arrays.asList()Collections.addAll() 灵活,这两个方法使用的都是可变参数列表。

注意:若使用Arrays.asList()给一个List赋值,需要使用显式类型参数说明,否则会自动寻找它们的共同最浅的基类,这样如果向上转型至根类,则会报错;但是若使用,Collections.addAll()则不存在这个问题。

四种容器


2.1 List

List承诺可以将元素维护在特定的序列中。List接口在Collection的基础上添加了大量的方法,使得可以在List的中间插入移除元素。

有两种类型的List:

1. ArrayList: 它可以随机访问元素,但是在List的中间插入和移除元素时较慢;

/**

  • @Author: Ly

  • @Date: 2020-08-13 08:46

*/

public class ListFeatures {

public static void main(String[] args) {

List li = new ArrayList();

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

li.add(i);

Integer h = 10;

li.add(h); //将指定的元素追加到此列表的末尾

li.add(1,0); //将指定的元素插入此列表中的指定位置

li.remove(0); //删除该列表中指定位置的元素

System.out.println(li);

System.out.println(li+"集合中含有:h "+li.contains(h));

li.set(0,10); //用指定的元素替换此列表中指定位置的元素

Integer p = li.get(0);//返回此列表中指定位置的元素

System.out.println(“数字”+p + “第一次出现位置的索引为:” + li.indexOf§); //打印此列表中指定元素的第一次出现的索引

li.remove§; //删除指定元素的第一次出现

System.out.println(li);

List sub = li.subList(1, 4);//用列表中第1到第4个(不包含第4个)元素创建新的集合

Boolean bl=li.containsAll(sub); //看列表是否包含指定集合的所有元素

System.out.println(“集合li中”+ (bl?“含有”:“不含有”) +“sub中的所有元素”);

List copy = new ArrayList(li);

copy.removeAll(sub); //从此列表中删除包含在指定集合中的所有元素

System.out.println(“copy集合:”+copy);

if(copy.size() > 1) //如果列表中的元素数大于1

copy.addAll(2, sub);//将指定集合中的所有元素插入到此列表中的指定位置

System.out.println(“copy集合:”+copy);

Object[] objects = li.toArray();//以正确的顺序返回一个包含此列表中所有元素的数组

for(Object o:objects){

System.out.print(o+", ");

}

System.out.println(li.isEmpty());//如果此列表不包含元素,则返回 true

li.clear();//从此列表中删除所有元素

System.out.println(li+":"+ li.isEmpty());

}

}

Output:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]集合中含有: h true

数字10第一次出现位置的索引为:0

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

集合li中含有sub中的所有元素

copy集合:[1, 5, 6, 7, 8, 9, 10]

copy集合:[1, 5, 2, 3, 4, 6, 7, 8, 9, 10]

1, 2, 3, 4, 5, 6, 7, 8, 9, 10, false

[]:true

2. LinkedList: 它通过代价较低的在List中间插入和删除操作,提供了优化的顺序访问。LinkedList在随机访问方面相对比较慢,但是它的特性集较ArrayList更大。

例:

/**

  • @Author: Ly

  • @Date: 2020-08-13 12:24

*/

public class LinkedListFeatures {

public static void main(String[] args) {

LinkedList list = new LinkedList();

for (int i = 1; i < 10; i++) {

list.add(i);

}

System.out.print(list.getFirst()+" "); //返回此列表中的第一个元素,为空时抛出异常

System.out.print(list.element()+" "); //检索但不删除此列表的头,为空时抛出异常

System.out.print(list.peek()+" "); //检索但不删除此列表的头,为空时返回null

System.out.print(list.removeFirst()+" "); //检索并删除此列表的头,为空时抛出异常

System.out.print(list.remove()+" "); //检索并删除此列表的头,为空时抛出异常

System.out.print(list.poll()+" "); //检索并删除此列表的头,为空时返回null

System.out.println();

System.out.println(list);

list.addFirst(0); //在该列表开头插入指定的元素

list.offer(10); //将指定的元素添加为此列表的末尾

list.add(11); //将指定的元素追加到此列表的末尾

list.addLast(12); //将指定的元素追加到此列表的末尾

list.removeLast(); //从此列表中删除并返回最后一个元素。

System.out.println(list);

}

}

Output:

1 1 1 1 2 3

[4, 5, 6, 7, 8, 9]

[0, 4, 5, 6, 7, 8, 9, 10, 11]

LinkedList也像ArrayList一样实现了基本的List接口,但是它执行某些操作时比ArrayList更高效,但是随机访问操作方面要差一些。LinkedList中添加了可以使其用作栈、队列或双端队列的方法。

3. Stack:

“栈”通常是指“后进先出(LIFO)”的容器,有时也称为叠加栈,LinkedList具有能够直接实现栈的所有功能的方法,因此可以直接将LinkedList作为栈使用。

/*

  • 通过使用泛型,引入了在栈的类定义中最简单的可行示例。

  • 类名之后的告诉编译器这将是一个参数化类型(在类被使用时将会被实际类型替换的参数)。

  • Stack使用LinkedList实现的,而LinkedList也被告知它将持有T类型对象;

*/

class Stack {

private LinkedList storage=new LinkedList();

public void push(T v){storage.addFirst(v);}

public T peek() {return storage.getFirst();}

public T pop () {return storage.removeFirst();}

public boolean empty () {return storage.isEmpty(); }

public String toString (){return storage.toString();}

}

public class StackTest{

public static void main(String[] args) {

Stack stack = new Stack ();

for (String s : “One Two Three”.split(" "))

stack.push(s);

while (!stack.empty())

System.out.print(stack.pop()+" ");

}

}

Output:

Three Two One

2.2 Set

Set不保存重复的元素;Set中最常用的是测试归属性,可以很容易地询问某个对象是否在某个Set中。正因如此,查找成了Set中最重要的操作,因此通常会选择一个HashSet的实现,它专门对快速查找进行了优化。(Hash查找最快。)

/**

  • @Author: Ly

  • @Date: 2020-08-14 19:27

*/

public class SetOfInteger {

public static void main(String[] args) {

Random rand=new Random(47);

Set intSet =new HashSet();

//如果想对结果排序,使用TreeSet来替代HashSet,不过在测试HashSet时你会发现打印出来的也是排序好的结果

//SortedSet intSet =new TreeSet();

for (int i = 0; i<10000 ; i++){

intSet.add(rand.nextInt(30));

};

System.out.println(intSet);

//使用contains()测试Set的归属性

System.out.println(intSet.contains(5));

System.out.println(intSet.contains(30));

}

}

Output:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,

16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]

true

false

TreeSet将元素存储在红-黑数据结构中,而HashSet使用的是散列函数。LinkedHashSet因为查询速度的原因也使用了散列,但是看起来它使用了链表来维护元素的插入顺序。

2.3 Map

将对象映射到其他对象的能力是一种解决编程问题的杀手锏;考虑一个程序,它将用来检查Java的Random类的随机性。理想情况下,Random可以产生理想的数字分布,但要想测试它,则需要生成大量的随机数,并对落入各种不同范围的数字进行计数。Map可以很容易地解决该问题。在这个问题中,键是由Random产生的数字,值是该数字出现的次数:

/**

  • @Author: Ly

  • @Date: 2020-08-14 20:38

*/

public class Statistics {

public static void main(String[] args) {

Random rand=new Random(47);

Map<Integer,Integer> m=

new HashMap<Integer,Integer>();

for(int i= 0; i <10000 ;i++){

int r=rand.nextInt(20);

Integer freq = m.get®;

m.put(r,freq==null ? 1 : freq+1);

}

System.out.println(m);

//containsKey()和containsValue()来测试Map,以便查看它是否包含某个键或某个值:

System.out.println(m.containsKey(

《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享

10));

System.out.println(m.containsValue(500));

}

}

Output:

{0=481, 1=502, 2=489, 3=508, 4=481, 5=503, 6=519, 7=471, 8=468, 9=549,

10=513, 11=531, 12=521, 13=506, 14=477, 15=497, 16=533, 17=509, 18=478, 19=464}

true

false

Map与数组和其他的Collection一样,可以很容易地扩展到多维,而我们只需将其值设置为Map(这些Map的值可以是其他容器,甚至是其他Map)。因此,能够很容易地将容器组合起来从而快速地生成强大的数据结构。

class Person{

private String name;

Person(String name){

this.name=name;

}

@Override

public String toString() {

return name;

}

}

public class MapOfList {

public static Map<Person , List>

petPeople = new HashMap<Person,List>();

static{

petPeople.put(new Person(“Dawn”),

Arrays.asList (“Cymric_Molly”,“Mutt_Spot”));

petPeople.put(new Person(“Kate”),

Arrays.asList (“Cat_Shackleton”, “Cat_Elsie”, “Dog_Margret”));

petPeople.put(new Person(“Marilyn”),

Arrays.asList(“Pug_Louie”, “Cat_Negro”, “Cat_Pinkola”));

petPeople.put (new Person(“Luke”),

Arrays.asList(“Rat_Fuzzy”, “Rat_Fizzy”));

petPeople.put(new Person (“Isaac”),

Arrays.asList(“Rat_Freckly”));

}

public static void main(String[] args) {

System.out.println("People: "+ petPeople.keySet());

System.out.println("Pets: "+ petPeople.values());

for(Person person : petPeople.keySet()){

System.out.print(person + " has : ");

for (String str : petPeople.get(person))

System.out.print(" "+str);

System.out.println();

}

}

}

Output:

Luke has : Rat_Fuzzy Rat_Fizzy

Marilyn has : Pug_Louie Cat_Negro Cat_Pinkola

Isaac has : Rat_Freckly

Dawn has : Cymric_Molly Mutt_Spot

Kate has : Cat_Shackleton Cat_Elsie Dog_Margret

Map是一种将对象(而非数字)与对象相关联的设计。HashMap设计用来快速访问;而TreeMap保持“键”始终处于排序状态,所以没有HashMap快。LinkedHashMap保持元素插入的顺序,但是也通过散列提供了快速访问能力。

2.4 Queue

队列是一个典型的先进先出(FIFO)的容器。即从容器的一端放入事物,从另一端取出,并且将事物放入容器的顺序与取出的顺序是相同的。队列常被当作一种可靠的将对象从程序的某个区域传输到另一个区域的途径。队列在并发编程中特别重要。

LinkedList提供了方法以支持队列的行为,并且它实现了Queue接口,因此LinkedList可以用作Queue的一种实现。通过将LinkedList向上转型为Queue,下面的示例使用了在Queue接口中与Queue相关的方法:

/**

  • @Author: Ly

  • @Date: 2020-08-14 21:00

*/

public class QueueDemo {

public static void printQ(Queue queue) {

//peek(),element(): 在不移除的情况下返回队头

//peek():在队列为空时返回null

//element():在队列为空时抛出NoSuchElementException异常

while(queue.peek()!= null)

System.out.print(queue.remove()+" ");

System.out.println();

}

public static void main(String[] args) {

Queue queue = new LinkedList();

Random rand = new Random(47);

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

//offer(): 在允许的情况下,将一个元素插入到队尾,或者返回false

queue.offer(rand.nextInt(i+10));

printQ(queue);

Queue qc= new LinkedList();

for(char c :“Brontosaurus”.toCharArray())

qc.offer©;

System.out.println(qc);

}

}

Output:

3 10 7 0 10 3 6 7 4 5

[B, r, o, n, t, o, s, a, u, r, u, s]

Queue接口窄化了对LinkedList的方法的访问权限,以使得只有恰当的方法才可以使用,因此,你能够访问的LinkedList的方法会变少。注意:与Queue相关的方法提供了完整而独立的功能。即,对于Queue所继承的Collection,在不需要使用它的任何方法的情况下,就可以拥有一个可用的Queue。

PriorityQueue:

先进先出描述了最典型的队列规则。队列规则是指在给定一组队列中的元素的情况下,确定下一个弹出队列的元素的规则。先进先出声明的是下一个元素应该是等待时间最长的元素。

优先级队列声明下一个弹出元素是最需要的元素(具有最高的优先级)。如果构建了一个消息系统,某些消息比其他消息更重要,因而应该更快地得到处理,那么它们何时得到处理就与它们何时到达无关。

/**

  • @Author: Ly

  • @Date: 2020-08-14 21:46

*/

public class PriorityQueueDemo {

public static void main(String[] args) {

PriorityQueue priorityQueue= new PriorityQueue ();

Random rand= new Random (47);

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

priorityQueue.offer(rand.nextInt(i+10));

QueueDemo.printQ(priorityQueue);

List ints = Arrays.asList(25,22,20,18,14,9,3,1,1,2,3,9,14,18,21,23,25);

priorityQueue=new PriorityQueue(ints);

QueueDemo.printQ(priorityQueue);

priorityQueue=new PriorityQueue(ints.size(), Collections.reverseOrder());

priorityQueue.addAll(ints);

QueueDemo.printQ(priorityQueue);

String fact = " EDUCATION SHOULD ESCHEW OBFUSCATION ";

List strings =Arrays.asList(fact.split(" "));

PriorityQueue stringPQ =new PriorityQueue(strings);

QueueDemo.printQ(stringPQ);

stringPQ= new PriorityQueue(

strings.size(),Collections.reverseOrder());

stringPQ.addAll(strings);

QueueDemo.printQ(stringPQ);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值