java集合
(1)、什么是集合?有什么用?
集合实际上是一个容器,比如数组。可以用来容纳其他类型的数据。
(2)、为什么说集合在开发中使用较多?
集合是一个容器,是一个载体,可以一次容纳多个对象。在实际开发中,假设连接数据库,数据库当中有10条记录,那么假设把这10条记录查询出来,在java中会将10条数据封装成10个java对象,然后将10个java对象放到某一个集合当中,将集合传到前端,然后遍历集合,将一个数据一个数据展现出来。
(3)、集合不能直接存储基本数据类型,另外集合也不能直接存储java对象,集合当中存储的都是java对象的内存地址。(或者说集合中存储的是引用)
list.add(100);//自动装箱Integer
注意:
-
集合在java中本身是一个容器,是一个对象。
-
集合中任何时候存储的都是“引用”。
(4)、在java中每一个不同的集合,底层会对应不同的数据结构。网不同的集合中存储元素,等于将数据放到了不同的数据结构中。什么是数据结构?数据存储的结构就是数据结构。不同的数据结构,数据存储方式不同。例如:
数组、二叉树、链表、哈希表…
new ArrayList();创建一个集合,底层是数组
new LinkList();创建一个集合对象,底层是链表
new TreeSet();创建一个集合对象,底层是二叉树
…
(5)、最好能将集合的继承结构图背会!
总结:
1)、ArrayList,底层是数组
2)、LinkList,底层是双向链表
3)、Vector,底层是数组,线程安全的,效率低,使用较少
4)、HashSet,底层是HashMap,放到HashSet集合中的元素等同于放到 HashMap集合key部分了
5)、TreeSet,底层是TreeMap,放到TreeSet集合中的元素等同于放到TreeMap集合key部分了
6)、HashMap,底层是哈希表
7)、Hashtable,底层是哈希表,只不过线程安全,效率较低,使用较少。
8)、Properties,是线程安全的, 并且key和value只能存储字符串String
9)、TreeMap,底层是二叉树,TreeMap集合的key可以自动按照大小顺序排序
(6)、在java中集合分为两大类:
-
一类是单个方式存储元素:
单个方式存储元素,这一类集合中超级父接口:java.util.Collection;
-
一类是以键值对的方式存储元素:
以键值对的方式存储元素,这一类集合中超级父接口: java.util.Map;
(7)、map.put(k,v)实现原理:
第一步:先将k,v封装到Node对象当中。
第二步:底层会调用K的hashCode()方法的出hash值,然后通过哈希函数/哈希算法,将hash值转换成数组的下标,下标位置上如果没有任何元素,就把Node添加到这个位置上了。如果说下标对应的位置上有链表,此时会拿着k和链表上每一个节点中的k进行equals,如果所有的equals方法返回的都是false,那么这个新节点将被添加到链表的末尾,如果其中有一个equals返回了true,那么这个节点的value将会被覆盖。
map.get(k)实现原理:
先调用k 的hashCode()方法得出哈希值,通过哈希算法转换成数组下标,通过数组下标快速定位到某个位置上,如果这个位置上什么也没有,返回null,如果这个位置上有单向链表,那么会拿着参数k和单向链表上的每个节点中的k进行equals,如果所有equals方法返回false,那么get方法返回null,只要有其中一个返回true,那么此时这个节点的value就是我们要找的value,get方法最终返回这个要找的value。
**以下分别介绍上面的几种集合类型
一、Collection
(1)关于java.util.Collection接口中常用的方法
package javaCoreTest;
import java.util.ArrayList;
import java.util.Collection;
/*
* 关于java.util.Collection接口中常用的方法
* 1、Collection中能存放什么元素?
* 没有使用“泛型”之前,Collection中可以存储Object的所有子类型
* 使用了“泛型”之后,Collection中只能存储某个具体的类型
* 【集合中不能直接存储基本数据类型,也不能存储java对象,只是存储java对象的内存地址】
* 2、Collection中的常用方法
* boolean add(Object e) 向集合中添加元素
* int size() 获取集合中元素个数
* void clear() 清空集合
* boolean contains(Object o)判断当前集合中是否包含元素o,包含为true,不包含为false
* boolean remove(Object o)删除集合中的某个元素
* boolean isEmpty() 判断该集合中元素的个数是否为空
* Object[] toArray() 把集合转换成数组
*
*/
public class CollectionTest01 {
public static void main(String [] args) {
//创建一个集合对象
//Collection c = new Collection ();//接口是抽象的,无法实例化、
//多态
Collection c = new ArrayList();
//1.boolean add(Object e) 向集合中添加元素
c.add(new Object());
c.add(1200);//自动装箱,实际上是放进去了一个对象的内存地址。Integer i = new Integer(1200);
c.add(3.14);//自动装箱
c.add(new Student());
c.add(true);//自动装箱
//2.int size() 获取集合中元素个数
System.out.println("集合中的元素个数:" + c.size());//5
//3.void clear() 清空集合
c.clear();
System.out.println("集合中的元素个数:" + c.size());//0
//再向集合中添加新的元素
c.add("hello");
c.add("java");
c.add("钢铁侠");
c.add("绿巨人");
c.add("迪迦奥特曼");
//4.boolean contains(Object o)判断当前集合中是否包含元素o
boolean bo1 = c.contains("java");
System.out.println(bo1);//true
System.out.println( c.contains("c++"));//false
System.out.println("集合中的元素个数:" + c.size());//5
//5.boolean remove(Object o)删除集合中的某个元素
c.remove("java");
System.out.println("集合中的元素个数:" + c.size());//4
//6.boolean isEmpty() 判断该集合中元素的个数是否为空
System.out.println(c.isEmpty());//false
c.clear();
System.out.println(c.isEmpty());//true
//再向集合中添加新的元素
c.add("hello");
c.add("java");
c.add("钢铁侠");
c.add("绿巨人");
c.add("迪迦奥特曼");
//7.(了解)Object[] toArray() 把集合转换成数组
Object [] obj = c.toArray();
for(int i = 0; i < obj.length; i++) {
System.out.println(obj[i]);//hello java 钢铁侠 绿巨人 迪迦奥特曼
}
}
}
class Student{
}
(2)List接口
package javaCoreTest;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/*
* 测试List接口中常用的方法:
* 1.List集合存储元素特点:有序可重复
*
* 2.List既然是Collection接口的子接口,那么肯定List接口有自己“特色”的方法:
* 以下列出List接口特有的常用的方法:
* void add(int index, Object element) 在列表的指定位置插入指定元素(第一个 参数是下标)
* Object get(int index) 根据下标获取元素
* int indexOf(Object o) 获取指定对象第一次出现处的索引
* int lastIndexOf(Object o) 获取指定对象最后一次出现处的索引
* Object remove(int index) 删除指定位置的元素
* Object set(int index, Object element) 修改指定位置的元素
*/
public class ListTest01 {
public static void main(String [] args) {
//创建List集合
List list = new ArrayList();
//添加元素
list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add("E");
//1.void add(int index, Object element) 在列表的指定位置插入指定元素(第一个 参数是下标)
//这个方法使用不多,因为对于ArrayList集合来说效率比较低
list.add(1, 100);
//迭代
Iterator it = list.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
//2.Object get(int index) 根据下标获取元素
System.out.println(list.get(1));//100
//自己比较特殊的遍历方式
//通过下标遍历【List集合有而Set没有】
for(int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//3.int indexOf(Object o) 获取指定对象第一次出现处的索引
System.out.println(list.indexOf(100));//1
//4.int lastIndexOf(Object o) 获取指定对象最后一次出现处的索引
System.out.println(list.lastIndexOf(100));//1
//5.Object remove(int index) 删除指定位置的元素
list.remove(1);
System.out.println(list.size());//5
//6.Object set(int index, Object element) 修改指定位置的元素
list.set(2, "money");
for(int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
(3)集合迭代/遍历
package javaCoreTest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
/**
*集合迭代/遍历专题
*/
/*
* 关于集合元素的remove
* 当集合的结构发生改变时,迭代器必须重新获取,如果还是用以前老的迭代器
* 会出现异常:java.util.ConcurrentModificationException
*
* 在迭代集合元素的过程中,不能调用集合对象的remove方法,删除元素:
* c.remove();//不能这样
*在迭代的过程中,一定要使用迭代器Iterator的remove方法,
*删除元素,不要使用集合自带的remove方法
*/
public class CollectionTest02 {
public static void main(String [] args) {
//注意:以下讲解的遍历/迭代方式是所有Collection通用的一种方式
//在Map集合中不能用。在所有的Collection以及子类中使用
//创建集合对象
Collection c = new ArrayList();
//如果在此处获取迭代器,指向的是集合中没有元素状态下的迭代器
//注意:集合结构只要发生改变,迭代器必须重新获取
//Iterator it = c.iterator();
//添加新元素
c.add("abc");
c.add("def");
c.add(100);
c.add(new Object());
c.add(100);
//对集合Collection进行遍历/迭代
//第一步:获取集合对象的迭代器对象Iterator
//迭代器最初并没有指向第一个元素
Iterator it = c.iterator();
//第二步:通过以上获取的迭代器对象开始迭代/遍历集合
/*
* 以下两个方法是迭代器对象Iterator中的方法:
* boolean hasNext()如果仍有元素可以迭代,则返回true。
* Object next() 返回迭代的下一个元素
*/
while(it.hasNext()) {
System.out.println(it.next());//输出有序可重复
//删除元素之后集合的结构发生变化,应该重新获取迭代器
//但是下一次循环的时候并没有重新获取迭代器,所有会出现异常
//c.remove(it.next());
//可以使用迭代器删除
it.remove();//删除的一定是迭代器指向的当前元素
}
//HashSet集合
Collection c1 = new HashSet();
c1.add(100);
c1.add(90);
c1.add(100);
c1.add(0);
c1.add(20);
c1.add(100);
c1.add(5000);
Iterator it1 = c1.iterator();
while(it1.hasNext()) {
System.out.println(it1.next());//无序不可重复
/*0
100
20
5000
90*/
}
}
}
(4)深入Collection集合的contains方法
package javaCoreTest;
import java.util.ArrayList;
import java.util.Collection;
/*
*深入Collection集合的contains方法:
* boolean contains(Object o)
* 判断集合中是否包含某个对象o
* 如果包含返回true,如果不包含返回false
*
* contains方法的底层是怎么判断集合中是否包含某个元素的呢?
* 调用了equals方法进行比对
*/
public class CollectionTest03 {
public static void main(String [] args) {
//创建集合对象
Collection c = new ArrayList();
//向集合中存储元素
String s1 = new String ("abc");
c.add(s1);
String s2 = new String ("def");
c.add(s2);
//新建String对象
String x = new String ("abc");
//c集合中是否包含x?
System.out.println(c.contains(x));//true
}
}
(5)测试contains、remove方法
package javaCoreTest;
import java.util.*;
//测试contains、remove方法
public class CollectionTest04 {
public static void main(String [] args) {
//创建集合对象
Collection c = new ArrayList();
User u1 = new User("jack");
c.add(u1);
User u2 = new User("jack");
//没有重写equals之前,这个结果是false
//因为比较的是内存地址
//System.out.println(c.contains(u2));//false
//重写equals方法之后,比较是内容
System.out.println(c.contains(u2));//true
c.remove(u2);
//如果重写了equals方法,删除u2会使集合中的u1被清除掉,如果不写equals方法则不行
System.out.println(c.size());//0
}
}
class User{
private String name;
public User() {
}
public User(String name) {
this.name = name;
}
public boolean equals(Object obj) {
if(obj == null || !(obj instanceof User))return false;
if(obj == this)return true;
User u = (User)obj;
return u.name.equals(this.name);
}
}
(6)ArrayList
package javaCoreTest;
import java.util.ArrayList;
import java.util.List;
/*
* ArrayList集合:
* 1、默认初始化容量是10
* 2、集合底层是一个Object[]数组
* 3、构造方法:
* new ArrayList();
* new ArrayList(20);
* 4、ArrayList集合的扩容:
* 原容量的1.5倍
* ArrayList集合底层是数组,怎么优化?
* 尽可能的减少扩容,因为数组扩容效率较低,建议在
* 使用ArrayList集合的时候预估计元素的个数,
* 给定一个初始化容量。
* 5、数组优点:检索效率比较高(每个元素占用空间大小相同,内存地址是连续的,知道首元素内存地址,然后知道下标,通过数学表达式计算出元素的内存地址,所以检索效率最高)
* 6、数组缺点:随机增删元素效率比较低
* 注意:向数组末尾添加元素,效率很高,不受影响。
* 7、面试官经常会问:这么多集合,你用哪个集合最多?
* ArrayList集合。因为往数组末尾添加元素,效率不受影响。
* 另外,我们检索/查找某个元素的操作比较多。
*/
public class ArrayListTest01 {
public static void main(String [] args) {
//默认初始化容量是10
List list = new ArrayList();
//集合的size()方法是获取当前集合中元素的个数,不是获取集合的容量
System.out.println(list.size());//0
//指定初始化容量
List list1 = new ArrayList(20);
//集合的size()方法是获取当前集合中元素的个数,不是获取集合的容量
System.out.println(list1.size());//0
}
}
package javaCoreTest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
/*
* 集合ArrayList的构造方法
*/
public class ArrayListTest02 {
public static void main(String [] args) {
//默认初始化容量10
List list1 = new ArrayList();
//指定初始化容量为100
List list2 = new ArrayList(100);
//创建一个HashSet集合
Collection c = new HashSet();
c.add(100);
c.add(200);
c.add(900);
c.add(50);
//通过这个构造方法可以将HashSet集合转换成List集合
List list3 = new ArrayList(c);
for(int i = 0; i < list3.size(); i++) {
System.out.println(list3.get(i));//
}
}
}
(7)Linkedlist
package javaCoreTest;
import java.util.LinkedList;
import java.util.List;
/*
* *链表的优点:
* 由于链表上的元素在空间存储上内存地址不连续,所以随机增删元素的时候不会有
* 大量元素位移,因此随机增删效率较高。在以后的开发中,如果遇到随机增删集合中
* 元素的业务比较多时,建议使用Linkedlist
*
* *链表的缺点:
* 不能通过数学表达式计算被查找元素的内存地址,每一次查找都是从头节点开始
* 遍历,直到找到为止,所以LinkedList集合检索/查找的效率较低
*/
public class LinkedListTest01 {
/*
* LinkedList 集合底层也是有下标的。
* 注意:ArrayList之所以检索效率比较高,不是单纯因为下标的原因,是因为底层数组发挥作用
* LinkedList集合照样有下标,但是检索/查找某个元素的时候效率比较低,因为只能从头结点开始一个个地遍历
*/
public static void main(String [] args) {
List list = new LinkedList();
list.add("a");
list.add("b");
list.add("c");
list.add("e");
for(int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
(8) HashSet
package javaCoreTest;
import java.util.HashSet;
import java.util.Set;
/*
* HashSet:无序不可重复
*/
public class HashSetTest01 {
public static void main(String [] args) {
//创建集合
Set<Integer> set = new HashSet<>();
//添加元素
set.add(1);
set.add(2);
set.add(3);
set.add(4);
set.add(5);
set.add(6);
set.add(3);
//迭代
for(Integer i : set) {
System.out.println(i);
}
}
}
(9)TreeSet
package javaCoreTest;
import java.util.Set;
import java.util.TreeSet;
/*
* TreeSet集合存储元素特点:
* 无序不可重复,但是存储的元素可以自动按照大小顺序排序!
* 称为:可排序集合
*
* 这里的无序指:存进去的顺序和取出的顺序不同,并且没有下标
*/
public class TreeSetTest01 {
public static void main(String [] args) {
//创建集合对象
Set<Integer> set = new TreeSet<>();
//添加元素
set.add(1);
set.add(2);
set.add(1);
set.add(4);
set.add(2);
set.add(8);
set.add(5);
set.add(0);
//迭代
for(Integer i : set) {
System.out.println(i);
}
}
}
(10) Vector
package javaCoreTest;
import java.util.*;
/*
* Vector:
* 1、底层是一个数组
* 2、初始化容量:10
* 3、扩容之后是原容量的2倍
* 10 --> 20 --> 40 --> 80
* 4、ArrayList集合扩容特点:
* 是原容量的1.5倍
* 5、Vector中所有的方法都是线程同步的,都带有synchronized关键字
* 效率比较低,使用较少。
* 6、怎么将一个线程不安全的ArrayList集合转换成线程安全的?
* 使用集合工具类:
* java.util.Collections;
* 【java.util.Collection是集合接口】
*
*/
public class VectorTest01 {
public static void main(String [] args) {
//默认初始化容量为10
List vec = new Vector();
//添加元素
vec.add(1);
vec.add(2);
vec.add(3);
vec.add(4);
vec.add(5);
vec.add(6);
vec.add(7);
vec.add(8);
vec.add(9);
vec.add(10);
//到此处,集合要进行扩容
vec.add(11);
for(int i = 0; i < vec.size(); i++) {
System.out.println(vec.get(i));
}
List list = new ArrayList();//非线程安全的
//变成线程安全的
Collections.synchronizedList(list);
list.add("hello");
list.add("world");
}
}
(11)Collections 集合工具类
package javaCoreTest;
import java.util.*;
/*
* java.util.Collection 集合接口
* java.util.Collections 集合工具类,方便集合的操作
*/
public class CollectionsTest01 {
public static void main(String [] args) {
//ArrayList集合不是线程安全的
List<String> list = new ArrayList<>();
//变成线程安全的
Collections.synchronizedList(list);
//排序
list.add("abz");
list.add("abf");
list.add("abt");
list.add("abg");
Collections.sort(list);
for(String arr : list) {
System.out.println(arr);
}
List<WuGui> list1 = new ArrayList<>();
list1.add(new WuGui(1000));
list1.add(new WuGui(2000));
list1.add(new WuGui(500));
//注意:对List集合中元素排序,需要保证List集合中的元素实现了:Comparable接口
Collections.sort(list1);
for(WuGui wg : list1) {
System.out.println(wg);
}
//对Set集合怎么排序?
Set<String> set = new HashSet<>();
set.add("king5");
set.add("king4");
set.add("king2");
set.add("king3");
//将Set集合转换成List集合
List<String> mylist = new ArrayList<>(set);
Collections.sort(mylist);
for(String s : mylist) {
System.out.println(s);
}
}
}
class WuGui implements Comparable<WuGui>{
int age;
public WuGui(int age) {
this.age = age;
}
@Override
public int compareTo(WuGui o) {
// TODO Auto-generated method stub
return this.age - o.age;
}
@Override
public String toString() {
return "WuGui [age=" + age + "]";
}
}
二、Map
(1).Map接口中常用的方法及遍历
package javaCoreTest;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/*
* java.util.Map接口中常用的方法及遍历
*
* 1.Map和Collection没有继承关系
* 2.Map集合以key和value的方式存储数据:键值对
* key和value都是引用数据类型
* key和value都是存储对象的内存地址
* key起到主导的地位,value是key的一个附属品
* 3.Map接口中常用的方法:
* V put(K key, V value) 向Map集合中添加键值对
* V get(Object key) 通过key获取value
* void clear() 清空Map集合
* boolean containsKey(Object key)判断Map中是否包含某个key
* boolean containsValue(Object value)判断Map中是否包含某个value
* boolean isEmpty() 判断Map集合中元素个数是否为0
* Set<K> keySet() 获取Map集合中所有的key(所有的键是一个set集合)
* V remove(Object key) 通过key删除键值对
* int size() 获取Map集合中键值对的个数
* Collection<V> values() 获取Map集合中所有value,返回一个Collection
*
* Set<Map.Entry<K,V>> entrySet()
* 将Map集合转换成Set集合,如下所示:
* map1集合对象
* key value
* -------------------------------
* 1 zhangsan
* 2 lisi
* 3 wangwu
* 4 zhsoliu
* Set set = map1.entrySet();
* 转换成Set对象
* 1=zhangsan【注意:Map集合通过entrySet()方法转换成Set集合,Set集合中元素的类型Map.<Entry<K,V>>】
* 2=lisi 【Map.Entry和String一样,都是一种类型的名字,只不过:Map.Entry是静态内部类】
* 3=wangwu
* 4=zhaoliu
*/
public class MapTest01 {
public static void main(String [] args) {
//创建Map集合对象
Map<Integer, String> map = new HashMap<>();
//1.V put(K key, V value) 向Map集合中添加键值对
map.put(1, "zhangsan");
map.put(2, "wangwu");
map.put(3, "lisi");
map.put(4, "zhaoliu");
//2.V get(Object key) 通过key获取value
System.out.println(map.get(2));//wangwu
//3.int size() 获取Map集合中键值对的个数
System.out.println(map.size());//4
//4.V remove(Object key) 通过key删除键值对
map.remove(2);
System.out.println(map.size());//3
//5.boolean containsKey(Object key)判断Map中是否包含某个key
System.out.println(map.containsKey(1));//true
//6.boolean containsValue(Object value)判断Map中是否包含某个value
//contains方法底层调用的是equals进行比对,所以自定义的类型都需要重写equals方法
System.out.println(map.containsValue("zhaoliu"));//true
//7.boolean isEmpty() 判断Map集合中元素个数是否为0
System.out.println(map.isEmpty());//false
//8.void clear() 清空Map集合
map.clear();
System.out.println(map.size());//0
map.put(1, "zhangsan");
map.put(2, "wangwu");
map.put(3, "lisi");
map.put(4, "zhaoliu");
System.out.println("------------------------------");
//Map集合的遍历
//第一种方式:获取所有的key,通过遍历key,来遍历value
//Set<K> keySet()获取Map集合中所有的key(所有的键是一个set集合)
Set<Integer> keys = map.keySet();
//通过迭代器,遍历key,通过key获取value
Iterator<Integer> it = keys.iterator();
while(it.hasNext()) {
Integer key = it.next();
String value = map.get(key);
System.out.println(key + "=" + value);
}
//foreach
for(Integer k : keys) {
System.out.println(k + "=" + map.get(k));
}
//第二种方式:Set<Map.Entry<K,V>> entrySet()
//以上这个方法把Map集合直接全部转换成Set集合、
//Set集合中元素的类型是:Map.Entry
Set<Map.Entry<Integer, String>> set = map.entrySet();
//遍历Set集合,每次取出一个Node
Iterator<Map.Entry<Integer, String>> it2 = set.iterator();
while(it2.hasNext()) {
Map.Entry<Integer, String> node = it2.next();
System.out.println(node);
Integer k = node.getKey();
String v = node.getValue();
System.out.println(k + "=" + v);
}
//foreach
//这种方式效率比较高
for(Map.Entry<Integer, String> i : set) {
System.out.println(i.getKey() + " " + i.getValue());
}
}
}
(2)HashMap
package javaCoreTest;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/*
* 1.HashMap集合底层是哈希表/散列表的数据结构
*
* 2.哈希表是一个怎样的数据结构?
* 哈希表是一个数组和单向链表的结合体、
* 数组:在查询方面效率很高,随机增删效率较低
* 单向链表:在随机增删方面效率很高,在查询方面效率低
* 哈希表将以上两种数据结构融合在一起,充分发挥他们各自的优点。
*
* 3.HashMap集合底层的源代码:
* public class HashMap{
* //HashMap底层实际上就是一个数组(一维数组)
* Node<K,V>[] table;
*
* //静态的内部类HashMap.Node
* static class Node<K,V>{
* final int hash;//哈希值
* final K key;//存储到Map集合中的那个key
* V value;//存储到Map集合中的那个value
* Node<K,V> next;//下一个节点的内存地址
* }
* }
*
* 4.最主要掌握的是:
* map.put(k,v)
* v = map.get(k)
* 以上这两个方法的实现原理,必须掌握
*
* 5.HashMap集合的key部分特点:
* 无序,不可重复。
* 为什么无序?
* 因为不一定挂到哪个单向链表上。
* 怎么保证不可重复?
* equals方法来保证HashMap集合的key不可重复。如果key重复了,value会覆盖。
*
* 放在HashMap集合key部分的元素其实是放在HashSet集合中了。
* 所以HashSet集合中的元素也需要同时重写hashCode()+ equals()方法。
*
* 6.哈希表HashMap使用不当时无法发挥性能!
* 假设将所有的hashCode方法返回值固定为某个值,那么会导致底层哈希表变为纯单向链表。这种情况称为:散列分布不均匀。
* 什么是散列分布均匀?
* 假设有100个元素,10个单向链表,每个单向链表上有10个节点,这是散列分布均匀的。
*
* 假设将所有的hashCode方法返回值都设定为不一样的值,可以吗?、
* 不行,因为这样的话导致底层哈希表就成为一维数组了,没有链表的概念。也是散列分布不均匀。
*
* 散列分布均匀需要重写hashCode方法时有一定的技巧。
*
* 7.重点:放在HashMap集合key部分的元素,以及放在HashMap集合中的元素,需要同时重写HashCode和equals方法。
*
* 8.HashMap集合的默认初始化容量是16,默认加载因子是0.75
* 【这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组开始扩容】
*
* 重点记住:HashMap集合初始化容量必须是2的倍数【官方推荐】
* 这是因为达到散列均匀,为了提高HashMap集合的存取效率
*/
public class HashMapTest01 {
public static void main(String [] args) {
//测试HashMap集合key部分的元素特点
//Integer是key,它的hashCode和equals都重写了
Map<Integer, String> map = new HashMap<>();
map.put(1111, "zhangsan");
map.put(1222200, "wangtiechui");
map.put(5555, "lisi");
map.put(5555, "zhaoliu");//key重复的时候value会自动覆盖
map.put(8888, "king");
System.out.println(map.size());//4
//遍历map集合
Set<Map.Entry<Integer,String>> set = map.entrySet();
for(Map.Entry<Integer,String> entry : set) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
}
}
package javaCoreTest;
import java.util.HashSet;
import java.util.Set;
/*
*1. 向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,然后在调用equals方法
* equals方法有可能调用,也有可能不调用。
*
* 拿put(k,v)举例,什么时候equals不会调用?
* k.hashCode()方法返回哈希值,哈希值经过哈希算法转换成数组下标。数组下标位置上
* 如果null,equals不需要执行。
*
* 拿get(k)举例,什么时候equals不会调用?
* 如果单向链表上为null时,不需要调用equals方法。
*
* 2.注意:如果一个类的equals方法重写,那么hashCode方法必须重写。
* 并且equals方法返回的如果是true,hashCode方法返回的值必须一样。
*
* equals方法返回true表示两个对象相同,在同一个单链表上比较。那么对于同一个单链表上的节点来说,
* 他们的哈希值都是相同的,所以hashCode方法的返回值也应该相同。
*
* 3.hashCode方法和equals方法可以直接用IDEA工具生成,但是这两个方法需要同时生成。
*
* 4.终极结论:
* 、 放在HashMap集合key部分的,以及放在HashSet集合中的元素,需要同时重写hashCode方法和equals方法
*
* 5.扩容之后的容量是原容量的2倍
*/
public class HashMapTest02 {
public static void main(String [] args) {
University u1 = new University("zhangsan");
University u2 = new University("zhangsan");
//重写equals方法之前是false
//System.out.println(u1.equals(u2));//false
//重写equals方法之后是true
System.out.println(u1.equals(u2));//true
System.out.println("u1的hashCode=" + u1.hashCode());//366712642(重写hashCode方法后:-1432604525)
System.out.println("u2的hashCode=" + u2.hashCode());//1829164700(重写hashCode方法后:-1432604525)
//u1.equals(u2)结果已经是true了,表示u1和u2是一样的,相同的,那么往HashSet集合中放的话,按说只能放进去1个。
//HashSet集合特点:无序不可重复
Set<University> univer = new HashSet<>();
univer.add(u1);
univer.add(u2);
System.out.println(univer.size());//1
}
}
class University{
private String name;
public University() {
}
public University(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
University other = (University) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
/*public boolean equals (Object obj) {
if(obj == null || !(obj instanceof University))return false;
if(obj == this) return true;
University u = (University)obj;
return this.name.equals(u.name);
}*/
}
package javaCoreTest;
import java.util.HashMap;
import java.util.Map;
/*
* HashMap集合key部分允许null吗?
* 允许
* 但是要注意:HashMap集合的key中null值只能有一个。
*/
public class HashMapTest03 {
public static void main(String [] args) {
Map map = new HashMap();
//HashMap集合允许key为null
map.put(null, null);
System.out.println(map.size());//1
//key重复的话value被覆盖
map.put(null, 100);
System.out.println(map.size());//1
//通过key获取value
System.out.println(map.get(null));//100
}
}
package javaCoreTest;
import java.util.Hashtable;
import java.util.Map;
/*
* Hashtable的key可以为null吗?
* Hashtable的key和value都是不能为null的。
* HashMap集合的key和value都是可以为null的。
*
* Hashtable方法都带有synchronized:线程安全的。
*
* Hashtable 和HashMap一样,底层都是哈希表数据结构
* Hashtable的初始化容量是11,默认加载因子是0.75
* Hashtable的扩容是:原容量*2 + 1
*/
public class HashtableTest01 {
public static void main(String [] args) {
Map map = new Hashtable();
//空指针异常
//map.put(null, 123);//java.lang.NullPointerException
//空指针异常
map.put(1, null);//java.lang.NullPointerException
}
}
(3)Properties
package javaCoreTest;
import java.util.Properties;
/*
* 目前只需要掌握Properties属性类对象的相关方法即可
* Properties是一个Map集合,继承Hashtable,Properties的key和value都是String类型
* Properties被称为属性类对象
* Properties是线程安全的。
*/
public class PropertiesTest01 {
public static void main(String [] args) {
//创建一个properties对象
Properties pro = new Properties();
//需要掌握Properties的两个方法,存和取
//存
pro.setProperty("user1", "jdbc:mysql://localhost:3306/bjpowernode");
pro.setProperty("driver", "com.mysql.jdbc.Driver");
pro.setProperty("username", "root");
pro.setProperty("password", "123");
//通过key获取value
String user1 = pro.getProperty("user1");
String driver = pro.getProperty("driver");
String username = pro.getProperty("username");
String password = pro.getProperty("password");
System.out.println(user1);
System.out.println(driver);
System.out.println(username);
System.out.println(password);
}
}
三、泛型
package javaCoreTest;
import java.util.*;
/*
* JDK5.0之后出现:泛型
* 使用泛型List<类型>,表示List集合中只允许存储此种类型的数据
* 用泛型来指定集合中存储的数据类型
* 泛型这种语法机制,只在程序编译阶段起作用,只是给编译器参考(运行阶段泛型没用)
*/
/*
* 1.使用泛型的优点:
* 集合中存储的元素类型统一
* 从集合中取出的元素类型是泛型指定的类型,不需要进行大量的“向下转型”
*
* 2.使用泛型的缺点:
* 导致集合中存储的元素缺乏多样性
*
* 大多数业务中,集合中元素的类型还是统一的,所以这种泛型特性被大家所认可。
*/
public class GenericTest01 {
public static void main(String [] args) {
List<Animal01> list = new ArrayList();
//编译报错,因为list集合指定为Animal类型
//list.add("string");
Cat01 c = new Cat01();
Bird01 b = new Bird01();
list.add(b);
list.add(c);
//获取迭代器
//迭代器也可以使用泛型
Iterator<Animal01> it = list.iterator();
while(it.hasNext()) {
//使用泛型之后,每一次迭代返回的数据都是Animal类型
Animal01 a = it.next();
//这里不需要进行强制类型转换,直接调用
a.move();
}
//调用子类特有的方法还是需要向下转换的
Iterator<Animal01> it1 = list.iterator();
while(it1.hasNext()) {
Animal01 a = it1.next();
if(a instanceof Cat01) {
Cat01 m = (Cat01)a;
m.catMouse();
}
if(a instanceof Bird01) {
Bird01 n = (Bird01)a;
n.fly();
}
}
}
}
class Animal01{
public void move() {
System.out.println("Runing!");
}
}
class Cat01 extends Animal01{
public void catMouse() {
System.out.println("Cat and mouse");
}
}
class Bird01 extends Animal01{
public void fly() {
System.out.println("Flying !");
}
}
package javaCoreTest;
/*
* 自定义泛型
* <>尖括号中是一个标识符,随便写
* java 源代码中经常出现的是:
* <T>和<E>
*/
public class GenericTest02 {
public static void main(String [] args) {
MyIterator<String> mi = new MyIterator<>();
String s1 = mi.get();
MyIterator<Integer> mi1 = new MyIterator<>();
Integer i = mi1.get();
}
}
class MyIterator<T>{
public T get() {
return null;
}
}
四、总结:必须要掌握的知识!!!:
(1)ArrayList、LinkedList
package javaCoreTest;
import java.util.*;
/*
* ArrayList、LinkedList总结:
* 每个集合对象的创建(new)
* 向集合中添加元素
* 从集合中取出某个元素
* 遍历集合
*/
public class AllList {
public static void main(String [] args) {
//创建集合对象
LinkedList<String> list = new LinkedList();
//ArrayList<String> list = new ArrayList<>();
//添加元素
list.add("zhangsan");
list.add("wangtiechui");
list.add("wangdaniang");
//从集合中取出某个元素
//List集合有下标
//取出某个元素
System.out.println(list.get(0));//zhangsan
//遍历
for(int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
for(String i : list) {
System.out.println(i);
}
Iterator it = list.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
(2)HashSet
package javaCoreTest;
import java.util.*;
/*
* HashSet(HashMap的key,存储在HashMap集合key的元素需要同时重写HashCode和equals方法)
* 每个集合对象的创建(new)
* 向集合中添加元素
* 从集合中取出某个元素
* 遍历集合
* 测试HashSet集合的特点:无序不可重复
*/
public class AllHashSet {
public static void main(String [] args) {
//创建集合对象
HashSet<String> set = new HashSet<>();
//添加元素
set.add("abc");
set.add("def");
set.add("king");
set.add("vde");
//遍历集合
Iterator it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
//测试HashSet集合的特点:无序不可重复
set.add("king");
set.add("king");
set.add("king");
set.add("king");
System.out.println(set.size());//4
set.add("10");
set.add("1");
Iterator it1 = set.iterator();
while (it1.hasNext()) {
System.out.println(it1.next());
}
}
}
(3)HashMap
package javaCoreTest;
import java.util.*;
/*
*每个集合对象的创建(new)
* 向集合中添加元素
* 从集合中取出某个元素
* 遍历集合
*/
public class AllHashMap {
public static void main(String [] args) {
Map<Integer, String> map = new HashMap<>();
//添加元素
map.put(1, "zhangsan");
map.put(9, "lisi");
map.put(1, "wangtiechui");
map.put(10, "kign");
map.put(2, "Sima");//key重复value会覆盖
//获取元素个数
System.out.println(map.size());//4
//取key是2的元素
System.out.println(map.get(2));//Sima
//遍历集合的几种方式
//第一种方式:先获取所有的key,遍历key的时候,通过key获取value
Set<Integer>keys = map.keySet();
for(Integer key : keys) {
System.out.println(key + "=" + map.get(key));
}
//第二种方式:将Map集合转换成Set集合,Set集合中每一个元素是Node
Set<Map.Entry<Integer, String>> nodes = map.entrySet();
for(Map.Entry<Integer, String> node : nodes) {
System.out.println(node.getKey() + "=" +node.getValue());
}
}
}
(4)TreeSet
package javaCoreTest;
import java.util.*;
/*
* 每个集合对象的创建(new)
* 向集合中添加元素
* 从集合中取出某个元素
* 遍历集合
* 测试TreeSet集合中的元素是可排序的
* 测试TreeSet集合中存储的类型是自定义的
*/
public class AllTreeSet {
public static void main(String [] args) {
//集合的创建(可以测试以下TreeSet集合中存储String、Integer的。这些类型都是SUN公司提前写好的)
//TreeSet<Integer> ts = new TreeSet();
//编写比较器可以改变规则
TreeSet<Integer> ts = new TreeSet<>(new Comparator<Integer>() {
public int compare(Integer o1, Integer o2) {
return o2 - o1;//自动拆箱
}
});
//添加元素
ts.add(1);
ts.add(100);
ts.add(20);
ts.add(80);
ts.add(12);
ts.add(500);
ts.add(14);
//遍历(迭代方式)
Iterator <Integer> it = ts.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
//TreeSet 集合中存储自定义类型
TreeSet <A> atree = new TreeSet<>();
atree.add(new A(100));
atree.add(new A(2000));
atree.add(new A(200));
atree.add(new A(100));
atree.add(new A(500));
atree.add(new A(100));
atree.add(new A(300));
atree.add(new A(400));
atree.add(new A(1000));
//遍历
for(A a : atree) {
System.out.println(a);
}
TreeSet<B> btree = new TreeSet<>(new BComparator());
btree.add(new B(500));
btree.add(new B(200));
btree.add(new B(100));
btree.add(new B(5600));
btree.add(new B(300));
for(B b : btree) {
System.out.println(b);
}
}
}
//实现Comparable接口
class A implements Comparable<A>{
int i;
public A(int i) {
this.i = i;
}
@Override
public int compareTo(A o) {
// TODO Auto-generated method stub
return this.i - o.i;
}
@Override
public String toString() {
return "A [i=" + i + "]";
}
}
//第二种方式
class B{
int i;
public B(int i) {
this.i = i;
}
@Override
public String toString() {
return "B [i=" + i + "]";
}
}
class BComparator implements Comparator<B>{
@Override
public int compare(B o1, B o2) {
// TODO Auto-generated method stub
return o1.i - o2.i;
}
}
(5)Properties
package javaCoreTest;
import java.util.Properties;
public class AllProperties {
public static void main(String [] args) {
//创建对象
Properties pro = new Properties();
//存
pro.setProperty("username", "zhangsan");
pro.setProperty("password", "123");
//取
String username = pro.getProperty("username");
String password = pro.getProperty("password");
System.out.println(username);
System.out.println(password);
}
}