JavaSE必备知识点4-容器详解

JavaSE必备知识点—容器(集合)

数组本身就是容器

数组就是一种容器,可以在其中放置对象或基本数据类型。

优势:是一种简单的线性序列,可以快速地访问数组元素,效率高。如果从效率和类型检查的角度讲,数组是最好的。

劣势:不灵活。容量需要事先定义好,不能随着需求的变化而扩容。 图9-1容器的接口层次结构图.png

Collection

Collection 表示一组对象,它是集中、收集的意思。Collection接口的两个子接口是ListSet接口。他们和数组的主要区别主要请点击这里

由于List、Set是Collection的子接口,意味着所有List、Set的实现类都有上面的方法。我们下一节中,通过ArrayList实现类来测试上面的方法 。下面给出代码示例:

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
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.x1aolin.a;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class TestList {
public static void main(String[] args) {
Collection<String> coll = new ArrayList<String>();

coll.add("aaa"); //添加元素
coll.add("bbb");
System.out.println(coll.isEmpty());//检查是否为空
System.out.println(coll.size()); //容器中元素的个数
System.out.println(coll.contains("aaa")); //容器中是否包含该元素
Object[] ob = coll.toArray(); //转化出一个Object数组
System.out.println(ob);
System.out.println(coll); //实际调用其toString()方法
//只是移除,并没有删除 容器内没有了,但是该对象还是实际存在的。 相当于只是删除了该容器与对象的联系
//(地址删除了,但对象还在)
coll.remove("aaa");
System.out.println(coll); //实际调用其toString()方法
coll.clear(); //移除所有的元素

Collection<Integer> li01= new ArrayList<Integer>();
li01.add(1);
li01.add(2);
li01.add(3);
List<Integer> li02= new ArrayList<>();
li02.add(3);
li02.add(4);
li02.add(5);
System.out.println("li01:"+li01);
li01.addAll(li02); //将容器li02中的所有元素增加到本(li01)容器
li01.removeAll(li02); //将容器li02中的所有与li01容器相同的元素删除
li01.retainAll(li02); //取交集
System.out.println("li01:"+li01);
boolean flag = li01.containsAll(li02); //检查li01是否包含li02的所有元素,包含返回true
System.out.println(flag);

}
}

List

List是有序,可重复的容器。

有序:List中每个元素都有索引标记。可以根据元素的索引标记(在List中的位置)访问元素,从而精确控制这些元素。

可重复:List允许加入重复的元素。更确切的讲,List通常允许满足e1.equals(e2)的元素重复加入容器。

List接口常用实现类有3个:ArrayListLinkedListVector等。它们分别具有不同的特点:ArrayList查询效率高,常用;若该容器经常涉及到增删操作,建议使用LinkedList;若考虑线程的安全性,则可以使用Vector

表9-2 List接口中定义的方法.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void test02(){
List<String> l = new ArrayList<>();
l.add("A");
l.add("B");
l.add("C");
l.add("D");
System.out.println(l.toString());
l.add(2, "x1aolin");
System.out.println(l.toString());
// l.remove("B"); //按照对象进行删除,若容器为整型,需要人为装箱,把int -> Integer对象,即强制转型
l.remove(2); //按照索引进行删除
l.set(2, "x1aolin");
System.out.println(l);
System.out.println(l.get(2));
l.add("C");
l.add("B");
l.add("A");
System.out.println(l);
System.out.println(l.indexOf("B")); //容器中出现B的第一个位置 , 从0开始 不存在返回-1
System.out.println(l.lastIndexOf("B")); //容器中出现B的最后一个位置,从0开始 不存在返回-1
}

ArrayList

ArrayList底层使用数组实现的存储,默认长度是0,后续会根据需要逐步扩充,数组大小每次增加(原数组长度+1)的50%。 特点:查询效率高,增删效率低(因为本质是数组的拷贝),线程不安全。我们经常使用它。

Question:既然ArrayList底层使用数组实现的存储,那数组长度不是有限且定死的嘛,怎么ArrayList可以存放任意数量的对象呢?

