容器

1. 出现原因

解决程序运行时需要创建新对象,在程序运行前不知道运行的所需的对象数量甚至是类型的问题。

Java中提供了一套集合类来解决这些问题包括:List、Set、Queue、Map

2. Java容器分类

  • Collection:独立的元素序列,都服从一条或多条的规则

    1. List :必须按照插入顺序保存元素
    2. Set : 不能含有重复元素
    3. Queue :按照排队规则来确定对象的产生顺序
  • Map:成对的“键对值”对象,允许使用键来查找值
    1. Map
    2. ArrayList:利用数值来查找值

3.操作容器

3.1添加一组元素

Array.asList()方法:

该方法接受一个数组或用逗号隔开的元素列表,并转换为一个List对象。

Collection.addAll()方法:

接受一个Collection对象,可以使用Array.asList()产生Collection对象。

Collecions.addAll()方法:

接受一个Collection对象,以及数组或者逗号隔开的元素列表,并将元素添加到Collection对象中。

Collection()构造器方法:

Collection构造器方法接受一个Collection对象用于自身初始化。

public class TestAddObj {
public static void main(String[] args) {
Collection<Integer> collection = new ArrayList<>(Arrays.asList(1,2,3,4,8));// 构造器添加元素
Integer[] moreInts = {7,5,6};
collection.addAll(Arrays.asList(moreInts)); //Collection对象addAll()方法添加元素 Collections.addAll(collection,9,10,11,12);//Collections类addAll方法添加元素
Collections.addAll(collection,moreInts);//以Collection对象添加元素 for (int i: collection) {
System.out.print(i+"\t");//打印元素
} List<Integer> list = Arrays.asList(1,2,3,6); //直接使用Array.asList方法添加
// list.add(3); // 报错,使用该方法产生的List本质上数组,不能修改大小,即不能适应add方法添加元素或delete方法删除元素
list.set(2,10); System.out.println();
for (int i: list) {
System.out.print(i+"\t");
}
}
}

3.2 打印

