Java中的集合
集合框架的概述
1、集合、数组都是对多个数据进行存储操作的结构,简称Java容器
说明:此时的存储,只要指的是内存层面的存储,不涉及到持久化的存储
2、数组在存储多个数据方面的特点:
- 一旦初始化以后,其长度就确定了
- 数组一旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据。比如:String[] arr, int[] arr1;
2.2、数组在存储多个数据方面的缺点:
- 一旦初始化,长度就不可修改
- 数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高
- 获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用
- 数组存储数据的特点:有序,可重复。对于无需,不可重复的需求,不能满足。
Java集合可分为Collection和Map的两个体系
Collection接口:单列数据,定义了存取一组对象的方法的集合
- List:元素有序、可重复的集合
- Set:元素无序、不可重复的集合
Map接口:双列数据,保存具有映射关系“key-value对”的集合
Collection中常用的方法(配合代码理解)
package com.haust.java1;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
public class CollectionTest {
@Test
public void test1(){
Collection collection = new ArrayList();
//add(Object e):将元素e添加到集合collection中
collection.add("AA");
collection.add("BB");
collection.add(123);
collection.add(new Date());
//size():获取添加的元素的个数
System.out.println(collection.size());//4
//addAll():将集合collection1集合中的元素添加到collection集合中
Collection collection1 = new ArrayList();
collection1.add("AA");
collection1.add("BB");
collection1.add(123);
collection1.add(new Date());
collection.addAll(collection1);
System.out.println(collection);
//isEmpty():判断当前集合是否为空
System.out.println(collection.isEmpty());//false
//clear():清空集合元素
collection.clear();
System.out.println(collection.isEmpty());//true
}
}
package com.haust.java;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/*Collection接口中声明的方法的测试
* 向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()
* */
public class CollectionTest {
@Test
public void test1(){
Collection collection = new ArrayList();
collection.add(123);
collection.add(456);
collection.add(new String("Tom"));
collection.add(false);
collection.add(new Person("Jerry",13));
//contains(Object obj):判断当前集合中是否包含obj,在判断时会调用obj对象所在类的equals()。
boolean contains = collection.contains(123);
System.out.println(contains);
System.out.println(collection.contains(new String("Tom")));
boolean contains1 = collection.contains(new Person("Jerry", 13));
System.out.println(contains1);
//containsAll(Collection coll1):判断形参coll1中的所有元素是否都在当前集合中
Collection collection1 = Arrays.asList(123);
boolean containsAll = collection.containsAll(collection1);
System.out.println(containsAll);
}
@Test
public void test2(){
//remove(Object obj)
Collection collection = new ArrayList();
collection.add(123);
collection.add(456);
collection.add(new String("Tom"));
collection.add(false);
collection.add(new Person("Jerry",13));
collection.remove(123);
System.out.println(collection);
collection.remove(new Person("Jerry",13));
System.out.println(collection);
//removeAll(Collection coll1):从当前集合中移除coll1中所有的元素
Collection collection1 = Arrays.asList(123,456);
collection.removeAll(collection1);
System.out.println(collection);
}
@Test
public void test3(){
Collection collection = new ArrayList();
collection.add(123);
collection.add(456);
collection.add(new String("Tom"));
collection.add(false);
collection.add(new Person("Jerry",13));
//retainAll(Collection collection1)求交集获取当前集合和coll1集合的交集,并返回给当前集合
Collection collection1 = Arrays.asList(123,456,789);
collection.retainAll(collection1);
System.out.println(collection);
// equals(Object obj):要想返回true,需要当前集合和形参集合的元素都相同
Collection collection2 = new ArrayList();
collection2.add(123);
collection2.add(456);
boolean equals = collection.equals(collection2);
System.out.println(equals);
}
@Test
public void test4(){
Collection collection = new ArrayList();
collection.add(123);
collection.add(456);
collection.add(new String("Tom"));
collection.add(false);
collection.add(new Person("Jerry",13));
//hashCode():返回当前对象的哈希值
System.out.println(collection.hashCode());
//toArray():集合转化为数组
Object[] arr = collection.toArray();
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
//拓展:数组--》集合:调用Arrays类的静态方法asList()
List<String> list = Arrays.asList(new String[]{"AA","BB"});
System.out.println(list);
}
}
Iterator迭代器遍历Collection集合
package com.haust.java;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/*集合中的遍历操作,使用迭代器Iterator接口
* 内部方法:hasNext()和next()方法搭配使用*/
public class IteratorTest {
@Test
public void test1(){
Collection collection = new ArrayList();
collection.add(123);
collection.add(456);
collection.add(new String("Tom"));
collection.add(false);
collection.add(new Person("Jerry",13));
Iterator iterator = collection.iterator();
// 方式一:
// for (int i = 0; i < collection.size(); i++) {
// System.out.println(iterator.next());
// }
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
Collection子接口之一:List接口
List集合中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引。
面试题:ArrayList、LinkedList、Vector三者的异同
同:
- 三个类都是实现了List接口,存储数据的特点相同,存储有序的、可重复的数据
不同:
- ArrayList:作为List接口的主要实现类;线程不安全的,效率高,底层使用Object[] elementData存储
- LinkedList:对于频繁的插入、删除操作。使用此类效率比ArrayList高,底层使用双向链表存储
- Vector:作为List接口的古老实现类,线程安全,效率低,底层使用 Object[] elementData存储
List中的常用方法
package com.haust.java;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/*List接口中的常用方法
* void add(int index, Object ele):在index位置插入ele元素
* boolean addAll(int index,Collection eles):从index位置开始讲eles中的所有元素添加进来
* Object get(int index):获取指定index位置的元素,如果不存在返回-1
* int indexOf(Object obj):返回obj在集合中首次出现的位置
* int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
* Object remove(int index):移除指定的index位置的元素,并返回此元素
* Object set(int index, Object ele):设置指定Index位置的元素为ele
* List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合
* */
public class ListTest {
@Test
public void test1(){
List list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
list.add(new Person("Tom",13));
System.out.println(list);
//add()
list.add(1,"bb");
System.out.println(list);
//addAll()
List list1 = Arrays.asList(1,2,3);
list.addAll(list1);
System.out.println(list);
//get()
System.out.println(list.get(0));
int indexOf = list.indexOf(456);
System.out.println(indexOf);
//remove()
Object remove = list.remove(0);
System.out.println(remove);
System.out.println(list);
//set()
list.set(0,"cc");
System.out.println(list);
//subList()
List list2 = list.subList(2, 4);
System.out.println(list2);
}
}
Collection子接口之二:Set接口
Set接口存储的是无序的,不可重复的数据
HashSet:作为Set接口的主要实现类;线程不安全,可以存储null值
LinkedHashSet:作为HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历
TreeSet:可以按照添加对象的指定属性,进行排序。
添加元素的过程:以HashSet为例:
我们想HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算HashSet底层数组找那个的存放位置(即为:索引位置),判断数组此位置上是否已经有元素
如果此位置上没有其他元素,则元素a添加成功 ——>情况1
如果此位置上有其他元素(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
如果hash值不相同,则元素a添加成功 ——>情况2
如果hash值相同,今儿需要调用元素a所在类的equals()方法
equals()返回true,元素a添加失败
equals()返回false,元素a添加成功 ——>情况3
对于添加成功的情况2和情况3而言,元素a与已经存在索引位置上数据以链表的方式存储
jdk7:元素a放在数组中,指向原来的元素
jdk8:原来的元素在数组中,指向元素a
总结:七上八下
HashSet底层:数组+链表的结构
要求:向Set中添加的数据,其所在的类一定要重写hashCode()和equals()方法
LinkedHashSet
LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据
优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet
TreeSet
package com.haust.java1;
import org.junit.Test;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
/*TreeSet
* 向TreeSet中添加的数据,要求是相同类的对象
* 两种排序方式:自然排序(实现Comparable接口)和定制排序
* 自然排序中,比较两个对象是否相同的标准为compareTo()返回0,不再是equals()
* 自然排序中,比较两个对象是否相同的标准为compare()返回0,不再是equals()
* */
public class TreeSetTest {
@Test
public void test1(){
TreeSet set = new TreeSet();
set.add(123);
set.add(456);
set.add(34);
set.add(-34);
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
@Test
public void test2(){
TreeSet set = new TreeSet();
set.add(new User("Tom",12));
set.add(new User("Jerry",32));
set.add(new User("Jim",2));
set.add(new User("Jack",65));
set.add(new User("Jack",56));
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
@Test
public void test3(){
Comparator comparator = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof User && o2 instanceof User){
User u1 = (User) o1;
User u2 = (User) o2;
return Integer.compare(u1.getAge(),u2.getAge());
}
else {
throw new RuntimeException("输入的数据类型不匹配");
}
}
};
TreeSet set = new TreeSet(comparator);
set.add(new User("Tom",12));
set.add(new User("Tom1",12));
set.add(new User("Jerry",32));
set.add(new User("Jim",2));
set.add(new User("Jack",65));
set.add(new User("Jack",56));
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
Map接口
Map的实现类结构的情况:
HashMap:作为Map的主要实现类,线程不安全,效率高,可以存储null的key和value
LinkedHashMap:保证在map元素是,可以按照添加的顺序实现遍历,原因:在原有的HashMap基础上,添加了一堆指针,指向前一个和后一个元素,对于频繁的遍历操作,此类执行效率高于HashMap。
TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或自然排序。底层使用红黑树。
Hashtable:作为古老的实现类,线程安全,效率低,不可以存储null的key和value
Properties:常用来处理配置文件。key和value都是String类型
HashMap的底层:数组+链表(jdk7及之前) 数组+链表+红黑树(jdk8)
Map结构的理解
Map中的key:无序的、不可重复的,使用Set存储所有的key,---->key所在的类重写equals()和hashCode() (以HashMap为例)
Map中的value:无序的,可重复的,使用Collection存储所有的value —> value所在类要重写equals()
一个键值对:key-value构成了一个Entry对象
Map中的entry:无序的、不可重复的,使用Set存储所需的entry
面试题:
1、HashMap的底层实现原理?
JDK7:
- HashMap map = new HashMap();
在实例化以后,底层创建了长度为16的一维数组Entry[] table。
...可能已经执行过多次put...
map.put(key1,value1);
首先,计算key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到Entry数组中的存放位置。
如果此位置上的数据为空,此时,key1-value1添加成功 ——>情况一
如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1与已经存在的一个或多个数据的哈希值:
如果key1的哈希值与已经存在的数据的哈希值都不成功,此时key1-value1添加成功 ——>情况二
如果key1的哈希值和已经存在的某一个数据的哈希值相同,继续比较:调用key1所在类的equals()方法,比较:
如果equals()返回false,此时key1-value1添加成功 ——>情况三
如果equals()返回true,此时使用value1替换value2。
补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储
在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)时会进行扩容,默认的扩容方式:扩容为原来容量的二倍,并将原有的数据复制过来
JDK8相较于jdk7在底层实现方面的不同:
1、new HashMap():底层没有创建一个长度为16的数组
2、jdk8 底层的数组是Node[],而非Entry[]
3、首次调用put()方法时,底层创建长度为16的数组
4、jdk7底层只有数组+链表。JDK8中底层数据结构:数组+链表+红黑树
当数组中宏的某一个索引位置上的元素以链表的形式存在的数据格式>8且当前数组的长度 > 64时,此时此索引位置上的所有数据改为使用红黑树存储。
2、HashMap和Hashtable的异同?
见上
Map中定义的方法
package com.haust.java;
/*Map:双列数据,存储key-value对的数据————类似于高中的函数 y = f(x)*/
import org.junit.Test;
import java.util.*;
/*
* Map中定义的方法
* 添加、删除、修改操作
* Object put(Object key, Object value):将指定key-value添加到(或修改)当前map对象中
* void putAll(Map m):将m中的所有的key-value对存放到当前map中
* Object remove(Object key):移除指定key的Key-value对,并返回value
* void clear():清空当前map中的所有数据
* Object get(Object key):获取指定key对应的value
* boolean containsKey(Object key):是否包含指定的key
* boolean containsValue(Object value):是否包含指定的value
* int size():返回map中key-value对的个数
* boolean isEmpty():判断当前map是否为空
* boolean equals(Object obj):判断当前map和参数对象obj是否相等
*
* 元视图操作的方法
* Set keySet():返回所有key构成的set集合
* Collection values():返回所有value构成的Collection集合
* Set entrySet():返回所有key-value对构成的Set集合
* */
public class MapTest {
@Test
public void test1(){
Map map = new HashMap();
//添加
map.put("AA",123);
map.put("BB",123);
map.put("CC",562);
//修改
map.put("AA",89);
Map map1 = new HashMap();
map1.put("DD",123);
map1.put("EE",123);
map.putAll(map1);
System.out.println(map);
Object value = map.remove("AA");
System.out.println(value);
System.out.println(map);
//clear()
map1.clear();
System.out.println(map1);
//get()
Object ee = map.get("EE");
System.out.println(ee);
boolean bb = map.containsKey("BB");
System.out.println(bb);
boolean b = map.containsValue(123);
System.out.println(b);
int size = map.size();
System.out.println(size);
}
@Test
public void test2(){
Map map = new HashMap();
//添加
map.put("AA",123);
map.put("BB",123);
map.put("CC",562);
//遍历所有的key集:keySet()
Set set = map.keySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
//遍历所有的value集:values()
Collection values = map.values();
Iterator iterator1 = values.iterator();
while (iterator1.hasNext()){
System.out.println(iterator1.next());
}
//遍历所有的key-value
//entrySet()
Set set1 = map.entrySet();
Iterator iterator2 = set1.iterator();
while (iterator2.hasNext()){
System.out.println(iterator2.next());
}
}
}
Collections是操作List、Set和Map的工具类
面试题:Collection和Collections的区别?
Collection是一个集合的接口,Collections是操作List、Set和Map的工具类
Collections中的常用方法:
package com.haust.java;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/*Collection是操作List、Set和Map的工具类
* 面试题:Collection和Collections的区别?
*reverse(List):反转List中元素的顺序
* shuffle(List):对List集合元素进行随机排序
* sort(List):根据元素的自然排序对指定的List集合元素按升序排序
* sort(List,Comparator):根据指定的Comparator产生的顺序对List集合元素进行排序
* swap(List,int,int):将指定list集合找那个的i处元素和j处元素进行交换
*
* Object max(Collection):根据元素的自然排序,返回给定集合中的最大元素
* Object max(Collection,Comparator):根据Comparator指定的顺序,返回给定集合中的最大元素
* Object min(Collection):根据元素的自然排序,返回给定集合中的最小元素
* Object min(Collection,Comparator):根据Comparator指定的顺序,返回给定集合中的最小元素
* int frequency(Collection,Object):返回指定集合中指定元素的出现次数
* void copy(List dest,List src):将src中的内容复制到dest中
* boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换list对应的旧值
*
* Collections 类中提供了多个syncchronizedXxx()方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题
*
* */
public class CollectionsTest {
@Test
public void test1(){
List list = new ArrayList();
list.add(123);
list.add(123);
list.add(43);
list.add(765);
list.add(-97);
list.add(0);
System.out.println(list);
System.out.println("*****reverse()*****");
Collections.reverse(list);
System.out.println(list);
System.out.println("****shuffle()*****");
Collections.shuffle(list);
System.out.println(list);
System.out.println("*******sort()****");
Collections.sort(list);
System.out.println(list);
System.out.println("*******max()********");
Comparable max = Collections.max(list);
System.out.println(max);
System.out.println("*******min()********");
Comparable min = Collections.min(list);
System.out.println(min);
System.out.println("*******frequenct()********");
int frequency = Collections.frequency(list, 123);
System.out.println(frequency);
System.out.println("******copy()******");
List list1 = new ArrayList();
list1.add(132);
list1.add(56);
list1.add(88);
list1.add(45);
Collections.copy(list,list1);
System.out.println(list);
}
}