一:Java集合产生的原因
一方面,面向对 象语言对事物的体现 都是以对象的形式,为了方便对多 个对象的操作,就要对对象进行存储。 另-方面,使用Array存储对 象方面具有一些弊端,大小不能动态改变,而Java 集合就像一 种容器,可以动态地把多 个对象的引用放入容器中。所以,Java集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组。
二:集合架构体系图
●Java集合可分为Collection和Map两种体系
➢Collection接口:
➢Set:元素无序、不可重复的集合--类似高中的"集合”
➢List:元素有序,可重复的集合--”动态”数组
➢Map接口:具有映射关系“key-value对” 的集合--类似于高中的“函数”y=f(x) (x1,y1) (x2,y2)
三:Collection接口
●Collection接口是List、Set 和Queue接口的父接口,该接口里定义的方法既可用于操作Set集合,也可用于操作List和Queue集合。
3.1:List接口
Java中数组用来存储数据的局限性,List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引。List容器中的元素都对应一个整数型的序号记载其在容器中的位置,
可以根据序号存取容器中的元素。JDK API中List接口的实现类常用的有: ArrayList、 LinkedList和Vector。
list集合中的元素或对象(主要是自己的实现类)要重写equals方法,因为equals方法是判断两个对象相等的条件,不重写可能导致相等的对象无法判断相等。
1 package Set;
2 import org.junit.Test;
3
4 import java.util.*;
5
6 /*
7 * 1.存储对象可以考虑:①数组 ②集合
8 * 2.数组存储对象的特点:Student[] stu = new Student[20]; stu[0] = new Student();....
9 * >弊端:①一旦创建,其长度不可变。②真实的数组存放的对象的个数是不可知。
10 * 3.集合
11 * Collection接口
12 * |------List接口:存储有序的,可以重复的元素
13 * |------ArrayList(主要的实现类)、LinkedList(对于频繁的插入、删除操作)、Vector(古老的实现类、线程安全的)
14 * |------Set接口:存储无序的,不可重复的元素
15 * |------HashSet、LinkedHashSet、TreeSet
16 * Map接口:存储“键-值”对的数据
17 * |-----HashMap、LinkedHashMap、TreeMap、Hashtable(子类:Properties)
18 */
19 public class CollectionTest {
20 @Test
21 public void testCollection1() {
22 //size()方法判断集合的大小,add(Object obj)用来向集合添加元素,addAll(Collection coll),将一个集合中的所有元素添加到另一个集合里面
23 // isEmpty()用来判断集合是否为空,clear()用来清空集合里的元素,contains用来判断集合是否包含指定的obj元素,对于自定义类对象要重写equals作为判断的依据
24 //containsAll(Collection coll):判断当前集合中是否包含coll中所有的元素,retainAll(Collection coll):求当前集合与coll的共有的元素,返回给当前集合
25 //equals判断两个集合中元素是否相同,toArray()变成数组,Arrays.toList()数组变成集合,集合可以通过.iterator()来进行遍历元素,包含next和hasnext方法,也可以增强for循环
26 Collection coll = new ArrayList();
27 coll.add(1);
28 coll.add("hello");
29 coll.add(new Person("nihao",12));//添加元素
30 System.out.println(coll.size());//判断大小,
31 coll.add(1);
32 System.out.println(coll.size());//重复的元素可以添加
33 ArrayList arrayList = new ArrayList();
34 arrayList.add(10);
35 coll.addAll(arrayList);//添加一个集合
36 System.out.println(coll.isEmpty());//判断是否为空
37 boolean b = (boolean) arrayList.remove(Integer.valueOf(10));//删除元素10,注意数字是表示按照下标进行删除,包装类则是安装元素进行删除
38 System.out.println(b);
39 System.out.println(arrayList.isEmpty());//现在为空
40 //coll.clear();//清空元素,
41 //System.out.println(coll);
42 System.out.println(coll.contains(Integer.valueOf(1)));//包含一个元素
43 arrayList.add(1);
44 System.out.println(coll.containsAll(arrayList));//包含一个集合
45 System.out.println(coll.retainAll(arrayList));//求交集
46 System.out.println(coll.equals(arrayList));//判断两个集合是否相同
47 System.out.println(arrayList.hashCode());//集合的hashcode值
48 arrayList.add(2);
49 System.out.println(arrayList.hashCode());//hashcode是对所有元素计算的到的。
50 Object []obj = coll.toArray();//集合变成数组
51 for (int i = 0; i < obj.length; i++) {
52 System.out.println(obj[i]);
53 }
54 int[] a = {1,2,3};
55 List<int[]> ints = Arrays.asList(a);//数组变成集合
56 Iterator iterator = ints.iterator();
57 while (iterator.hasNext()){
58 System.out.println(iterator.next());
59 }
60 for (Object ob:coll){
61 System.out.println(ob);
62 }
63 }
64 //ArrayList:List的主要实现类
65 /*
66 * List中相对于Collection,新增加的方法
67 * void add(int index, Object ele):在指定的索引位置index添加元素ele
68 boolean addAll(int index, Collection eles)
69 Object get(int index):获取指定索引的元素
70 Object remove(int index):删除指定索引位置的元素
71 Object set(int index, Object ele):设置指定索引位置的元素为ele
72 int indexOf(Object obj):返回obj在集合中首次出现的位置。没有的话,返回-1
73 int lastIndexOf(Object obj):返回obj在集合中最后一次出现的位置.没有的话,返回-1
74 List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex结束的左闭右开一个子list
75
76 List常用的方法:增(add(Object obj)) 删(remove) 改(set(int index,Object obj))
77 查(get(int index)) 插(add(int index, Object ele)) 长度(size())
78 */
79 @Test
80 public void testArrayList(){
81 ArrayList ints = new ArrayList();
82 ints.add(1);
83 ints.add("a");
84 Collection arrayList = new ArrayList();
85 arrayList.add("a");
86 arrayList.add(1);
87 System.out.println(arrayList.equals(ints));//判断相等不仅看元素,还要看顺序,
88 System.out.println(arrayList.hashCode());//一般hashcode除了要看内容,还要看顺序,因此若hashcode不相等,equals一般也不想等,互等价
89 System.out.println(ints.hashCode());
90 ints.addAll(0,arrayList);
91 System.out.println(ints);
92 System.out.println(ints.get(0));
93 System.out.println(ints.remove(0));//删除指定位置的元素
94 System.out.println(ints.indexOf("a"));//首次出现的位置,
95 System.out.println(ints.lastIndexOf("a"));//最后一次出现的位置
96 System.out.println(ints.subList(0,2));//返回左闭右开的区间的内容
97 System.out.println(ints.set(0,"b"));//指定位置修改
98 System.out.println(ints);
99
100 }
101
102 }
3.2 set接口
set集合中的元素或对象(主要是自己的实现类)要重写equals方法,因为equals方法是判断两个对象相等的条件,不重写可能导致相等的对象无法判断相等,
还要重写hashcode方法,否则会导致所有的对象的hashcode的值都相等,且只能输出一个对象的值。
linkedhashset要维护指针,所以插入的效率要低一些,但是频繁遍历的效果要好于hashset
要求是同一个类的才能添加到同一个treeset,才能按照统一的标准进行遍历。
1 package Set;
2
3 import org.junit.Test;
4
5 import java.util.*;
6
7 public class Set {
8 @Test
9 public void test(){//测试hashset
10 HashSet hashSet = new HashSet();
11 hashSet.add(123);
12 hashSet.add(222);
13 hashSet.add("BB");
14 System.out.println(hashSet);//存储无顺序,输出也无顺序。
15 work work1 = new work("ll",10);
16 work work2 = new work("ll",10);
17 hashSet.add(work1);
18 hashSet.add(work2);
19 System.out.println(work1.hashCode());
20 System.out.println(work2.hashCode());
21 System.out.println(hashSet);//对类对象,只有重写hashCode方法和equals方法后,生成的对象的hash值才可以相同,才能保证set集合的无重复性
22 }
23 /*
24 * LinkedHashSet:使用链表维护了一个添加进集合中的顺序。导致当我们遍历LinkedHashSet集合
25 * 元素时,是按照添加进去的顺序遍历的!
26 *
27 * LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。
28 */
29 @Test
30 public void test2(){//测试LinkedHashSet()
31 LinkedHashSet linkedHashSet = new LinkedHashSet();
32 linkedHashSet.add(1);
33 linkedHashSet.add(2);
34 linkedHashSet.add(null);
35 Iterator iterator1 = linkedHashSet.iterator();
36 while (iterator1.hasNext())//因为测试LinkedHashSet在hashset的基础上添加了链表,因此所有添加的元素连接起来,遍历有序,这是主要的不同
37 {
38 System.out.println(iterator1.next());
39 }
40
41 }
42 @Test//自然排序
43 public void test3(){//在people类里面重写
44 TreeSet set = new TreeSet();
45 set.add(new work("liulei",16));
46 set.add(new work("ww",12));
47 set.add(new work("cc",13));
48 for (Object str1:set){
49 System.out.println(str1);
50 }
51
52 }
53 @Test
54 public void test4(){//因为只有相同类的两个实例才会比较大小,所以向 TreeSet 中添加的应该是同一个类的对象
55
56 Comparator com = new Comparator() {//定制排序
57 @Override
58 public int compare(Object o1, Object o2) {
59 if(o1 instanceof work && o2 instanceof work){
60 work c1 = (work) o1;
61 work c2 = (work) o2;
62 int i = c1.getAge().compareTo(c2.getAge());//小于负值大于正值
63 if(i==0){//相等,继续比较
64 return c1.getName().compareTo(c2.getName());
65 }
66 return i;
67 }
68 return 0;
69 }
70 };
71 TreeSet set = new TreeSet();
72 set.add(new work("liulei",16));
73 set.add(new work("ww",12));
74 set.add(new work("cc",13));
75 for (Object str1:set){
76 System.out.println(str1);
77 }
78
79 }
80
81 }
对于自然排序,people里面实验Comparable,重写compareTo方法
package Set;
public class Person implements Comparable{
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Person() {
super();
}
public Person(String name, Integer age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
//static int init = 1000;
@Override
public int hashCode() {//return age.hashCode() + name.hashCode();û�����Ľ�׳�Ժá�
final int prime = 31;
int result = 1;
result = prime * result + ((age == null) ? 0 : age.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
//return init++;//����������
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age == null) {
if (other.age != null)
return false;
} else if (!age.equals(other.age))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
//����TreeSet�����Person��Ķ���ʱ�����ݴ˷�����ȷ�������ĸ��������С�
@Override
public int compareTo(Object o) {
if(o instanceof Person){
Person p = (Person)o;
//return this.name.compareTo(p.name);
//return -this.age.compareTo(p.age);
int i = this.age.compareTo(p.age);
if(i == 0){
return this.name.compareTo(p.name);
}else{
return i;
}
}
return 0;
}
}
View Code
自然排序
1 @Override
2 public int compareTo(Object o) {
3 if(o instanceof Person){
4 Person p = (Person)o;
5 //return this.name.compareTo(p.name);
6 //return -this.age.compareTo(p.age);
7 int i = this.age.compareTo(p.age);
8 if(i == 0){
9 return this.name.compareTo(p.name);
10 }else{
11 return i;
12 }
13 }
14 return 0;
15 }
定制排序
Comparator com = new Comparator() {//定制排序
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof work && o2 instanceof work){
work c1 = (work) o1;
work c2 = (work) o2;
int i = c1.getAge().compareTo(c2.getAge());//小于负值大于正值
if(i==0){//相等,继续比较
return c1.getName().compareTo(c2.getName());
}
return i;
}
return 0;
}
};
3.3.Queue
Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了Queue接 口。Queue接口窄化了对LinkedList的方法的访问权限(即在方法中的参数类型如果是Queue时,就完全只能访问Queue接口所定义的方法 了,而不能直接访问 LinkedList的非Queue的方法),以使得只有恰当的方法才可以使用。BlockingQueue 继承了Queue接口。队列是一种先进先出的数据结构,元素在队列末尾添加,在队列头部删除。Queue接口扩展自Collection,并提供插入、提取、检验等操作。上图中,方法offer表示向队列添加一个元素,poll()与remove()方法都是移除队列头部的元素,两者的区别在于如果队列为空,那么poll()返回的是null,而remove()会抛出一个异常。方法element()与peek()主要是获取头部元素,不删除。接口Deque,是一个扩展自Queue的双端队列,它支持在两端插入和删除元素,因为LinkedList类实现了Deque接口,所以通常我们可以使用LinkedList来创建一个队列。PriorityQueue类实现了一个优先队列,优先队列中元素被赋予优先级,拥有高优先级的先被删除。
如图所示,在并发队列上,JDK提供了2套实现,一个是以ConcurrentLinkedQueue为代表的高性能非阻塞队列,一个是以BlockingQueue接口为代表的阻塞队列,无论哪种都继承自Queue。使用阻塞算法的队列可以用一个锁(入队和出队用同一把锁)或两个锁(入队和出队用不同的锁)等方式来实现,而非阻塞的实现方式则可以使用循环CAS的方式来实现,下面我们来一一分析。
ConcurrentLinkedQueue
一个适用于高并发场景下的队列,通过无锁的方式(CAS+volatile),实现了高并发下的高性能,通常ConcurrentLinkedQueue的性能好于BlockingQueue。
它是一个基于链接节点的×××线程安全队列,遵循先进先出的原则,头是最先加入的,尾是最近加入的,不允许加入null元素。
注意add()/offer()都是加入元素的方法,这里没有区别;poll()/peek()是取出头元素的方法,区别点在于poll会删除元素,而peek不会。
要特别注意到由于它的非阻塞性,并不像其他普通集合那样,获取队列的SIZE的方法并不是常量时间的花费,而是O(N)的,因此我们应该尽可能避免使用size()方法,可以考虑使用isEmpty()代替。
虽然使用到了CAS+VOLATILE的机制避免了锁,但是我们要明白的是,这只是保证单个操作,如peek()的安全,但是多个操作如果想保证的话,需要使用锁机制来达到同步的效果。
BlockingQueue API
入队:
offer(E e):如果队列没满,立即返回true; 如果队列满了,立即返回false-->不阻塞
put(E e):如果队列满了,一直阻塞,直到数组不满了或者线程被中断-->阻塞
offer(E e, long timeout, TimeUnit unit):在队尾插入一个元素,,如果数组已满,则进入等待,直到等待时间超时
出队:
poll():非阻塞拿数据,立即返回
take():阻塞拿数据
poll(long timeout, TimeUnit unit):带有一定超时时间的poll拿取数据
ArrayBlockingQueue
基于数组的阻塞队列实现,在其内部维护了一个定长数组,以便缓存队列中的数据对象,由于ArrayBlockingQueue内部只有一个锁对象(ReentrantLock),因此读写没有实现分离,也就意味着生产消费不能完全并行。由于长度需要定义,因此也叫有界队列。
LinkedBlockingQueue
基于链表的阻塞队列实现,同ArrayBlockingQueue类似,其内部也维持着一个数据缓冲队列(链表构成)。
LinkedBlockingQueue之所以较ArrayBlockingQueue更加高效的处理并发数据,是因为内部实现采用了2把锁,也就是实现了入队、出队分别上锁,即读写分离,从而生产者、消费者完全到达了并行。
无需定义长度,也叫×××队列。当然不定义长度时,需要注意下生产者的速度和消费者的速度,因为默认情况下队列长度是Integer.MAX_VALUE。
SynchronousQueue
一个没有缓冲的队列,生产者生产的数据会直接被消费者获取到并消费。它是一个轻量级的阻塞队列,因为不具备容量,在用法上,只能是一个线程阻塞着取元素,等待另一个线程往队列里面放入一个元素,然后会被等待着的线程立即取走,其实就是实现了线程间的轻量级的单元素交换。SynchronousQueue直接使用CAS实现线程的安全访问。
PriorityBlockingQueue
基于优先级的阻塞队列(优先级的判断通过构造函数传入的Compator对象决定,也就是传入队列中对象必须实现Comparable接口)。
在实现PriorityBlockingQueue时,内部控制线程同步的锁采用的是公平锁,也是一个×××的队列。
通俗的来说,不是先进先出的队列了,而是谁的优先级低谁先出去。那么可以思考下,是否每次add/offer都会进行一次排序呢?我们是否需要按照优先级进行全排序呢?实际上,可以大致看一看add/take方法,会了解到PriorityBlockingQueue的设计思想:在add时,并不进行排序处理,当进行take时,选择优先级最小的拿出来而已,这样既避免了在add时花费时间排序,又在take时节省了时间,因为并没有全排序,仅仅是挑选了一个优先级低的元素而已。
DelayQueue
带有延迟时间的Queue,其中的元素只有当指定的延迟时间到了,才能从队列中获取到该元素。队列中的元素必须实现Delayed接口,没有大小限制。本质上来说,是借助于PriorityBlockingQueue来实现的,以延迟时间作为优先级。延迟队列的应用场景很多,比如缓存超时的数据进行移除,任务超时处理,空闲连接的关闭等等。
四:Map接口
3.1 hashmap
3.2 Linkedhashmap
LinkedHashMap的概述: Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序LinkedHashMap的特点: 底层的数据结构是链表和哈希表 元素有序 并且唯一
元素的有序性由链表数据结构保证 唯一性由 哈希表数据结构保证
Map集合的数据结构只和键有关
3.3 Treemap
TreeMap 键不允许插入null
TreeMap: 键的数据结构是红黑树,可保证键的排序和唯一性
排序分为自然排序和比较器排序
线程是不安全的效率比较高
TreeMap集合排序:
实现Comparable接口,重写CompareTo方法
使用比较器
3.4 Hashtable
3.5 properties
1 @Test
2 public void Test5() throws IOException {
3 Properties pros = new Properties();
4 pros.load(new FileInputStream(new File("D:/config.txt")));
5 String user = pros.getProperty("user");
6 System.out.println(user);
7 }
五:Collections工具类
1 package com.atguigu.java;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collections;
6 import java.util.List;
7
8 import org.junit.Test;
9
10 /*
11 * 操作Collection以及Map的工具类:Collections
12 *
13 * 面试题:区分Collection与Collections
14 *
15 */
16 public class TestCollections {
17 /*
18 * Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
19 Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
20 Object min(Collection)
21 Object min(Collection,Comparator)
22 int frequency(Collection,Object):返回指定集合中指定元素的出现次数
23 void copy(List dest,List src):将src中的内容复制到dest中
24 boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值
25
26 */
27 @Test
28 public void testCollections2(){
29 List list = new ArrayList();
30 list.add(123);
31 list.add(456);
32 list.add(12);
33 list.add(78);
34 list.add(456);
35 Object obj = Collections.max(list);
36 System.out.println(obj);
37 int count = Collections.frequency(list, 4567);
38 System.out.println(count);
39 //实现List的复制
40 //List list1 = new ArrayList();//错误的实现方式
41 List list1 = Arrays.asList(new Object[list.size()]);
42 Collections.copy(list1, list);
43 System.out.println(list1);
44 //通过如下的方法保证list的线程安全性。
45 List list2 = Collections.synchronizedList(list);
46 System.out.println(list2);
47 }
48 /*
49 * reverse(List):反转 List 中元素的顺序
50 shuffle(List):对 List 集合元素进行随机排序
51 sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
52 sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
53 swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
54
55 */
56 @Test
57 public void testCollections1(){
58 List list = new ArrayList();
59 list.add(123);
60 list.add(456);
61 list.add(12);
62 list.add(78);
63 System.out.println(list);
64 Collections.reverse(list);
65 System.out.println(list);
66 Collections.shuffle(list);
67 System.out.println(list);
68 Collections.sort(list);
69 System.out.println(list);
70 Collections.swap(list, 0, 2);
71 System.out.println(list);
72 }
73 }
作者:你的雷哥
本文版权归作者所有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。