大多数人,都低估了编程学习的难度,而高估了自己的学习能力和毅力。
当前系列: Java语法 修改

复习:J&C:集合概述 / 迭代器模式

Java中所有常用集合都在java.util包下,所以可以

import java.util.*;


List

Java中List本身是一个泛型接口,继承自Collection,最常用的实现类是ArrayList。

演示:

  • 除ArrayList以外,还有LinkedList、vector下的Stack
  • List中定义的方法:get()/set()/add()/remove()/indexOf()……

ArrayList

千万要忘了初始化!

ArrayList<Integer> scores = null;
//不会是false,而是报错:NullPointerException
System.out.println(scores.isEmpty()); 

其他增删改查方法:

ArrayList<Integer> scores = new ArrayList<>();
System.out.println(scores.size()); 

scores.add(12);   //按下标index依次添加的
scores.add(25);
scores.set(0, 21);   //只能改,不能加
System.out.println(scores.isEmpty());
System.out.println(scores.size());    //存放元素个数:2 System.out.println(scores.get(0));
System.out.println(scores.get(1));

System.out.println(scores.contains(21));    //true
System.out.println(scores.contains(28));    //false

System.out.println(scores.indexOf(21));   //返回下标:0
System.out.println(scores.indexOf(28));   //找不到:-1

scores.remove(0);    //删除下标为0的元素
scores.remove(12);    //删除元素12
scores.clear();    //清除所有元素 

size和capacity

为什么要使用集合?数组不够强大,^_^

比如:数组能不能add()?

但为什么ArrayList就能一直add()呢?其实现原理和StringBuilder非常类似:

  1. ArrayList内部维护着一个数组elementData,所有数据实际上装在这里面
  2. 数组有一个初始长度:DEFAULT_CAPACITY = 10,也可以在初始化时指定capacity,代替默认的
    public ArrayList(int initialCapacity) {
  3. 每次add()的时候检查数组长度是否足够,不够的话,自动扩容

源代码演示内部实现:扩容的方法

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;    //elementData:数组容器
        int newCapacity = oldCapacity + (oldCapacity >> 1);

PS:位运算右移(>>)各二进位全部右移若干位,相当于乘以2(但建议我们的代码也这么写:降低可读性)

@想一想@:既然ArrayList这么强大,还有必要使用数组么?

foreach

因为所有集合都采用迭代器模式,实现了iterable,所以可以用一种统一的模式来遍历集合元素。

Java干脆更进一步,在语法层面提供了foreach迭代:

List<Person> people = new ArrayList<>();
for (Person person : people) {
	System.out.println(person.Name);
}

@想一想@:能不能使用for循环么?

for (int i = 0; i < people.size(); i++) {
	System.out.println(people.get(i).Name);
}

能。但是:

  • foreach基于iterable,所有集合都可以使用
  • for基于index,只有List才能使用


Set

Set和List非常类似,都继承自Collection,所以有相同的增(add)删(remove/clear)查(isEmpty/contains)方法。

Set是一个接口,常用的实现类是HashSet和TreeSet(演示)

但是,因为Set不允许重复,所以重复的元素会被自动合并:

Set<Person> people = new HashSet<>();

Person atai = new Person();
people.add(atai);
people.add(atai);
people.add(atai);

System.out.println(people.size());   //始终是1

@想一想@:抛异常是不是更好一些?所以推荐这种写法:

if (!people.contains(atai)) {    //增加代码可读性
	people.add(atai);	
}

此外,因为Set没有索引,所以不能精确查找某个元素,当然也不能更改。一个变通的方法是:先删除,再添加。

//先删除后添加 = 更改
people.remove(atai);
Person bo = new Person();
bo.Name = "波仔";
people.add(bo);
最后,Set可以foreach遍历,没法for循环:
for (Person person : people) {
	System.out.println(person.Name);
}


Map

继承结构

Java中Map本身是一个泛型接口,常用的实现类有:HashMap和HashTable。

#常见面试题:两者的区别?#

主要是在线程安全(后文详述)方面:

  • HashMap:适合于单线程,非线程安全
  • Hashtable:适合于多线程,线程安全

因为HashMap更新(内部实现了Tree),一般情况推荐使用HashMap,多线程时推荐ConcurrentHashMap。

常用方法

scores.put("atai", 100.0);    //增
scores.remove("atai");    //删:通过key
scores.replace("atai", 98.5);     //改
//查
System.out.println(scores.get("atai"));
System.out.println(scores.getOrDefault("atai", 80.0));    //查不到给默认值
System.out.println(scores.containsKey("atai"));     //检查“键”
System.out.println(scores.containsValue(100.0));    //检查“值”

注意:Map的key也是不允许重复的,如果put时有重复,和Set一样处理。不报错,后面的键值对覆盖前面的。

没有foreach

因为Map没有继承自iterable,所以是能直接foreach遍历的!

如果确实要遍历,需要首先进行转化,拿到

  • 所有的键(key):
    for (String student : scores.keySet()) {
  • 所有的值(value):
    for (Double score : scores.values()) {
  • 所有的键值对(entry)
    for (Map.Entry<String, Double> entry : scores.entrySet()) {
    	System.out.println(
    			entry.getKey() +   //键
    			":" + 
    			entry.getValue()); //值
    }
    注意Entry是Map的内部接口,所以只能通过Map点出。
    另:entry还有一个setValue()方法,可以更改它的值。


作业

见:J&C:集合概述 / 迭代器模式 / ER模型 / 仓储模式

Java 集合
觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 Java语法 中继续学习:

我们的 特色

  • 先学习,后付费
  • 线上/线下,自由组合

更多了解

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