使用打印默认的打印行为(容器提供的toString()方法

public class TestPrintContainers {
public static void main(String[] args) {
System.out.println(fill(new ArrayList<>()));
System.out.println(fill(new LinkedList<>()));
System.out.println(fill(new HashSet<>()));
System.out.println(fill(new TreeSet<>()));
System.out.println(fill(new LinkedHashSet<>()));
System.out.println(fill(new TreeMap<>()));
System.out.println(fill(new HashMap<>()));
System.out.println(fill(new LinkedHashMap<>())); }
static Collection<String> fill(Collection<String> collection){
collection.addAll(Arrays.asList("rat","cat","dog"));//使用Collection对象作为参数添加一组元素
return collection;
}
static Map<String,String> fill(Map<String,String> map){
map.put("rat","Fuzzy");
map.put("cat","Rags");//put<key,value>添加键值 get(key)获取键的值
map.put("dog","Bosco");
map.put("dog","Spot");
return map;
}
}

4. List

List接口在Collection接口的基础上添加了大量的方法,使得可以在List中间插入和移除元素

List接口的实现类

  • ArrayList

    长于随机访问元素,在其中插入和移除元素时较慢

  • LinkedList

    随机访问较慢,可以较低代价的在List中间插入和移除元素

public class testList {
public static void main(String[] args) {
Random rand = new Random(100);
ArrayList<Pet> pets = Pets.namePets(7);// 产生pets System.out.println("1: "+pets);
Pet lola = new Pet("lola"); pets.add(lola);//add(obj) 列表最后插入obj
System.out.println("2: "+pets);
System.out.println("3: "+pets.contains(lola));// contains()判断是否包含
pets.remove(lola); //remove(obj)删除列表中的对obj象
Pet p = pets.get(2); //get(Index)获取索引位置对象
System.out.println("4: "+p+" "+pets.indexOf(p));//indexOf(obj)获取obj对象的位置
Pet milo = new Pet("Milo");
pets.add(milo);
System.out.println("5: "+ pets.indexOf(milo));
System.out.println("6: "+ pets.remove(milo)); System.out.println("7: "+pets.remove(p));
System.out.println("8: "+pets); pets.add(3,new Pet("Jack"));//add(index,obj)将obj插入到index位置
System.out.println("9: "+pets); List<Pet> sub = pets.subList(1,4);//sub(start,end)获取[start,end)列表片段
System.out.println("10: sub list [1,4) "+sub);
System.out.println("11: "+ pets.containsAll(sub));//containsAll(listObj)判断是否包含listObj的全部对象(忽略顺序)
Collections.sort(sub);//Collections.sort(list)对列表list进行排序(需要list包含的对象实现泛型接口Comparable<>)
System.out.println("12: sorted sub "+ sub);
System.out.println("13: "+ pets.containsAll(sub));
Collections.shuffle(sub,rand);//Collections.shuffle(list,rand)将list按rand规则打乱
System.out.println("14: shuffle sublist "+sub);
System.out.println("15: "+pets.containsAll(sub)); List<Pet> copy = new ArrayList<>(pets);
sub = Arrays.asList(pets.get(1),pets.get(4));//获取pets的第一个和第4个对象,返回一个列表,传入sub
System.out.println("16: sub: "+sub);
copy.retainAll(sub);//retainAll(list)与list所有元素取交集
System.out.println("17: "+copy); copy = new ArrayList<>(pets);//构造器传参,初始化list
System.out.println(copy);
copy.remove(2);//remove(index)移除index位置对象
System.out.println("18: "+copy);
copy.removeAll(sub);//removeAll(list)与list取差集,移除list中包含的元素
System.out.println("19: "+copy); copy.set(1,new Pet("Toby"));//set(index,obj)将index位置元素替换成obj
System.out.println("20: "+copy); copy.addAll(2,sub);//add(index,list)将list所有元素插入到index位置
System.out.println("21: "+copy);
System.out.println("22: "+copy.isEmpty());//isEmpty()判断是否非空
copy.clear();//clear()移除所有元素
System.out.println("23: "+ copy.isEmpty()); pets.addAll(Pets.namePets(4));
System.out.println("24: "+pets);
Object[] o = pets.toArray();//toArray()转为数组
System.out.println("25: "+ o[3]);
Pet[] pas = pets.toArray(new Pet[0]);//toArray(T[])T[]为数组设置初始值
System.out.println("26: "+ pas[3].name); }
}
class Pets{
static ArrayList<Pet> namePets(int i){ //返回含有将i个Pet对象的列表
String[] names = new String[]{"Charlie","Max", "Buddy","Oscar",
"Milo","Bella","Molly"," Coco" ,"Ruby" ,"Lucy"};
Random random = new Random();
ArrayList<Pet> pets = new ArrayList<>();
for (int j = 0; j < i; j++) {
pets.add(new Pet(names[j]));
}
return pets;
} }
class Pet implements Comparable<Pet>{//继承泛型实现sort方法
public Pet(String name) {
this.name = name;
}
String name; @Override
public String toString() {//重写toString方法实现打印
return this.name;
} @Override
public int compareTo(Pet anotherPet) {//实现Comparable接口
return this.name.compareTo(anotherPet.name);
}
}

5.迭代器

迭代器是一个用于遍历并选择一个容器中元素的对象,而不必关心容器中的元素的底层结构。

Java中使用Iterator来表示迭代器对象

  • 使用iterator方法要求容器返回一个迭代器。Iterator将准备好返回序列的第一个元素
  • next()方法返回序列的下一个元素
  • hasNext()检查是否还有下一个元素
  • remove()移除序列最新返回的元素
public class TestIterator {
public static void main(String[] args) {
ArrayList<Pet> pets = Pets.namePets(9);
System.out.println(pets);
Iterator<Pet> petIterator = pets.iterator();//use method list.iterator() to create
while (petIterator.hasNext()){//判断
Pet p = petIterator.next();//返回下一个元素
System.out.print(p+"\t");
}
System.out.println();
for (Pet p: pets) { //通过Iterator实现的forEach循环遍历 本质上还是是Iterator
System.out.print(p+"\t");
} petIterator = pets.iterator();
for (int i = 0; i < 4; i++) {
petIterator.next();//返回下一个元素(头部开始)
petIterator.remove();//移除next()方法返回的元素,使用该方法必须先使用next()方法
}
System.out.println();
System.out.println(pets);
} }

使用迭代器时,不必关心序列底层结构,统一了对容器的访问方式

public class TestIterator1 {
public static void displayContainer(Iterator<Pet> petIterator){
while (petIterator.hasNext()){
Pet p = petIterator.next();
System.out.print(p+"\t");
}
System.out.println();
} public static void main(String[] args) {
ArrayList<Pet> pets= Pets.namePets(6);
LinkedList<Pet> petsLL = new LinkedList<>(pets);
HashSet<Pet> petsHS = new HashSet<>(pets);
TreeSet<Pet> petsTS = new TreeSet<>(pets);
displayContainer(pets.iterator());
displayContainer(petsLL.iterator()); //迭代器能够访问不同的容器
displayContainer(petsHS.iterator());
displayContainer(petsTS.iterator());
}
}

List类的专用迭代器ListIterator

ListIterator添加了previous方法和hasPrevious方法实现了对前一个元素的访问以及存在性判断;

添加了set方法替换访问过的最后一个元素

public class TestListIterator {

    public static void main(String[] args) {
List<Pet>pets = Pets.namePets(7);
ListIterator<Pet> petListIterator = pets.listIterator();
while (petListIterator.hasNext()){
System.out.print(petListIterator.next()+"\t");
}
System.out.println();
while (petListIterator.hasPrevious()){
System.out.print(petListIterator.previous()+"\t");//前移动访问
}
System.out.println();
petListIterator.next();
petListIterator.set(new Pet("Scout"));//将第一个元素替换
System.out.println(pets);
}
}

6. LinkedList

LinkedListArrayList一样实现了基本的List接口,它在中间插入更加高效,在随机访问上要慢一些

LinkedList还添加了作为栈、队列双端队列的方法

public class TestLinkedList {
public static void main(String[] args) {
LinkedList<Pet> pets = new LinkedList<>(Pets.namePets(8));
System.out.println(pets);
//获取头部元素,前面二者完全相同,当为空时抛出NoSuchElementException,peek()方法返回null
System.out.println("pets.getFirst(): "+pets.getFirst());
System.out.println("pets.element(): "+pets.element());
System.out.println("pets.peek(): "+pets.peek());
//移除头部元素,前面二者完全相同,当为空时抛出NoSuchElementException,poll()方法返回null
System.out.println("pets.remove(): "+pets.remove());
System.out.println("pets.removeFirst(): "+pets.removeFirst());
System.out.println("pets.poll(): "+pets.poll());
//头部插入
pets.addFirst(new Pet("Ollie"));
System.out.println("After addFirst: "+pets);
//尾部插入
pets.offer(Pets.randomPet());
System.out.println("After offer: "+pets);
pets.add(Pets.randomPet());
System.out.println("After add: "+pets);
pets.addLast(Pets.randomPet());
System.out.println("after addLast: "+pets);
//移除尾部元素
System.out.println("pets.removeLast(): "+pets.removeLast()); } }

7.Stack

栈通常是指“后进先出的容器”

LinckedList具有实现栈的全部功能的方法,因此可用LinkedList实现栈

但是如果只需要栈的行为,使用继承LinkedList的栈就不合适,因为会继承LinkedList的其他方法

Stack通常有这几类方法

  • push():接受T类型的对象,将其压入栈中
  • pop():移除栈顶元素并返回
  • peek():返回栈顶元素,但部移除
public class TestStack {
public static void main(String[] args) {
Stack<String> stringStack = new Stack<>();
for (String s: "My dog has fleas".split(" ")) {
stringStack.push(s);//入栈
}
System.out.println(stringStack);
while (!stringStack.empty()){//empty()方法判断栈是否为空
System.out.print(stringStack.pop()+"\t");//出栈
}
}
}

练习:将字母压入栈“+”压入,“-” 取出打印

"+U+n+c---+e+r+t---+a-+i-+n+t+y---+-+r+u--+l+e+s"
public class TestStack01 {
public static void main(String[] args) { char[] s = "+U+n+c---+e+r+t---+a-+i-+n+t+y---+-+r+u--+l+e+s".toCharArray();
charStack(s); }
public static Stack<Character> charStack ( char[] s){
Stack<Character> characterStack = new Stack<>();
for (int i = 0; i < s.length; i++) {
System.out.println(characterStack);
if (s[i] == '+') {//当遍历到字符数组为加号时,遍历后面元素,检测到非符号时压入栈中
for (int j = i; j < s.length; j++) {
if (!(s[j] == '+' || s[j] == '-')) {
characterStack.push(s[j]);
break;
}
}
} else if (s[i] == '-') {//检测到'-'弹出元素
characterStack.pop();
}
}
return characterStack;
}
}

8. Set

Set不能保存相同元素,将多个相同实例添加到Set时,就会阻止这种重复现象

Set具有与Collection完全一样的接口,Set实际上就是Collection只是行为不同

public class TestSet {
public static void main(String[] args) {
Random random = new Random(47);
Set<Integer> set = new HashSet<>();
for (int i = 0; i < 10000; i++) {
set.add(random.nextInt(30)); }
System.out.println(set);//HashSet输出无序
}
}
public class TestTreeSet {
public static void main(String[] args) {
Set<Integer> set = new TreeSet<>();
Random rand = new Random(47);
for (int i = 0; i < 1000; i++) {
set.add(rand.nextInt(30));
}
System.out.println(set);//TreeSet输出有序
}
}

Set操作。

public class SetOperations {
public static void main(String[] args) {
HashSet<String> s1 = new HashSet<>();
Collections.addAll(s1,"A B C D E F G H I J K L".split(" "));
s1.add("M");
System.out.println("Set1 contain H "+s1.contains("H"));
System.out.println("Set1 contain N "+s1.contains("N"));
HashSet<String> s2 = new HashSet<>();
Collections.addAll(s2,"H I J K L".split(" "));
System.out.println("set2 in set1 "+s1.containsAll(s2));
s1.remove("H");
System.out.println("Set1: "+s1);
System.out.println("Set2: "+s2);
System.out.println("set2 in set1 "+s1.containsAll(s2));
s1.removeAll(s2);
System.out.println("set2 removed from set1 "+ s1);
}
}

9. Map

  • Map不是collection的子接口或者实现类。Map是一个接口。

  • Map用于保存具有“映射关系”的数据。每个Entry都持有键-值两个对象。其中,Value可能重复,但是Key不允许重复(和Set类似)。

  • Map可以有多个Value为null,但是只能有一个Key为null。

实现类

HashMap

和HashSet集合不能保证元素的顺序一样,HashMap也不能保证key-value对的顺序。并且类似于HashSet判断两个key是否相等的标准一样: 两个key通过equals()方法比较返回true、 同时两个key的hashCode值也必须相等

LinkedHashMap

LinkedHashMap也使用双向链表来维护key-value对的次序,该链表负责维护Map的迭代顺序,与key-value对的插入顺序一致(注意和TreeMap对所有的key-value进行排序区分)

Properties

Properties对象在处理属性文件时特别方便(windows平台的.ini文件)。Properties类可以把Map对象和属性文件关联,从而把Map对象的key - value对写入到属性文件中,也可把属性文件中的“属性名-属性值”加载进Map对象中。

TreeMap

TreeMap是一个红黑树结构,每个键值对都作为红黑树的一个节点。TreeMap存储键值对时,需要根据key对节点进行排序,TreeMap可以保证所有的key-value对处于有序状态。 同时,TreeMap也有两种排序方式:自然排序、

public class TestMap {
public static void main(String[] args) {
Map<String,Pet> petMap = new HashMap<>();
petMap.put("MyCat",new Pet("Molly"));
petMap.put("MyDog",new Pet("Ginger"));
System.out.println(petMap);
Pet dog = petMap.get("MyDog");
System.out.println(dog);
System.out.println(petMap.containsKey("MyDog"));
System.out.println(petMap.containsValue(dog)); System.out.println(petMap.entrySet());//entrySet() 方法返回映射中包含的映射的 Set 视图
System.out.println(petMap.values() instanceof Collection);// 获取值返回一个Collection ArrayList<String> arrayList = new ArrayList<>();
System.out.println(Arrays.asList(1,2,3) instanceof List);
}
}

10. Queue

此接口用于模拟“队列”数据结构(FIFO)。新插入的元素放在队尾,队头存放着保存时间最长的元素

Queue接口与Collections接口中的方法是独立的

实现类

PriorityQueue—— 优先队列(类)

其实它并没有按照插入的顺序来存放元素,而是按照队列中某个属性的大小来排列的。故而叫优先队列。

Deque——双端队列(接口)

  • ArrayDeque

    基于数组的双端队列,类似于ArrayList有一个Object[] 数组。

  • LinkedList

  1. LinkedList实现
public class TestLinkedListQueue {
public static void printQ(Queue q){
while (q.peek()!=null){//peak()方法返回头部元素
System.out.print(q.peek()+"\t");//remove()方法移除并返回头部元素
q.remove();
}
} public static void main(String[] args) {
Queue<Integer> queue = new LinkedList<>();
Random random = new Random();
for (int i = 0; i < 10; i++) {
queue.offer(random.nextInt(i+10)); //offer()方法入队
}
printQ(queue);
System.out.println();
Queue<Character> characters = new LinkedList<>();
for (char c:"FuckYOU!!!!".toCharArray()){
characters.offer(c);
}
printQ(characters);
}
}
  1. PriorityQueue

    优先队列会根据元素的优先级对每个插入的元素进行排序,弹出的每个元素为此时优先级最高的元素

    优先队列的排序方式为默认排序方式,也可以提供Comparator来修改排序

    //优先队列2参数构造器,容量+比较方法
    public PriorityQueue(int initialCapacity,
    Comparator<? super E> comparator) {
    // Note: This restriction of at least one is not actually needed,
    // but continues for 1.5 compatibility
    if (initialCapacity < 1)
    throw new IllegalArgumentException();
    this.queue = new Object[initialCapacity];
    this.comparator = comparator;
    }
    public class TestPriorityQueue {
    public static void main(String[] args) {
    PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
    Random rand = new Random();
    for (int i = 0; i < 10; i++) {
    priorityQueue.offer(rand.nextInt(i+10));
    }
    TestLinkedListQueue.printQ(priorityQueue);
    List<Integer> integerList = Arrays.asList(99,2,22,100,2,7,33,1276);
    PriorityQueue<Integer> priorityQueue1 = new PriorityQueue<>(integerList);
    TestLinkedListQueue.printQ(priorityQueue1); priorityQueue1 = new PriorityQueue<>(integerList.size(), Collections.reverseOrder());//自定义优先级
    priorityQueue1.addAll(integerList);
    TestLinkedListQueue.printQ(priorityQueue1); String fact = "SB ZHI YUAN ZHE HUO DONG LANG FEI YE DE SHI JIAN";
    List<String> strings = Arrays.asList(fact.split(""));
    PriorityQueue<String> priorityQueue2 = new PriorityQueue<String>(strings);
    int pq2Size = priorityQueue2.size();
    TestLinkedListQueue.printQ(priorityQueue2);
    System.out.println(pq2Size); PriorityQueue<String> priorityQueue3 = new PriorityQueue<>(pq2Size,Collections.reverseOrder());
    priorityQueue3.addAll(strings);
    TestLinkedListQueue.printQ(priorityQueue3); }
    }

Java学习笔记--常用容器的更多相关文章

  1. Java 学习笔记(10)——容器

    之前学习了java中从语法到常用类的部分.在编程中有这样一类需求,就是要保存批量的相同数据类型.针对这种需求一般都是使用容器来存储.之前说过Java中的数组,但是数组不能改变长度.Java中提供了另一 ...

  2. JAVA学习笔记--初识容器类库

    一.前言 JAVA中一切皆为对象,因而,持有对象显得尤为重要. 在JAVA中,我们可以通过创建一个对象的引用的方式来持有对象: HoldingObject holding; 也可以创建一个对象数组来持 ...

  3. Qt学习笔记常用容器

    主要说Qt的以下几种容器 1.QList<T> 2.QLinkedList<T> 3.Map<T> 和一些常用的容器方法的使用 qSort qCopy qFind ...

  4. 5.10(java学习笔记)容器的同步控制与只读设置

    1.容器的同步控制 像我们平常使用的容器有些是不同步的即线程不安全,例如HashMap等,在多线程时可能出现并发问题. 而有些容器又是同步的,例如Hashtable. 有些时候我们需要将这些不同步的容 ...

  5. java学习笔记--常用类

    一.Math类:针对数学运算进行操作的类 1.常用的方法 A:绝对值   public static int abs(int a) B:向上取整  public static double ceil( ...

  6. 《Java学习笔记(第8版)》学习指导

    <Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...

  7. Java学习笔记--Swing用户界面组件

    很多与AWT类似. 事件处理参考:Java学习笔记--AWT事件处理 1.设计模式: 模型:存储内容视图:显示内容控制器:处理用户输入· 2. 文本输入常用组件 2.1 文本域: JLabel lab ...

  8. Java学习笔记4

    Java学习笔记4 1. JDK.JRE和JVM分别是什么,区别是什么? 答: ①.JDK 是整个Java的核心,包括了Java运行环境.Java工具和Java基础类库. ②.JRE(Java Run ...

  9. springmvc学习笔记(常用注解)

    springmvc学习笔记(常用注解) 1. @Controller @Controller注解用于表示一个类的实例是页面控制器(后面都将称为控制器). 使用@Controller注解定义的控制器有如 ...

随机推荐

  1. 一份热乎的字节跳动客户端面经,已拿Offer

    字节面试过程: 4月4号进行内推,7天的简历评估,11号接到电话面试,尽管猝不及防回答仓促,但好在前期准备充分,通过.14号现场面试,次日收到通知,通过,二面.三面都很顺利.20号进行HR面,26号收 ...

  2. sqli-labs lesson 11-15

    从这一关开始我们开始进入到post注入的世界了,什么是post呢?就是数据从客户端提交到服务器端,例如我们在登录过程中,输入用户名和密码,用户名和密码以表单的形式提交,提交到服务器后服务器再进行验证. ...

  3. DG:11.2.0.4 RAC在线duplicate恢复DG

    1.环境介绍 测试环境, 在一个双节点的RAC上使用duplicate搭建DG,使用在线的方式搭建 主机 IP 操作系统 实例 db_name db_unique_name db_version 配置 ...

  4. 题解 P3317 [SDOI2014]重建

    题解 前置芝士:深度理解的矩阵树定理 矩阵树定理能求生成树个数的原因是,它本质上求的是: \[\sum_T \prod_{e\in T} w_e \] 其中 \(w_e\) 是边权,那么我们会发现其实 ...

  5. Tomcat配置SSL证书(PFX证书)

    公司项目,应该是阿里云服务器 在windows2008 R2搭建的 Tomcat部署SSL证书,本文以PFX证书为例. 配置好之后开始 一.什么是SSL(证书)? SSL证书服务(Alibaba Cl ...

  6. ASP.NET Core:ASP.NET Core中使用NLog记录日志

    一.前言 在所有的应用程序中,日志功能是不可或缺的模块,我们可以根据日志信息进行调试.查看产生的错误信息,在ASP.NET Core中我们可以使用log4net或者NLog日志组件来实现记录日志的功能 ...

  7. ANSI C说明了三个用于存储空间动态分配的函数

    1.1 malloc的全称是memory allocation,中文叫动态内存分配.原型:extern void *malloc(unsigned int num_bytes);说明:分配长度为num ...

  8. C#中Finalize方法的问题

    ninputer在关于"值类型的Finalize不会被调用"中(http://blog.joycode.com/lijianzhong/archive/2005/01/13/429 ...

  9. 【转】Linux命令:ps -ef |grep java

    转自:https://www.cnblogs.com/feizifeiyu/p/8492550.html 一.ps -ef |grep java 查看包含"java"的所有进程 二 ...

  10. 通过refresh响应头,定时刷新或隔n秒跳转页面

    package day08; import java.io.IOException; import javax.servlet.ServletException; import javax.servl ...