通过观看源码我们会发现,上述功能本质上就是通过定义新的更大的数组,将旧数组中的内容拷贝到新数组,来实现扩容。 下面代码并非源码!!只是自身写的,用于熟悉底层原理!

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package com.x1aolin.myCollectin;
/**
* 自定义实现一个arrayList,体会底层原理
* 增加泛型02
* 增加数组扩容功能03
* 增加set与get方法 以及数组边界的检查04
* 增加remove功能 05
* @author x1aolin
*
*/
public class PrivateArrayList05<E>{
private Object[] elementData;
private int size;
private static final int DEFAULT_CAPACITY = 10;

public PrivateArrayList05(){
elementData = new Object[DEFAULT_CAPACITY]; //初始化
}
public PrivateArrayList05(int capacity){

if(capacity<0){
throw new RuntimeException("容器的容量不能为负数:"+capacity);
}else if(capacity==0){
elementData = new Object[DEFAULT_CAPACITY];
}else{
elementData = new Object[capacity];
}

}
public void add(E element){
//什么时候扩容??
if(size >= elementData.length){
//数组扩容为原来的1.5倍 这里有个优先级小问题,注意加括号
Object[] newArray = new Object[elementData.length+(elementData.length>>1)];
System.arraycopy(elementData, 0, newArray, 0, elementData.length);
elementData = newArray;
elementData[size++] = element;
}else{
elementData[size++] = element;
}
}
public E get(int index){
checkRange(index);
return (E)elementData[index];
}
public void set(E element,int index){
//索引合法判断
checkRange(index);
elementData[index] = element;
}
public void remove(E element){
//将element与容器中所有元素挨个比较,获得第一个比较为true的,返回。
for(int i=0;i<size;i++){
if(element.equals(get(i))){ //容器中所有的比较操作用的都是equals,而不是==
remove(i);
}
}
}
public void remove(int index){
checkRange(index);
if((elementData.length-index-1)>0)
System.arraycopy(elementData, index+1, elementData, index, elementData.length-index-1);
elementData[--size] = null;
}
public void checkRange(int index){
if(index<0||index>=size){
throw new RuntimeException("索引不合法:"+index); //手动抛一个异常
}
}
public int size(){
return size;
}
public boolean isEmpty(){
return size==0?true:false;
}
@Override
public String toString() {
StringBuffer str = new StringBuffer();
str.append('[');
for(int i=0;i<size;i++){
str.append(elementData[i]+",");
}
str.setCharAt(str.length()-1, ']');
return str.toString();
}

public static void main(String[] args) {
PrivateArrayList05<String> s1 = new PrivateArrayList05<>(12);
for(int i=0;i<30;i++){
s1.add("NO"+i);
}
s1.remove(1);
System.out.println(s1);
System.out.println(s1.get(1));
s1.set("hhhhh", 1);
System.out.println(s1);
System.out.println(s1.size());
System.out.println(s1.isEmpty());
}
}

LinkedList

LinkedList底层用双向链表实现的存储,设置了first和last引用来记录链表中的第一个和最后一个结点。 特点:查询效率低,增删效率高,线程不安全。双向链表示意图如下:

