java集合
集合、数组都是对多个数据进行存储操作的结构,简称Java容器
此时的存储,主要是指内存层面的存储,不涉及持久化的存储(txt,jpg,avi)
Java集合可分为Collection 和 Map 两种体系
1. Collection接口:单列数据,定义了存取一组对象方法的集合
List : 元素有序、可重复的集合 -->动态数组
ArratList、LinkedList、Vector
Set:元素无序、不可重复的集合 -->数学中的集合
HashSet、LinkedHashSet、TreeSet
2.Map接口:双列数据、保存具有映射关系"key-value"的集合 -->高中函数、不能一个key 对应两个value。python中的字典
HashMap、LinkedHashMap、TreeMap、Hashtable、Properties
Collection 接口中的API(都在代码里有注释了)
package Collection;
import java.awt.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import org.junit.Test;
public class CollectionTest {
@Test
// 向Collection接口的实现类的对象中添加数据obj时,要求obj所在类重写equals。否则Obj里面比的是地址
public void test1(){
Collection coll = new ArrayList();
coll.add("AA");
coll.add("BB");
coll.add(123);
coll.add(new Date());
//size():获取添加的元素的个数
System.out.println(coll.size());
//addAll() 将coll1中的元素添加到coll中
Collection coll1 = new ArrayList();
coll1.add("CC");
coll1.add(456);
coll.addAll(coll1);
System.out.println(coll);
// clear() 清空集合元素
coll.clear();
// isEmpty() 判断当前集合是否为空
System.out.println(coll.isEmpty());
// contains 当前集合中是否包含xx.调用的是equals
// 在判断时会调用OBJ对象所在类的equals方法
System.out.println(coll.contains("CC"));
// containsAll(Collection coll1) 判断形参coll1中的所有元素是否都在当前集合中
Collection coll2 = Arrays.asList(123,456);
System.out.println(coll.containsAll(coll2));
}
@Test
public void test2(){
Collection coll = new ArrayList();
coll.add("AA");
coll.add("BB");
coll.add(123);
coll.add(new Date());
//remove 返回的是布尔值,移除了就返回true
coll.remove("AA"); //可以用一个boolean的值去接收他
System.out.println(coll);
// removeAll(Collention coll1) 从当前元素中移出cool1中所有元素
Collection coll1 = Arrays.asList(123,4567);
coll.removeAll(coll1);
System.out.println(coll);
}
@Test
public void test3(){
Collection coll = new ArrayList();
coll.add("AA");
coll.add("BB");
coll.add(123);
coll.add(new Date());
// // 获取当前集合与coll1的交集,并返回给当前集合
// Collection coll1 = Arrays.asList(123,456,789);
// coll.retainAll(coll1);
// System.out.println(coll);
// equlas
Collection coll2 = new ArrayList();
coll2.add("AA");
coll2.add("BB");
coll2.add(123);
coll2.add(new Date());
System.out.println(coll.equals(coll2)); //顺序也要一样噢
}
@Test
public void test4(){
Collection coll = new ArrayList();
coll.add("AA");
coll.add("BB");
coll.add(123);
coll.add(new Date());
//hashCode() 返回当前对象的哈希值
System.out.println(coll.hashCode());
// 集合---->数组,toArray()
Object[] arr = coll.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","CC"});
// System.out.println(list);
//
}
}
}
Iterator 接口遍历集合元素
package Collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import org.junit.Test;
public class IteratorTest {
@Test
public void test(){
Collection coll = new ArrayList();
coll.add("AA");
coll.add("BB");
coll.add(123);
coll.add(new Date());
Iterator iterator = coll.iterator();
//方式一:不推荐
for (int i = 0;i < coll.size();i++){
System.out.println(iterator.next());
}
// 方式二:推荐
while(iterator.hasNext()){ //还有没有下一个
//next指针下移,将下移后的值返回
System.out.println(iterator.next());
}
// // 错误方法,他每调一次while 就有一个新的迭代器
// while(coll.iterator().hasNext()){
// System.out.println(coll.iterator().next());//死循环
// }
}
// remove方法,在遍历中删除集合中的元素,不同于集合直接调用remove
@Test
public void test2(){
Collection coll = new ArrayList();
coll.add("AA");
coll.add("BB");
coll.add(123);
coll.add(new Date());
Iterator iterator = coll.iterator();
while(iterator.hasNext()){
Object obj = iterator.next();
if("AA".equals(obj)){
iterator.remove(); //删除集合中AA字符串
}
}
iterator = coll.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
JDK5.0新增了foreach循环,用于遍历集合、数组
package Collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import org.junit.Test;
public class ForTest {
@Test
public void test(){
Collection coll = new ArrayList();
coll.add("AA");
coll.add("BB");
coll.add(123);
coll.add(new Date());
// for(集合中元素类型 局部遍历 : 集合对象)
// 内部仍然调用迭代器
for (Object obj : coll){
System.out.println(obj);
}
}
@Test
public void test2(){
int[] arr = new int[]{1,2,3,4};
for(int i : arr){
System.out.println(i);
}
}
@Test
public void test3(){
String[] arr = new String[]{"MM","MM","MM"};
// 方式一 普通for循环 数组内部都会被改变
for(int i = 0;i < arr.length;i++){
arr[i] = "GG";
}
// 方式二:增强for循环,数组内部最后还是不会变,是形参变了
// for(String s : arr){
// s = "GG";
// }
//
for(String s : arr){
System.out.println(s);
}
}
}
List接口
ArrayList,LinkedList,Vector三者的异同?
三个类都是List接口,存储数据的特点相同,存储有序的,可重复的数组
- ArrayList 作为List的主要实现类,线程不安全的,效率高,底层使用Object[] elementData存储
- LinkedList 底层使用双向链表,对于频繁的插入和删除,使用效率比ArrayList高
- Vector 作为List的古老实现类,线程安全的,效率低,底层使用Object[] elementData存储
ArrayList
jdk7 的情况下
ArrayList List = new ArrayList(); //底层创建了长度为10的Object[]数组
List.add(123);//elementData[0] = new Integer(123);
如果此次的添加导致底层的数组容量不够,则扩容;
扩容为原来的1.5倍,同时需要将原有数组中的数据复制到新的数组中
建议开发中去使用带参的构造器,ArrayList List = new ArrayList(int capacity)
jdk8 中 ArrayList的变化
ArrayList List = new ArrayList();//底层Object[] elementData 初始化为{ },并没有创建长度为10的数组
List.add(123);才创建长度为10的数组,并将123添加到elementData[0]
后续添加与扩容操作与jdk7无异
小结:jdk7中ArrayList对象的创建类似于单例的饿汉式。jdk8中ArrayList对象的创建类似于单例的懒汉式。延迟了数组的创建,节省内存。
LinkedList
LinkedList list = new LinkedList;//内部声明了node类型的first 和last
list.add(123);//将123封装到node中,创建了node对象
Vector
底层创建了长度为10的Object[]数组,在扩容方面,扩容为原来的2倍
List的API
Set接口
Set ---- 无序的、不可重复的
- HashSet :作为Set接口的主要实现类,线程不安全的,可以储存null值
- LinkedSet :作为HashSet的子类,遍历其内部数据时,可以按照添加的数据遍历
- TreeSet:可以按照添加元素的指定属性进行排序
无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的Hash值来决定
不可重复性:保证添加的元素按照equals方法判断时,不能返回true,即相同的元素不能添加进来。
添加元素的过程(以HashSet为例
我们向HashSet添加元素a,首先调用元素a的hashcode()方法,计算元素a的哈希值,通过此哈希值算出在HashSet底层存放位置,判断在此位置上是否有其他元素
如果没有其他元素,则直接放入
如果此位置上有其他元素b(或以链表形式存放)则比较a和b的哈希值
如果哈希值不相同,则添加元素a成功
如果哈希值相同,进而需要调用元素a所在类的equals方法,返回true则添加成功
对于情况2和情况3而言,元素a与已经存在的的数据以链表的形式存储
jdk7.0:元素a放到原来的数组中,指向原来的数组
jdk8.0:原来的元素在数组中,指向元素a
对于存放在Set容器中的类,必须要重写hashcode和equals
最好直接Alt + shift + s去生成
LinkedHashSet 在添加数据的同时,每个数据还维护了两个引用,记录此数据前后的数据
优点:对于频繁的遍历操作,LinkHashSet效率比HashSet高
TreeSet
向TreeSet当中添加的数据,要求是相同类的对象
两种排序:自然排序、定制排序
自然排序中,比较两个对象是否相同的标准:compareTo()方法,不再是equals
定制排序:构造TreeSet时带参数,参数为比较的方法compare()方法
Map 接口
Map : 双列数据,存储key - value 对的数据 类似于高中的函数:y = f(x)
|--------HashMap 作为Map的主要实现类,线程不安全的,效率高。可以存储null的key和value
--------------------------LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。
原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。
对于频繁的遍历操作,此类执行效率高于HashMap
|-------- TreeMap:保证按照添加的key - value 进行排序,实现排序遍历。此时考虑key 的自然排序和定制排序。底层使用红黑树
|--------Hashtable 作为古老的实现类 ,线程安全的,效率低。不能存储null的key和value
--------------------------Properties:常用来处理配置文件。key 和 value 都是String 类型
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
public class MapTest {
@Test
public void test(){
Map map = new HashMap();
map.put(null, null);
System.out.println(map);
}
}
HashMap的底层
数组 + 链表 (jdk7之前)
数组 + 链表 + 红黑树(jdk8.0)
Map 结构理解
map 中的key是无序的、不可重复的,使用Set存储所有的value --> key所在类要重写equals 和 hashCode (以hashMap为例)
map 中的Value无序的、可重复的,用Collection 存储所有的value —> values 所在类要重写equals
一个键值对:key - value 构成了一个Entry对象
Map中的Entry :无序的、不可重复的,使用Set存储所有的Entry
HashMap 底层实现原理 (以jdk7为例说明
HashMap map = new HashMap();
底层创建了一个长度为16的一个一维数组Enter[] table
…
map.put(key1,value1);
首先,调用key1所在类的hashcode()计算key1哈希值,此哈希值经过某种计算以后,得到在Entry数组中的存放位置,如果在此位置上的数据为空,此时的key1 - value1 添加成功 --情况一
如果此位置上不为空,(意味着此位置上存在一个或多个数据(以链表形式)),比较key1与他们的哈希值,
如果key1与他们的哈希值都不相同,则key1 - value1 添加成功 --情况二
如果key1的哈希值与已经存在的某一个数据相同,则比较key1所在类的equals方法
如果equals 返回false ,则key1 - value1 添加成功 --情况三
如果equals 返回ture ,使用value1 去替换相同key的value值
关于情况二和情况三,此时的key1 - value1 的数据以链表的方式存储
在不断的添加过程中,会涉及到扩容问题,默认的扩容方式,扩容为原来容量的二倍,并将原有数据复制
jdk8相对于Jdk7的一些不同
new HashMap() 的时候,没有创建一个长度为16的数组
jdk底层的数组是node类型的数组,而非entry数组
首次调用put()方法时,才创建长度为16的数组
jdk7.0底层结构只有,数组+链表 jdk8.0底层结构是 数组 + 链表 + 红黑树
当数组的某一个索引上的元素以链表存在的数据个数大于8,且当前数组长度>64
此时索引位置上的所有数组改为红黑树存储
源码
LinkedHashMap(了解就行)
能够记录添加元素的前后顺序
对于遍历操作很多的时候,可以考虑使用LinkedHashMap
Map接口中定义的方法
常用方法:
添:put
删:removt
改:put
查询:get
长度:size()
遍历:keySet / values()/enterSet
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.junit.Test;
public class MapTest {
@Test
public void test(){
Map map = new HashMap();
map.put(null, null);
System.out.println(map);
}
@Test
public void test2(){
Map map = new HashMap();
map.put("aA",1);
map.put("bA",2);
map.put("cA",3);
// 遍历所有的key
Set set = map.keySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
// 遍历所有的values,是Collection 可以直接用它的方法
Collection coll = map.values();
for(Object obj : coll){
System.out.println(obj);
}
// 遍历所有的key - value
Set entrySet = map.entrySet();
Iterator iterator2 = entrySet.iterator();
while(iterator2.hasNext()){
// System.out.println(iterator2.next());
Object obj = iterator2.next();
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "------>" + entry.getValue());//也能这样
System.out.println(obj.);
}
}
}
向TreeMap中添加key - value ,要求key必须是同一类的对象
因为key要进行排序,自然排序、定制排序
Properties
key - value 都是字符串类型
package Collection;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Properties;
import org.junit.Test;
public class PropertiesTest {
@Test
public void test() throws Exception{
Properties pro = new Properties();
FileInputStream fis = new FileInputStream("jdbc.properties");
pro.load(fis); //加载对应流文件
String name = pro.getProperty("name");
System.out.println(name);
}
}
jdbc.properties 如下
Collections 工具类
Collections是操作Collection 和 Map的工具类
Collections 和 Collection的区别