1.ArrayList
1.1与数组的区别
数组局限性:多了放不下,少了浪费
为了解决数组的局限性,引入容器类的概念。 最常见的容器类就是
ArrayList
“capacity"会随着对象的增加,自动扩容,默认容量10,int
newCapacity = (oldCapacity *
3
)/
2
+
1
;
只需要不断往容器里增加即可,不用担心会出现数组的边界问题。
当数组的全部空间用尽,调用add时,数组列表将自动地创建一个更大的数组,并将所有对象从较小的数组中拷贝到较大的数组中。
优化:
如果已经清楚或能够估计出数组可能存储的元素数量,就可以在填充数组之前调用ensureCapacity方法:
staff.ensureCapacity(100);
该方法调用将分配一个包含100个对象的内部数组。然后可以调用100次add,而不用重新分配空间。
另外,还可以把初始容量传递给ArrayList构造器:
ArrayList<Hero> hero =new ArrayList<>(100);
1.2常用方法
add | 增加 heros.add(3, specialHero); | |
contains | 判断是否存在容器中。标准:是否是同一个对象,而不是name是否相同 | |
get | 获取指定位置的对象 越界会报错 | |
indexOf | 获取对象所处的位置。 标准:是否是同一个对象,而不是name是否相同 | |
remove | 删除 heros.remove(2);//根据下标删除,或者根据对象删除 | |
set | 替换 | |
size | 获取大小 | |
toArray | 转换为数组 | |
addAll | 把另一个容器所有对象都加进来 | |
clear | 清空集合 |
1.3ArrayList和List
ArrayList实现了List接口,所以List接口的方法ArrayList都能用
常见的写法会把引用声明为接口List类型
注意:是java.util.List,而不是java.awt.List
public class TsetCollection {
public static void main(String[] args) {
//接口引用指向子类对象(体现多态)
List a1 = new ArrayList();
a1.add(new Hero("盖伦"));
System.out.println(a1.size());
}
}
1.4泛型 Generic
- 不指定泛型的容器,可以存放任何类型的元素
- 指定了泛型的容器,只能存放指定类型的元素以及其子类
- 泛型<Type>可以是类,抽象类,接口
public class TsetCollection {
public static void main(String[] args) {
//对于不使用泛型的容器,可以往里面放英雄,也可以往里面放物品
List a1 = new ArrayList();
a1.add(new Hero("盖伦"));
//本来用于存放英雄的容器,现在也可以存放物品了
a1.add(new Item("血瓶"));
//对象转型会出现问题
//容器里放的对象太多的时候,就记不清楚哪个位置放的是哪种类型的对象了
//引入泛型Generic
//声明容器的时候,就指定了这种容器,只能放Hero,放其他的就会出错
List<Hero> a2 = new ArrayList<>();
a2.add(new Hero("提莫"));
//a2.add(new Item("宝剑")); 报错了
//除此之外,还能存放Hero的子类
//并且在取出数据的时候,不需要再进行转型了,因为里面肯定是放的Hero或者其子类
Hero h = a2.get(0);
}
}
1.5遍历
用for循环遍历
通过前面的学习,知道了可以用size()和get()分别得到大小,和获取指定位置的元素,结合for循环就可以遍历出ArrayList的内容
public static void main(String[] args) {
//对于不使用泛型的容器,可以往里面放英雄,也可以往里面放物品
List<Hero> heros = new ArrayList<>();
//放5个Hero进去
for(int i=0;i<5;i++) {
heros.add(new Hero("hero"+i));
}
// 第一种遍历 for循环
System.out.println("--------for 循环-------");
for(int j=0;j<heros.size();j++) {
Hero h = heros.get(j);
System.out.println(h);
}
}
迭代器遍历
public static void main(String[] args) {
//对于不使用泛型的容器,可以往里面放英雄,也可以往里面放物品
List<Hero> heros = new ArrayList<>();
//放5个Hero进去
for(int i=0;i<5;i++) {
heros.add(new Hero("hero"+i));
}
//第二种遍历,使用迭代器
System.out.println("--------for的迭代器-------");
for(Iterator<Hero> it = heros.iterator();it.hasNext();){
Hero hero = it.next();
System.out.println(hero);
}
}
用增强型for循环
//第三种遍历,增强型for循环
System.out.println("--------增强型for循环-------");
for(Hero h:heros) {
System.out.println(h);
}
使用增强型for循环可以非常方便的遍历ArrayList中的元素,这是很多开发人员的首选。
不过增强型for循环也有不足:
- 无法用来进行ArrayList的初始化
- 无法得知当前是第几个元素了,当需要只打印单数元素的时候,就做不到了。 必须再自定下标变量。
2.LinkedList
序列分为:
- 先进先出FIFO,在Java中又叫Queue 队列
- 先进后出FILO,在Java中又叫Stack 栈
2.1 LinkedListList和List
与ArrayList一样,LinkedList也实现了List接口,诸如add,remove,contains等等方法。
2.2双向链表 - Deque
除了实现了List接口外,LinkedList还实现了双向链表结构Deque,可以很方便的在头尾插入删除数据
public class TsetCollection {
public static void main(String[] args) {
//对于不使用泛型的容器,可以往里面放英雄,也可以往里面放物品
List<Hero> heros = new LinkedList<>();
//LinkedList是一个双向链表结构的list
LinkedList<Hero> ll =new LinkedList<Hero>();
//所以可以很方便的在头部和尾部插入数据
//在最后插入新的英雄
ll.addLast(new Hero("hero1"));
ll.addLast(new Hero("hero2"));
ll.addLast(new Hero("hero3"));
System.out.println(ll);
//在最前面插入新的英雄
ll.addFirst(new Hero("heroX"));
System.out.println(ll);
//查看最前面的英雄
System.out.println(ll.getFirst());
//查看最后面的英雄
System.out.println(ll.getLast());
//查看不会导致英雄被删除
System.out.println(ll);
//取出最前面的英雄
System.out.println(ll.removeFirst());
//取出最后面的英雄
System.out.println(ll.removeLast());
//取出会导致英雄被删除
System.out.println(ll);
}
}
2.3 队列 Queue
LinkedList实现了Deque,进而又实现了Queue这个接口,Deque继承了Queue
Queue是先进先出队列 FIFO,常用方法:
offer 在最后添加元素
poll 取出第一个元素
peek 查看第一个元素
public class TestCollection {
public static void main(String[] args) {
//和ArrayList一样,LinkedList也实现了List接口
List ll =new LinkedList<Hero>();
//所不同的是LinkedList还实现了Deque,进而又实现了Queue这个接口
//Queue代表FIFO 先进先出的队列
Queue<Hero> q= new LinkedList<Hero>();
//加在队列的最后面
System.out.print("初始化队列:\t");
q.offer(new Hero("Hero1"));
q.offer(new Hero("Hero2"));
q.offer(new Hero("Hero3"));
q.offer(new Hero("Hero4"));
System.out.println(q);
System.out.print("把第一个元素取poll()出来:\t");
//取出第一个Hero,FIFO 先进先出
Hero h = q.poll();
System.out.println(h);
System.out.print("取出第一个元素之后的队列:\t");
System.out.println(q);
//把第一个拿出来看一看,但是不取出来
h=q.peek();
System.out.print("查看peek()第一个元素:\t");
System.out.println(h);
System.out.print("查看并不会导致第一个元素被取出来:\t");
System.out.println(q);
}
}
ArrayList和LinkedList还有Vector的区别
- ArrayList 插入,删除数据慢
- LinkedList, 插入,删除数据快
- ArrayList是线程不安全的,多线程下使用Vector代替(有同步关键字),LinkedList也是线程不安全的。
ArrayList是顺序结构,所以定位很快,指哪找哪。 就像电影院位置一样,有了电影票,一下就找到位置了。
LinkedList 是链表结构,就像手里的一串佛珠,要找出第99个佛珠,必须得一个一个的数过去,所以定位慢
3.HashMap
3.1键值对
HashMap储存数据的方式是—— 键值对
public class TsetCollection {
public static void main(String[] args) {
HashMap<String,String> cars = new HashMap<>();
cars.put("b1", "宝马");// (key,value)
cars.put("b2", "奔驰");
cars.put("a", "奥迪");
System.out.println(cars.get("b1"));//get(key)
}
}
2.键不能重复,值可以重复
对于HashMap而言,key是唯一的,不可以重复的。
所以,以相同的key 把不同的value插入到 Map中会导致旧元素被覆盖,只留下最后插入的元素。
不过,同一个对象可以作为值插入到map中,只要对应的key不一样
.
HashMap和Hashtable的区别
HashMap和Hashtable都实现了Map接口,都是键值对保存数据的方式
区别1:
- HashMap可以存放 null(一个)
- Hashtable不能存放null
区别2:
- HashMap不是线程安全的类
- Hashtable是线程安全的类
4.HashSet
4.1Set中的元素,不能重复
public class TsetCollection {
public static void main(String[] args) {
HashSet<String> cars = new HashSet<>();
HashSet<String> names = new HashSet<String>();
names.add("gareen");
//第二次插入同样的数据,是插不进去的,容器中只会保留一个
names.add("gareen");
System.out.println(names);
}
}
4.2没有顺序
Set中的元素,没有顺序。
严格的说,是没有按照元素的插入顺序排列
HashSet的具体顺序,既不是按照插入顺序,也不是按照hashcode的顺序。
以下是HashSet源代码中的部分注释
/**
* It makes no guarantees as to the iteration order of the set;
* in particular, it does not guarantee that the order will remain constant over time.
*/
即不保证Set的迭代顺序; 确切的说,在不同条件下,元素的顺序都有可能不一样
换句话说,同样是插入0-9到HashSet中, 在JVM的不同版本中,看到的顺序都是不一样的。 所以在开发的时候,不能依赖于某种臆测的顺序,这个顺序本身是不稳定的 。
4.3遍历
Set不提供get()来获取指定位置的元素,所以遍历需要用到迭代器,或者增强型for循环
public class TestCollection {
public static void main(String[] args) {
HashSet<Integer> numbers = new HashSet<Integer>();
for (int i = 0; i < 20; i++) {
numbers.add(i);
}
//Set不提供get方法来获取指定位置的元素
//numbers.get(0)
//遍历Set可以采用迭代器iterator
for (Iterator<Integer> iterator = numbers.iterator(); iterator.hasNext();) {
Integer i = (Integer) iterator.next();
System.out.println(i);
}
//或者采用增强型for循环
for (Integer i : numbers) {
System.out.println(i);
}
}
}
4.4HashMap和HashSet的关系
1、HashSet底层是采用HashMap实现的。HashSet 的实现比较简单,HashSet 的绝大部分方法都是通过调用 HashMap 的方法来实现的,因此 HashSet 和 HashMap 两个集合在实现本质上是相同的。
2、HashMap的key就是放进HashSet中对象,value是Object类型的。
3、当调用HashSet的add方法时,实际上是向HashMap中增加了一行(key-value对),该行的key就是向HashSet增加的那个对象,该行的value就是一个Object类型的常量 (private static final Object PRESENT = new Object();)
4.5HashSet LinkedHashSet TreeSet
HashSet: 无序
LinkedHashSet: 按照插入顺序
TreeSet: 从小到大排序
5.Collection
Collection是 Set List Queue和 Deque的接口,是一个接口类,其继承了java迭代接口Iterable。
Queue: 先进先出队列
Deque: 双向链表
注:Collection和Map之间没有关系,Collection是放一个一个对象的,Map 是放键值对的
注:Deque 继承 Queue,间接得继承了 Collection