下面给出自定义LinkedList,来进一步熟悉底层代码。不是源码,想使用该类中的方法请自行查看源码。

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package com.x1aolin.myCollectin;
/**
* 自定义一个链表
* 增加泛型和get方法02
* 增加remove方法 03
* 增加 插入节点方法 04
* alt+shift+R 可以一次命名一个方法中的所有的名称
* @author x1aolin
*
*/
public class PerLinkedList04<E>{
private Node first;
private Node last;
private int size;
//[]
//["a","b"]
public void add(E e){
Node node = new Node(e);
//说明是第一次放东西
if(first==null){
node.previous = node;
node.next = node;
first = node;
last = node;
size++;
}else{
node.previous = last;
node.next = first;

last.next = node;
last = node;
first.previous = last;
size++;
}
}
public E get(int index){
//检查是否越界
checkRange(index);
//优化查询,使其尽量运行的快一些
Node temp = getNode(index);
return temp!=null?(E)temp.element:null;
}
private Node getNode(int index){
checkRange(index);
Node temp = null;
if(index<=size/2){
temp = first;
// System.out.println("PerLinkedList02.get(if)");
for(int i=0;i<index;i++){
temp = temp.next;
}
}else{
//["a","b","c","d","e","f"]
temp = last;
// System.out.println("PerLinkedList02.get(else)");
for(int i=0;i<size-index-1;i++){
temp = temp.previous;
}
}
return temp;
}
@Override
public String toString() {
Node temp = first;
StringBuilder sb = new StringBuilder("[");
do{
sb.append(temp.element+",");
temp = temp.next;
}while(temp!=first);
sb.setCharAt(sb.length()-1, ']');
return sb.toString();
}
//判断是否出界
private void checkRange(int index){
if(index<0||index>=size){
throw new RuntimeException("索引数字不合法:"+index);
}
}
//删除节点
public void remove(int index){
checkRange(index);
Node temp = getNode(index);
if(temp!=null){
if(index==0){
first = first.next;
}else if(index == size-1){
last = last.previous;
}
Node up = temp.previous;
Node down = temp.next;
up.next = down;
down.previous = up;
size--;
}
}
//插入节点
public void add(E e,int index){
checkRange(index);
//思路 先创造一个节点 然后插入到链表当中
Node temp = new Node(e); //生成要插入的节点
if(temp!=null){
Node hh = getNode(index); //找到要插入的位置
Node up = hh.previous;
up.next = temp;
temp.previous = up;
temp.next = hh;
hh.previous = temp;
if(index==0){
first = temp;
}else if(index==size-1){
last = temp;
}
}
}
public static void main(String[] args) {
PerLinkedList04<String> list = new PerLinkedList04<>();
list.add("a");
list.add("b");
list.add("c");
// list.remove(0);
list.add("hhhhh", 0);
System.out.println(list);
System.out.println(list.get(0));
}
}

Vector

同ArrayList一样,Vector底层是用数组实现的List,通过查看源码可以发现,Vector可以人为确定初始数组大小,若不人为确定,则默认为10。另外,Vector可以人为决定拓展大小,若不人为确定或者输入的数据不合法(负数或0),则会拓展一倍。

Vector相关的方法都加了同步检查,因此“线程安全,效率低”。 比如,indexOf方法就增加了synchronized同步标记。 随机截取Vector源码如下:

1
2
3
4
//表示该方法增加了同步检查 是多线程方面的    
public synchronized boolean isEmpty() {
return elementCount == 0;
}

增加了同步标记就意味着效率的降低,所以只有在该容器对象需要多个线程共享的时候,才考虑使用Vector,其余时间使用ArrayList。

CopyOnWriteArrayList

这里仅需要了解即可,这个在JUC并发编程中进行使用,能够保持并发同步,在后面学习的多线程领域可以使用。

1
2
//在写的基础上进行拷贝
import java.util.concurrent.CopyOnWriteArrayList;

Map

map主要用于成对存储某些信息。Map就是用来存储“键(key)-值(value)对”的。Map类中存储的“键值对”通过键来标识,即用键对象来查找值对象,所以“键对象”不能够重复。

重复不会编译出错,但是会采用最新的那个设定,前面的都会失效!!是否重复根据equals方法来进行确认。它和数组的区别就是他的键可以为任意形式的对象,而不仅仅是整型值,使用时更加方便。

Map接口的实现类有HashMap TreeMap HashTable Properties等,第一个最常用。

表9-3 Map接口中常用的方法.png

键对象重复判定本质上使用了equals()方法,而不是==

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.x1aolin.myCollectin;
import java.util.HashMap;
import java.util.Map;

