一、集合简介
1.什么是集合
集合collection,是一个数据容器,类似于数组,但是集合是可变的,数组是不可变的,集合可随时对里面的内容进行增添和删除操作,另外集合中也提供了若干的方法来操作集合中的数组。
集合里的数据,我们称之为元素(elements);集合只能用来存储引用数据类型数据,不能存数八大基本数据类型的数据。
2.泛型的引入
Java SE 5.0以前,集合的元素只要是Object类型就行,那个时候任何对象都可以存放在集合内,但是从集合中获取对象后,需要进行正确的强制类型转换。但是,Java SE 5.0 以后,可以使用新特性”泛型”,用来指定要存放在集合中的对象类型。避免了强制转换的麻烦。
public static void main(String[] args){
ArrayList list = new ArrayList();
list.add(new Person());
list.add(5);
list.add("helloworld");
Object obj = list.get(0);
Person p = (Person)obj;
String name = p.getName();
System.out.println("name:"+name);
}
3.集合结构
在JAVA中每一个不同的集合,底层都对应着不同的数据结构。往不同的集合中存储元素,等于将数据放到了不同的数据结构中,不同的数据结构,数据存储方式不同。
3.1单列集合Collection
List中的数据元素可以重复 :ArrayList/LinkedList
Set中的元素不可重复:HashSet/TreeSet
List特点:此处的顺序并不是大小顺序,而是存入数据的先后顺序,有序是因为List集合都有下标,下表从0 开始递增。
Set:取出顺序和存入顺序不一致,Set集合没有下标
ArrayList是非线程安全的。
HashSet集合在new的时候,底层实际上new了一个hashMap集合。向HashSet集合中存储元素,实际上存储到了HashMap的key中。HashMap集合是一个Hash表数据结构。
SortedSet集合存储元素的特点:由于继承了Set集合,所以他的特点也是无序不可重复,但是放在SortedSet集合中的元素可以自动排序。放到该集合中的元素是自动按照大小顺序排序的。
TreeSet集合底层实际上是TreeMap。TreeSet集合在new的时候,底层实际上new了一个TreeMap集合。向TreeSet集合中存储元素,实际上是存储到了TreeMap的key中了。TreeMap集合是一个二叉树数据结构
3.2双列集合Map:HashMap、TreeMap
二、Collection接口
Collection接口是List,Set 和 Queue 接口的父接口,改接口定义了他们三个子接口的共同方法。即可以用于操作Set集合,也可以用于操作 List 和 Queue 集合。
1.常用方法
Collection集合的常用方法 | |||||||||
boolean add(E e) | 添加元素到集合的末尾(追加) | ||||||||
boolean remove(Object o) | 删除指定元素,成功则返回true(底层调用equles) | ||||||||
void clear() | 清空集合 | ||||||||
boolean contains(Object o) | 判断元素是否在集合中,存在返回true(底层调用equles) | ||||||||
boolean isEmpty() | 判断集合是否为空,空则返回true | ||||||||
int size() | 返回集合中的元素个数 | ||||||||
iterator() | 获取集合的迭代器 | ||||||||
toArray() | 获取集合数组 | ||||||||
addAll(Collention c) | 将一个集合添加到当前集合后面(并集) | ||||||||
removeAll(Collection c) | 获得集合的差集 | ||||||||
containsAll(Collection c) | 获取当前集合的指定元素 | ||||||||
boolean retainAll(Collection<?> c) | 仅保留此Collection里的集合 |
Collection collection = new ArrayList();
collection.add("tom");
collection.add("lucy");
collection.add("hello");
collection.add("hello");
collection.add("lisa");
collection.add("john");
//判断tom是否在集合中
System.out.println(collection.contains("tom"));//true
//删除集合中的元素hello,删除的是从前往后数的第一个匹配到的
System.out.println(collection.remove("hello"));
System.out.println(collection);
//返回集合中的元素个数
System.out.println(collection.size());
//创建一个新集合,并添加元素
Collection collection1 = new ArrayList<>();
collection1.add("lihua");
collection1.add("xiaohui");
collection1.add("tim");
//判断两个集合是否相同(比较的是集合中的元素)
System.out.println(collection.equals(collection1));
//将collection1集合中的所有元素添加到collection集合后面
System.out.println(collection.addAll(collection1));
//获取集合数组,数组元素用Arrays.toString()输出
System.out.println(Arrays.toString(collection.toArray()));
//获取集合的迭代器
System.out.println(collection.iterator());
System.out.println(collection1.iterator());
//判断集合是否为空
System.out.println(collection.isEmpty());
//判断参数集合Collection1中的元素是否都在当前集合Collection中包含
System.out.println(collection.retainAll(collection1));
//将Collection1中的元素从Collection中删除,将所有元素删除后集合为Null,
System.out.println(collection.removeAll(collection1));
System.out.println(collection);
2.集合的迭代
* 1.就是指对集合中的元素进行遍历。
* 2.几乎所有的集合子类型都实现了Iterator接口,所以可以使用迭代器来遍历集合。
* 3.Iterator接口中定义了三个方法:
* -boolean hasNext():判断是否有下一个元素,可以形象的认为有指针指向下一个元素
* -E next():获取下一个元素,返回指针指向的元素
* -void remove():删除当前元素。删除的是指针指向的元素。
*
* 注意:使用迭代器遍历集合时,不能使用集合中(自己)的方法来添加元素,否则会抛出异常。ConcurrentModificationException
2.1增强for循环
在进行集合的遍历的时候,基本都与Iterable有关。此接口提供了对集合进行迭代的方法,只有实现了Iterable接口,在能使用增强for循环进行遍历。
//使用增强for循环实现集合的遍历
for (Object ele : collection){
System.out.println(ele);
}
2.2迭代器
迭代器Iterator,是一个接口, Collection集合中有一个方法 iterator() 可以获取这个接口的实现类 对象。在这个迭代器中,维护了一个引用,指向集合中的某一个元素。默认指向一个集合前不存在的元 素,可以认为是下标为-1的元素。
迭代器的工作原理:循环调用 next() 方法进行向后的元素指向,并返回新的指向的元素。同时,在向后进行遍历的过程中,使用 hasNext() 判断是否还有下一个元素可以迭代。
在迭代器使用的过程中,需要注意:
-
不允许对集合中的元素进行修改
-
不允许对集合的长度进行修改
//获取迭代器对象,输出集合中的元素
Iterator<String> iterator = collection.iterator();
String element = null;
while (iterator.hasNext()){
//让迭代器向后指向一位,并返回
element = iterator.next();
System.out.println(element);
}
3.Queue子接口
简介:
-
队列Queue也是Collection的一个子接口,它也是常用的数据结构,可以将队列看成特殊的线性表,队列限制对线性表的访问方式:只能从一端添加(offer)元素,从另一端取出(poll)元素。
-
队列遵循先进先出(FIFO first Input First Output)的原则
-
实现类LinkedList也实现了该接口,选择此类实现Queue的原因在于Queue经常要进行添加和删除操作,而LinkedList在这方面效率比较高。
-
其主要方法如下:
方法 解析 boolean offer(E e) 作用:将一个对象添加到队尾,如果添加成功返回true E poll() 作用:从队首删除并返回这个元素 E peek() 作用:查看队首的元素
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
//add 和 offer都能添加元素
queue.add("qin");
queue.offer("a");
queue.offer("b");
queue.offer("c");
queue.offer("d");
queue.add("e");
System.out.println(queue);
String string = null;
while (!queue.isEmpty()){
//从队首删除并返回元素
string = queue.poll();
System.out.print(string);//遵循先进先出
//查看队首元素
System.out.println(queue.peek());
/*
qina
ab
bc
cd
de
enull
*/
}
System.out.println(queue);
}
Deque接口
Deque是Queue的子接口,定义了所谓的双端队列,即从队列的两端分别可以入队(offer、add)和出队(poll)。同样,LinkedList实现了该接口。
如果将Deque限制为只能一段入队和出队,则可以实现“栈”(Stack)的数据结构,对于栈而言,入栈被称为push,出栈被称为pop。遵循先进后出原则。
* 提供的方法:
* offerFirst :从头部插入元素
* offerLast :从尾部插入元素
* pollFirst :从头部移除元素
* pollLast :从尾部移除元素
public static void main(String[] args) {
Deque<String> deque = new LinkedList<>();
deque.add("q");
deque.offer("w");
deque.offer("e");
deque.offer("r");
deque.add("t");
deque.offer("y");
System.out.println(deque);
//在集合尾部插入元素
deque.offerLast("he");
System.out.println(deque);
//在集合头部插入元素
deque.offerFirst("oh");
System.out.println(deque);
//删除集合头部元素并输出
deque.poll();
System.out.println(deque);
//删除集合尾部元素并输出
deque.pollLast();
System.out.println(deque);
//从头部出栈
deque.pop();
System.out.println(deque);
}
2.List子接口
2.1List接口特点
* 1. 元素有序,并且可以重复。
* 2. 两个实现类:ArrayList和LinkedList
* - ArrayList:动态数组+各种方法
* - LinkedList: 双向链表+各种方法
* 3. 两个实现类的效率:(元素特别多的情况下)
* - 对于随机访问来说, ArrayList要高于LinkedList
* - 对于频繁的插入和删除来说, LinkedList要高于ArrayList.
*
* 4. 基于List接口,两个实现类的方法基本一致。
-
Stack:是一个古老的集合, JDK1.0版本时候出现,模拟栈结构存储数据。
-
Vector:是一个古老的集合, JDK1.0版本时候出现,现在已经被ArrayList和LinkedList替代
2.2Vector
1、底层也是一个数组。
2、初始化容量:10
3、怎么扩容的?
扩容之后是原容量的2倍。
10--> 20 --> 40 --> 80
4、Vector中所有的方法都是线程同步的,都带有synchronized关键字,
是线程安全的。效率比较低,使用较少了。
5、怎么将一个线程不安全的ArrayList集合转换成线程安全的呢?
使用集合工具类:
java.util.Collections;
java.util.Collection 是集合接口。
java.util.Collections 是集合工具类。
Collections.synchronizedList();//将及格转换为线程安全的。
2.3常用方法
LinkedList\ArrayList构造和添加方法 | |||||||||
public ArrayList<E>() | 创造一个空集合 | ||||||||
public boolean add(E e) | 将指定的参数元素追加到集合的末尾 | ||||||||
public void add(int index ,E e) | 在集合的指定位置添加元素(插入元素) | ||||||||
public void addAll(E object) | 将指定集合中的所有元素添加到当前集合中 | ||||||||
piblic addAll(int index ,Collection c) | 将集合中的所有元素添加到当前集合的指定位置 | ||||||||
LinkedList\ArrayList集合常用方法 | |||||||||
public boolean remove(Object o) | 删除指定元素,成功则返回true | ||||||||
public E remove(int index) | 删除指定索引位置上的元素,返回被删除的元素 | ||||||||
public set(int index,E e) | 修改指定位置上的元素,返回修改前的元素 | ||||||||
public E get(int index) | 获取指定索引位置上的元素 | ||||||||
public int size() | 获取集合中元素个数 | ||||||||
int indexOf(Object obj) | 返回指定索引元素第一次出现的位置,没有返回-1 | ||||||||
int lastIndexOf(Object obj) | 返回指定索引元素最后一次出现的位置,没有返回-1 | ||||||||
List subList(int fromIndex,int toIndex) | 截取元素,返回指定区间内的元素,[fromIndex,toIndex) |
public static void main(String[] args) {
List<String> list = new ArrayList<>();
//添加元素
list.add("w");
list.add("e");
list.add("l");
list.add("c");
list.add("o");
list.add("m");
list.add("e");
System.out.println(list);
List<String> list1 = new ArrayList<>();
list1.add("来");
list1.add("到");
list1.add("中国");
System.out.println(list1);
//将list1中的元素添加到list中
list.addAll(list1);
System.out.println(list);
//删除元素-按下标删除
list1.remove(1);
System.out.println(list1);
//删除元素-按元素删除,删除的是从头到尾查询到的第一个元素
//要想全部删除可以遍历集合
list.remove("e");
System.out.println(list);
//修改元素,修改之前的元素可以返回
System.out.println(list.set(1, "o"));
System.out.println(list);
//获取元素
System.out.println(list1.get(0));
//截取元素
System.out.println(list.subList(1,5));
//返回指定索引第一次出现的位置
System.out.println(list.indexOf("o"));
//返回指定索引最后一次出现的位置
System.out.println(list.lastIndexOf("o"));
}
2.4 ListIterator
ListIterator是Iterator接口的子接口,继承到了Iterator中的所有方法,同时自己也添加了若干个方法。允许使用ListIterator在进行元素迭代的时候,对集合中的数据进行修改,或者对集合的长度进行修改。同时,使用ListIterator还可以进行倒序的迭代。
注意:
在进行迭代的过程中,允许修改集合。但是要注意的是,这个修改集合,只能通过接口中的方法进行,并不能通过集合中的方法进行修改。
// 3、在迭代的过程中,修改集合
while (iterator.hasNext()) {
// 向后指向一位,并返回当前指向的元素
String ele = iterator.next();
if (ele.equals("Vertu")) {
// 在迭代的过程中,删除这个元素
iterator.remove();
// 在迭代的过程中,添加一个元素(加在当前迭代的这个元素的后面)
iterator.add("HTC");
// 在迭代的过程中,对当前迭代的元素进行修改
iterator.set("Nokia");
}
}
3.Set子接口
3.1简介
1.Set集合中的元素是无序的(取出顺序与存入顺序无关)
2.Set集合中的元素不能重复
3.2实现类
1).HashSet
- HashSet是Set接口的典型实现,大多数时候使用Set集合都会使用这个类。
- HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取和查找性能。
HashSet 具有以下特点:
- 不能保证元素的排列顺序
- HashSet不是线程安全的
- 集合元素可以是null
当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值决定该对象在 HashSet 中的存储位置。
如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功。
2).LinkedHashSet
- LinkedHashSet是HashSet的子类
- LinkedHashSet集合根据元素的HashCode值来决定元素的存储位置,旦它同时使用链表维护元素的次序,这使得元素看起来是插入顺序保存的。
- LinkedHashSet性能插入性能略低于HashSet,但在迭代访问Set 里的全部元素有很好的性能
- LinkedHashSet 不允许集合元素重复
3).TreeSet
TreeSet是SortedSet 接口的实现类,TreeSet集合是用来对元素进行排序的,同样也可以保证元素的唯一性。TreeSet可以确保集合元素处于排序状态。
TreeSet支持两种排序方法 : 自然排序和定制排序。默认情况,采用自然排序。
4.List排序
4.1Comparable接口
如果集合里的元素想要排序,那么元素对象之间一定要有大小之分。
如果界定大小???
此时元素必须要是comparable 接口的实现类,该接口提供了方法
int comparaTo(T t). 规范了其子集是可以进行比较的,因此子类必须重写该抽象方法
比较规则:(默认升序)
如当前对象大于给定对象,那么返回值应为>0的整数
若小于给定对象,那么返回值应为<0的整数
若两个对象相等,则应返回0
4.2 工具类提供的排序方法
Collections是集合的工具类,其中排序的方法有sort方法。
- static void sort(List<T> list) : 对指定集合的元素进行自然排序。前提是元素类型必须实现Comparable接口
4.3 Comparator比较器接口
java类一旦定义好,就不要轻易再去修改它。因此当java类实现了Comparable接口,也就代表比较规则已经确定.
但是,有的时候我们想临时改变一下比较规则,怎么办呢?
此时我们可以采用Comparator接口回调的方式。它提供了一个抽象方法:
- int compare(T o1 , T o2)
比较规则如下
若o1>02, 则返回值应该>0
若o1<o2,则返回值应该<0
若o1==o2, 则返回值应该为0
工具类中提供了sort方法的重载方法
static void sort(List<T> list , Comparator c)
作用:使用比较器c,指定临时排序规则
4.4 Collections
Collections 是一个操作 Set、List 和 Map 等集合的工具类,提供了大量方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法
Collections工具类提供的方法 | |||||||||
排序操作 | |||||||||
reverse(List) | 反转List中元素的顺序 | ||||||||
shuffle(List) | 对List集合元素进行随机排序 | ||||||||
sort(List,Comparator) | 根据指定的Comparator产生顺序对List集合元素排序 | ||||||||
sort(List) | 根据元素的自然顺序对指定几何元素按升序排序 | ||||||||
swap(List ,i ,j) | 将指定List集合中的 i 出元素和 j 处元素进行交换 | ||||||||
查找替换 | |||||||||
Object max(Collection) | 根据元素的自然顺序,返回给定集合中的最大元素 | ||||||||
Object max(Collection,Comparator) | 根据comparator指定的顺序,返回集合中的最大元素 | ||||||||
Object min(Collection) | |||||||||
Object max(Collection,Comparator) | |||||||||
int frequency(Collection,Object) | 返回指定集合中指定元素的出现次数 | ||||||||
boolean replaceAll(List list,Object old,Object new) | 替换list中的元素,new 换 old |
public static void main(String[] args) {
List<String> list = new LinkedList<>();
list.add("a");
list.add("e");
list.add("r");
list.add("t");
list.add("d");
list.add("y");
list.add("k");
list.add("m");
System.out.println(list);
//对集合中的元素进行排序
// 使用匿名内部类创建 Comparator, 重写比较器
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s2.compareTo(s1);
}
});
System.out.println("排序后: " + list);
//对集合进行随机排序
Collections.shuffle(list);
System.out.println("随机排序后: " + list);
//对集合进行排序
Collections.sort(list);
System.out.println("排序后: " + list);
//反转List中的元素的顺序
Collections.reverse(list);
System.out.println("反转后: " + list);
//对集合中的指定元素进行交换
Collections.swap(list,1,4);
System.out.println("交换后: " + list);
//返回集合中最大的元素
System.out.println("最大的为:" + Collections.max(list));
//返回集合中最小的元素
System.out.println("最小的为:" + Collections.min(list));
//替换集合中的元素
Collections.replaceAll(list,"a","A");
System.out.println("替换后: " + list);
三、Map接口
1.Map简介
Map是集合框架中的另一个父接口,它用来保存具有映射(一对一)关系的数据,这样的数据称之为键值对(Key-Value-Pair)。key可以看成是value的索引。特点如下:
key和value必须是引用数据类型
作为key,在Map集合中不允许重复
key可以为null
key和value之间存在一对一关系,通过指定的key总能找到唯一确定的value
根据内部数据结构的不同,Map接口有多种实现类,其中常用的有内部为hash表实现的
HashMap和内部为排序二叉树实现的TreeMap 。
2.常用方法
Map的基本方法 | |||||||||
V put(K key,V value) | 设置键值对 | ||||||||
V remove(Object key) | 删除元素 | ||||||||
void clear() | 清空集合 | ||||||||
boolean containsKey(Object key) | 判断键是否存在,存在返回true | ||||||||
boolean containsValue(Object value) | 判断值是否存在,存在返回true | ||||||||
boolean isEmpty() | 判断简直关系是否为空,空则返回true | ||||||||
boolean equals(Object o) | 比较指定的对象与此映射是否相等 | ||||||||
int hashCode() | 返回此映射的哈希值 | ||||||||
int size() | 返回映射中的键-值映射关系数 | ||||||||
putIfAbsent("age","30") | 当键值对中不存在此关系时才会增加,否则没有任何效果 | ||||||||
replace() | 修改键值对中的值 | ||||||||
enterSet() | 返回此映射中包含的键-值对的 Set 视图 |
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
//设置键值对,添加元素
map.put("name", "tom");
map.put("age", "18");
map.put("gender", "male");
System.out.println(map);
//删除元素,当键存在时,返回true,错误返回false
System.out.println(map.remove("name", "lucy"));
//返回此映射的哈希值,哈希值用于存储和查询元素
System.out.println(map.hashCode());
// System.out.println(map);
//返回键值对的数量
System.out.println(map.size());
//判断键是否存在
System.out.println(map.containsKey("age"));
//判断值是否存在
System.out.println(map.containsValue("18"));
//判断键值对关系是否为空
System.out.println(map.isEmpty());
//清空映射关系
map.clear();
System.out.println(map);
3.Map的遍历
3.1 遍历所有的key
- Set<K> keySet();
- 该方法会将当前Map中的所有key存入一个Set集合后返回
3.2 遍历所有的key-value
- Set<Entry<K,V>> entrySet()
- 该方法会将当前Map中的每一组key-value封装成Entry对象存入Set集合后返回
3.3 遍历所有的value(不常用)
- Collection<V> values
4.HashMap的实现原理
4.1原理
HashMap的底层主要是基于数组和链表来实现的,他之所以有相当快的查询速度,主要是因为它是通过计算散列码来决定存储位置。HashMap中主要是通过key的HashCode来计算Hash值,只要HashCode相同,计算出来的Hash值就是一样的。如果存储的对象太多,就有可能不同的对象算出来的Hash值是相同的,这就是所谓的Hash冲突。
图中,紫色部分即代表哈希表,也称为哈希数组,数组的每个元素都是一个单链表的头节点,链表是用来解决冲突的,如果不同的key映射到了数组的同一位置处,就将其放入单链表中
4.2装载因子及其HashMap优化
capacity:容量,hash表里bucket(桶)的数量,也就是散列数组大小
initial capacity:初始容量,创建hash表时,初始bucket的数量,默认构建容量为16,也可以使用特定容量。
size:大小,当前散列表中存储数据的数量
load factor:加载银子,默认值0.75也就是75%,当向散列表增加数据时,如果size/capacity的值大于loadfactor,则发生扩容并且重新散列(rebash)
性能优化:加载因子较小时,散列查找性能会提高,但是也浪费了散列桶空间容量。0.75是性能和空间相对平衡结果。在创建散列表时指定合理容量,减少rehash能提供性能
5. HashMap 与 HashTable
HashMap特点:
1、无序,不可重复。
2、放在HashMap集合key部分的元素其实就是放到HashSet集合中了。
所以HashSet集合中的元素也需要同时重写hashCode()+equals()方法。
3、HashMap集合的默认初始化容量是16,默认加载因子是0.75
这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组以二叉树开始扩容。
HashMap 和 Hashtable 是 Map 接口的两个典型实现类
区别:
Hashtable 是一个古老的 Map 实现类,不建议使用
Hashtable 是一个线程安全的 Map 实现,但 HashMap 是线程不安全的。
Hashtable 不允许使用 null 作为 key 和 value,而 HashMap 可以
与 HashSet 集合不能保证元素的顺序的顺序一样,Hashtable 、HashMap 也不能保证其中 key-value 对的顺序
6.LinkedHashMap
LinkedHashMap 是 HashMap 的子类
LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key-Value 对的插入顺序一致
7.TreeMap
TreeMap 存储 Key-Value对时,需要根据 Key 对 key-value 对进行排序。TreeMap 可以保证所有的 Key-Value 对处于有序状态。
TreeMap 的 Key 的排序:
自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException
定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口
8.Properties
Properties 类是 Hashtable 的子类,该对象用于处理属性文件
由于属性文件里的 key、value 都是字符串类型,所以 properties 里的 Key 和 Value 都是字符串类型的