public class TestMap {
public static void main(String[] args) {
Employee e1 = new Employee(1001,"x1aolin_1",50000);
Employee e2 = new Employee(1002,"x1aolin_2",100000);
Employee e3 = new Employee(1003,"x1aolin_3",150000);

Map<Integer,Employee> map = new HashMap<>();
map.put(1001, e1); //键 值 对
map.put(1002, e2);
map.put(1003, e3);
Employee emp = map.get(1001);
System.out.println(emp.getEname());
}
}
class Employee{
private int id;
private String ename;
private double salary;

public Employee(int id, String ename, double salary) {
super();
this.id = id;
this.ename = ename;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}

HashMap

HashMap采用哈希算法实现,是Map接口最常用的实现类。 由于底层采用了哈希表存储数据,我们要求键不能重复,如果发生重复,新的键值对会替换旧的键值对。 HashMap在查找、删除、修改方面都有非常高的效率。

在JDK1.8之前,hashMap都是采用了数组+链表的结构,但是到了JDK1.8之后,当链表的存储数据个数大于等于8的时候,不再采用链表存储,而采用了红黑树存储结构。这样做主要是查询的时间复杂度上,链表为O(n),而红黑树一直都是O(log2n)。如果冲突多,并且超过8,采用红黑树来提高效率。这个大家暂时没必要理解,先看下面的实现方案。

数组:占用空间连续。寻址容易,查询速度快。但是,增加和删除效率非常低。

链表:占用空间不连续。寻址困难,查询速度慢。但是,增加和删除效率非常高。

hashMap底层采用了哈希表,其本质就是“数组+链表”,详细解释请点击这里

图9-15 Entry数组存储结构图.png

存储数据过程put(key,value)

在看之前,如果您不了解什么是hashCode请点击这里:Java提高篇——equals()与hashCode()方法详解

下面给出底层键值对的存储过程,数组默认大小为16,后续可能会扩充。另外,为提高效率,我们应该设计算法使对象尽可能地散列到这些数组当中。如何散列呢,一开始的想法是用除法取余数,但是除法效率有点慢,因此我们首先约定数组长度必须为2的整数幂,这样采用位运算即可实现取余数效果:hashValue = hashcode&(数组长度-1)。当然,后续为了更“散“,jdk又对其进行了改进,这里就不再介绍了。

当添加一个元素(key-value)时,首先计算key的hash值,以此确定插入数组中的位置,但是可能存在同一hash值的元素已经被放在数组同一位置了,这时就添加到同一hash值的元素的后面,他们在数组的同一位置,就形成了链表,同一个链表上的Hash值是相同的,所以说数组存放的是链表。 JDK8中,当链表长度大于8时,链表就转换为红黑树,这样又大大提高了查找的效率。

查找键值对过程

当我们取数get(key)的时候,先计算其key对象的hashcode,并计算hash值,找到对应的数组位置后,根据equals()方法依次比较,找到之后返回即可。

Java对象的eqauls方法和hashCode方法是这样规定的:

  1. 相等(相同)的对象必须具有相等的哈希码(或者散列码)。
  2. 如果两个对象的hashCode相同,它们并不一定相同。

下面给出手工实现hashMap的代码,不是源码!!!

Node2.java

1
2
3
4
5
6
7
8
package com.x1aolin.myCollectin;
//用于自定义hashmap中
public class Node2<K,V> {
int hash;
K key;
V value;
Node2 next;
}

PerHashMap04.java

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package com.x1aolin.myCollectin;

public class PerHashMap04<K,V>{
Node2<K,V>[] table; //位桶数组
int size; //存放键值对的个数
//初始化
@SuppressWarnings("unchecked")
public PerHashMap04(){
table = new Node2[16]; //长度一般定义成2的整数幂
}
@SuppressWarnings("unchecked")
public void put(K key,V value){
//如果要完善,还需要考虑数组扩容的问题
//定义了新的节点对象
Node2<K,V> newNode = new Node2<>();
newNode.key = key;
newNode.value = value;
newNode.hash = myHash(key.hashCode(),table.length);
newNode.next = null;
//将其插入对应的hash位置
Node2<K,V> temp = table[newNode.hash];
Node2<K,V> iterNode = null;
boolean flag = true;
if(temp==null){ //位桶数组的首个为空,则直接将新节点放进去
table[newNode.hash] = newNode;
size++;
}
else{
//此处节点不为空,则遍历对应链表
while(temp!=null){
//判断key如果和第一个重复,则覆盖,如果不重复,则将其放到该链表最后
if(temp.key.equals(newNode.key)){
flag = false;
temp.value = value;
break;
}
else{
iterNode = temp;
temp = temp.next;
}
}
if(flag){ //如果执行覆盖就不用执行下面的代码了
iterNode.next = newNode;
size++;
}
}
}
//重写toString方法
public String toString() {
StringBuilder sb =new StringBuilder("{");
for(int i=0;i<table.length;i++){ //遍历位桶数组
//遍历每个链表
Node2<K,V> t = table[i];
while(t!=null){
sb.append(t.key+":"+t.value+",");
t = t.next;
}
}
sb.setCharAt(sb.length()-1, '}');
return sb.toString();
}

public V get(K key){
//没必要遍历,浪费时间 利用hash值找到对应数组列即可
int i = myHash(key.hashCode(),table.length);
//遍历每个链表
Node2<K,V> t = table[i];
while(t!=null){
if(t.key.equals(key)){
return (V)t.value;
}
t = t.next;
}
return null; //若找不到则返回空
}
public int myHash(int v,int length){
return v&(length-1);
}
public static void main(String[] args) {
PerHashMap04<Integer,String> m = new PerHashMap04<>();
m.put(16, "AA");
m.put(32, "BB");
m.put(48, "CC");
m.put(63, "DD");
m.put(81, "EE");
m.put(96, "FF");
System.out.println(m);
System.out.println(m.get(63));
System.out.println(m.get(33));
}
}

HashTable

HashTable类和HashMap用法几乎一样,底层实现几乎一样,都是实现了哈希表,只不过HashTable的方法添加了synchronized关键字确保线程同步检查,效率较低。 HashMap: 线程不安全,效率高。允许keyvaluenullHashTable: 线程安全,效率低。不允许keyvaluenull

TreeMap

TreeMap是红黑二叉树的典型实现。TreeMap和HashMap实现了同样的接口Map,因此,用法对于调用者来说没有区别。HashMap效率高于TreeMap,在需要排序的Map时才选用TreeMap。

1
TreeMap<Integer,String> trmp = new TreeMap<>(); //所含方法就是Map里面的方法

排序是按照key递增的方式排序,但若key为自定义对象类型,那么应该如何递增呢?这就需要实现Comparable接口来自定义排列顺序,该接口只有一个public int compareTo(T o)方法,我们要做的就是去重写该方法,下面给出重写规则介绍!此情况下该接口必须实现!

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import java.util.TreeMap;
import java.util.Map;

class Emp implements Comparable<Emp>{
int id;
String name;
int salary;

public Emp(int id, String name, int salary) {
super();
this.id = id;
this.name = name;
this.salary = salary;
}
//正数 大于,负数 小于,0 等于,在等于的时候会覆盖旧值!!!
public int compareTo(Emp o){
if(this.salary>o.salary) return 1;
else if(this.salary<o.salary) return -1;
else{
if(this.id>o.id) return 1;
else if(this.id<o.id) return -1;
else return 0;
}
}
//重写toString()方法,其余地方也可参考
public String toString() {
// TODO Auto-generated method stub
return id+" "+name+" "+salary;
}
}
public class TestTreeMap {
public static void main(String[] args) {
Map<Emp,String> map = new TreeMap<>();
Emp a = new Emp(1001,"张三",5000);
Emp b = new Emp(100,"李四",5000);
map.put(a, "工作很勤奋");
map.put(b, "努力学习");
for(Emp key:map.keySet()){
System.out.println(key.name+"--"+map.get(key));
}
//打印时先打印李四,然后打印张三,因为薪资一样,id小的放前面。
}
}

Set

Set接口继承自Collection,Set接口中没有新增方法,方法和Collection保持完全一致,下面给出例子

Set容器特点:无序、不可重复。无序指Set中的元素没有索引,我们只能遍历查找;不可重复指不允许加入重复的元素。更确切地讲,新元素如果和Set中某个元素通过equals()方法对比为true,则不能加入;甚至,Set中也只能放入一个null元素,不能多个。

Set常用的实现类有:HashSet、TreeSet等,我们一般使用HashSet。遍历时,因为无序,所以我们可以采用增强for循环来遍历。

Hashset

HashSet是采用哈希算法实现,底层实际是用HashMap实现的(HashSet本质就是一个简化版的HashMap),因此,查询效率和增删效率都比较高。

查看源码即可知,hashSet本质上是添加hashMap的key,hashMap的key不可重复,所以hashSet的key不可重复。

手动实现,不是源码:

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
30
31
import java.util.HashMap;
public class NewHashSet {
HashMap map;
private static final Object PRESENT = new Object();
public NewHashSet(){
map = new HashMap();
}
public void add(Object o){
map.put(o, PRESENT);
}
public int size(){
return map.size();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[");
for(Object key:map.keySet()){
sb.append(key+",");
}
sb.setCharAt(sb.length()-1, ']');
return sb.toString();
}
public static void main(String[] args) {
NewHashSet set = new NewHashSet();
set.add("aaa");
set.add("bbb");
set.add("ccc");
System.out.println(set);
}
}

TreeSet

TreeSet底层实际是用TreeMap实现的,内部维持了一个简化版的TreeMap,通过key来存储Set的元素。 TreeSet内部需要对存储的元素进行排序,因此,我们对应的类需要实现Comparable接口。这样,才能根据compareTo()方法比较对象之间的大小,才能进行内部排序 。

迭代器

迭代器为我们提供了统一的遍历容器的方式。 如果遇到遍历容器时,判断删除元素的情况,使用迭代器遍历! 大家先看一下它的部分源码:

1
2
3
4
5
public interface Iterator<E> {
boolean hasNext(); //判定是否还有下一个
E next(); //指向下一个元素,直到没有下一个为止。
}
}

下面是对各种容器遍历的示例,请大家依次对号入座:

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* 测试迭代器遍历List、Set、Map
* @author x1aolin
*/
public class TestIterator {
public static void main(String[] args) {
//这里面调用对应函数
}
//List
public static void testList(){
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
//获取迭代器
for(Iterator<String> iter = list.iterator();iter.hasNext();){
String temp = iter.next(); //放回当前内容,并且游标指向下一个
System.out.println(temp);
}
}
//Set
public static void testSet(){
Set<String> set = new HashSet<>();
set.add("qq");
set.add("ww");
//获取迭代器
for(Iterator<String> iter = set.iterator();iter.hasNext();){
String temp = iter.next(); //向下迭代
System.out.println(temp);
}
}
//Map方法1
public static void testMap01(){
Map<Integer,String> map = new HashMap<>();
map.put(1,"qqq");
map.put(2,"www");
//entrySet是返回map中所有的键值对
Set<Entry<Integer,String>> ss = map.entrySet();
for(Iterator<Entry<Integer,String>> iter = ss.iterator();iter.hasNext();){
Entry<Integer,String> temp = iter.next(); //向下迭代
System.out.println(temp.getKey()+"---"+temp.getValue());
}
}
//Map方法2
public static void testMap02(){
Map<Integer,String> map = new HashMap<>();
map.put(1,"q");
map.put(2,"w");
//keySet()是返回map中所有的键,然后通过键来获取值,键值对嘛。
Set<Integer> ss = map.keySet();
for(Iterator<Integer> iter = ss.iterator();iter.hasNext();){
Integer temp = iter.next(); //向下迭代
System.out.println(temp+"---"+map.get(temp));
}
}
}

Collections工具类

要注意区别上面的Collection接口,工具类里面都是静态方法,调用的时候直接使用类名进行调用。类 java.util.Collections 提供了对Set、List、Map进行排序、填充、查找元素的辅助方法。

  1. void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序,自定义需要实现Comparable接口。
  2. void shuffle(List) //对List容器内的元素进行随机排列。
  3. void reverse(List) //对List容器内的元素进行逆续排列 。
  4. void fill(List, Object) //用一个特定的对象重写整个List容器。
  5. int binarySearch(List, Object) //对于顺序的List容器,采用折半查找的方法查找特定对象。

容器遍历方法汇总

点击上标题即可查看。

您的每一份支持将鼓励我继续创作!
-------------本文结束感谢您的阅读-------------