Java ArrayList
ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。
ArrayList 继承了 AbstractList ,并实现了 List 接口。
import java.util.ArrayList; // 引入 ArrayList 类
ArrayList<E> objectName =new ArrayList<>(); // 初始化
- E: 泛型数据类型,用于设置 objectName 的数据类型,只能为引用数据类型。
- 对象名称: 对象名。
ArrayList 是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
import java.util.*;
public class Test01 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("王");//add添加元素
list.add("帅哥");
System.out.println(list);
System.out.println(list.get(0));//get()通过下标访问元素
list.set(0,"刘");//set()修改值,第一个参数是索引位置,第二个参数是要修改的值
System.out.println(list);
list.remove(0);//remove()通过索引删除元素
System.out.println(list);
System.out.println(list.size());//size()计算集合中的元素数量
for (String i:list){ //用for-each来迭代元素
System.out.println(i);
}
}
}
结果为:
[王, 帅哥]
王
[刘, 帅哥]
[帅哥]
1
帅哥
Java LinkedList
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址。
链表可分为单向链表和双向链表。
一个单向链表包含两个值: 当前节点的值和一个指向下一个节点的链接。
一个双向链表有三个整数值: 数值、向后的节点链接、向前的节点链接。
Java LinkedList(链表) 类似于 ArrayList,是一种常用的数据容器。
import java.util.LinkedList; // 引入 LinkedList 类
LinkedList<E> list = new LinkedList<E>(); // 普通创建方法或者
LinkedList<E> list = new LinkedList(Collection<? extends E> c); // 使用集合创建链表
创建一个简单的链表
import java.util.*;
public class Test01 {
public static void main(String[] args) {
LinkedList<String> sites = new LinkedList<String>();
sites.add("12");//add添加元素
sites.add("34");
sites.add("56");
System.out.println(sites);
sites.addFirst("999"); // 使用 addFirst() 在头部添加元素
sites.addLast("666"); // 使用 addLast() 在尾部添加元素
System.out.println(sites);
sites.removeFirst(); // 使用 removeFirst() 移除头部元素
sites.removeLast(); // 使用 removeLast() 移除尾部元素
System.out.println(sites);
System.out.println(sites.getFirst()); // 使用 getFirst() 获取头部元素
System.out.println(sites.getLast()); // 使用 getLast() 获取尾部元素
}
}
结果为:
[12, 34, 56]
[999, 12, 34, 56, 666]
[12, 34, 56]
12
56
迭代元素
1.第一种方法使用 for 配合 size() 方法来迭代列表中的元素
import java.util.*;
public class Test01 {
public static void main(String[] args) {
LinkedList<String> sites = new LinkedList<String>();
sites.add("12");//add添加元素
sites.add("34");
sites.add("56");
for (int i=0;i< sites.size();i++){
System.out.println(sites.get(i));
}
}
}
2.第二种方法使用 for-each 来迭代元素
import java.util.*;
public class Test01 {
public static void main(String[] args) {
LinkedList<String> sites = new LinkedList<String>();
sites.add("12");//add添加元素
sites.add("34");
sites.add("56");
for (String i:sites){
System.out.println(i);
}
}
}
Java Vector
Vector可实现自动增长的对象数组。java.util.vector提供了向量类(vector)以实现类似动态数组的功能。
java中并没有指针,为了弥补这个缺点,Java提供了丰富的类库来方便编程者使用,vector类便是其中之一。
向量类
创建了一个向量类的对象后,可以往其中随意插入不同类的对象,即不需顾及类型也不需预先选定向量的容量,并可以方便地进行查找。对于预先不知或者不愿预先定义数组大小,并且需要频繁地进行查找,插入,删除工作的情况。可以考虑使用向量类。
向量类提供了三种构造方法
public vector()
public vector(int initialcapacity,int capacityIncrement)
public vector(int initialcapacity)
使用第一种方法系统会自动对向量进行管理,若使用后两种方法。则系统将根据参数,initialcapacity设定向量对象的容量(即向量对象可存储数据的大小),当真正存放的数据个数超过容量时。系统会扩充向量对象存储容量。
参数capacityincrement给定了每次扩充的扩充值。当capacityincrement为0的时候,则每次扩充一倍,利用这个功能可以优化存储。在Vector类中提供了各种方法方便用户的使用:
插入功能:
(1)public final synchronized void adddElement(Object obj)
将obj插入向量的尾部。obj可以是任何类型的对象。对同一个向量对象,亦可以在其中插入不同类的对象。但插入的应是对象而不是数值,所以插入数值时要注意将数组转换成相应的对象。
例如:要插入整数1时,不要直接调用v1.addElement(1),正确的方法为:
Vector v1 = new Vector();
Integer integer1 = new Integer(1);
v1.addElement(integer1);
(2)public final synchronized void setElementAt(Object obj,int index)
将index处的对象设置成obj,原来的对象将被覆盖。
(3)public final synchronized void insertElementAt(Object obj,int index)
在index指定的位置插入obj,原来对象以及此后的对象依次往后顺延。
删除功能:
(1)public final synchronized void removeElement(Object obj)
从向量中删除obj,若有多个存在,则从向量头开始试,删除找到的第一个与obj相同的向量成员。
(2)public final synchronized void removeAllElement();
删除向量所有的对象
(3)public fianl synchronized void removeElementAt(int index)
删除index所指的地方的对象
查询搜索功能:
(1)public final int indexOf(Object obj)
从向量头开始搜索obj,返回所遇到的第一个obj对应的下标,若不存在此obj,返回-1.
(2)public final synchronized int indexOf(Object obj,int index)
从index所表示的下标处开始搜索obj.
(3)public final int lastindexOf(Object obj)
从向量尾部开始逆向搜索obj.
(4)public final synchornized int lastIndex(Object obj,int index)
从index所表示的下标处由尾至头逆向搜索obj.
(5)public final synchornized firstElement()
获取向量对象中的首个obj
(6)public final synchornized Object lastElement()
获取向量对象的最后一个obj
示例:
import java.util.Iterator;
import java.util.Vector;
public class Test01 {
public static void main(String[] args) {
Vector vector=new Vector();//创建一个Vector向量对象
vector.addElement("one");//加入为字符串对象
vector.addElement(1);
vector.addElement(2);
System.out.println(vector);
vector.insertElementAt("two",1);//向指定位置插入新对象,第一个参数是对象,第二个参数是索引
System.out.println(vector);
vector.setElementAt("three",2);//将指定位置的对象设置为新的对象
System.out.println(vector);
Iterator it=vector.iterator(); //迭代器
while (it.hasNext()){
System.out.print(it.next()+" ");
}
}
}
[one, 1, 2]
[one, two, 1, 2]
[one, two, three, 2]
one two three 2
Java Set和Map接口
一、HashSet集合
HashSet集合是非线程安全的。
HashSet集合存储元素的特点:无序不可重复。
——无序:存进去的顺序和取出来的顺序不一定相同。
——不可重复:存进去一个1,不能再存进去一个1。
放到HashSet集合中的元素实际上是放到HashMap集合中的key部分,底层是哈希表。
二、TreeSet集合
TreeSet集合存储元素的特点:无序不可重复的,储存的元素是可以自动按照大小顺序排序的。
——无序:存进去的顺序和取出来的顺序不一定相同。
——不可重复:存进去一个1,不能再存进去一个1。
——可排序:储存的元素可以自动按照大小顺序排序。
放到TreeSet集合中的元素实际上是放到TreeMap集合中的key部分,底层是二叉树。
一、Map集合接口中常用的方法
Map接口和Collection接口没有继承关系。
Map集合是以key和value这种键值对的方式存储元素。
Map集合接口中的常用方法:
Vput(K key, V value):向Map集合中添加键值对
Vget(Object key):通过指定的key,获得其所对应的value
voidclear():清空Map集合
booleancontainsKey(Object key):判断Map集合中是否包含指定的key
booleancontainsValue(Object value):判断Map集合中是否包含指定的value
booleanisEmpty():判断Map集合中的元素个数是否为0
Set<K>keySet():获得Map集合中所有的key(所有的key是一个Set集合)
Vremove(Object key):删除指定key所对应的键值对
intsize():获取Map集合中键值对的个数
public class MapTest01 { public static void main(String[] args) { //创建一个Map集合对象 Map<Integer, String> map = new HashMap<>(); //put() 添加元素 map.put(1, "张三"); map.put(2, "李四"); map.put(3, "王五"); map.put(4, "陈六"); //size() 获得Map集合中键值对的个数 System.out.println("集合中键值对的个数:"+map.size());//集合中键值对的个数:4 //get() 获得指定key所对应的value System.out.println(map.get(2));//李四 //remove() 删除集合中指定key的键值对 map.remove(2); System.out.println("集合中键值对的个数:"+map.size());//集合中键值对的个数:3 //containsKey() 集合中是否包含指定key System.out.println(map.containsKey(3));//true //containsValue() 集合中是否包含指定Value System.out.println(map.containsValue("陈六"));//true //values() 获得集合中所有的value Collection<String> values = map.values(); for (String value : values) { System.out.print(value+" ");//张三 王五 陈六 } System.out.println(); //clear() 清空集合 map.clear(); System.out.println("集合中键值对的个数:"+map.size());//集合中键值对的个数:0 //isEmpty() 判断集合中键值对个数是否为0 System.out.println(map.isEmpty());//true //======================================================================================================== //以下两个方法可以用作Map集合的遍历(重点) map.put(1, "张三"); map.put(2, "李四"); map.put(3, "王五"); map.put(4, "陈六"); //第一个:keySet() 获得集合中所有的key,通过遍历key获得所有的value //获得所有的key,所有的key是一个Set集合 Set<Integer> set = map.keySet(); //foreach循环 for (Integer key : set) { System.out.print(key+"="+map.get(key)+" ");//1=张三 2=李四 3=王五 4=陈六 } System.out.println(); //迭代器循环 Iterator<Integer> it = set.iterator(); while (it.hasNext()) { Integer key = it.next(); System.out.print(key+"="+map.get(key)+" ");//1=张三 2=李四 3=王五 4=陈六 } System.out.println(); //第二种(建议):entrySet() 将Map集合转换为Set集合 Set<Map.Entry<Integer, String>> set1 = map.entrySet(); //使用迭代器循环 Iterator<Map.Entry<Integer, String>> it1 = set1.iterator(); while (it1.hasNext()) { Map.Entry<Integer, String> node = it1.next(); System.out.print(node.getKey()+"="+node.getValue()+" ");//1=张三 2=李四 3=王五 4=陈六 } System.out.println(); //使用foreach循环 for (Map.Entry<Integer, String> node : set1) { System.out.print(node.getKey()+"="+node.getValue()+" ");//1=张三 2=李四 3=王五 4=陈六 } } }
三、Hashtable集合
Hashtable集合默认初始容量为11,默认加载因子为0.75
——默认加载因子是当Hashtable集合底层数组的容量达到75%的时候,数组开始扩容。
Hashtable集合的扩容:默认扩容到原容量的2倍再加1((原容量*2)+1)。
Hashtable集合的key和value不能为null。(为null会出现空指针异常)
——HashMap集合的key和value可以为null。
Hashtable集合的底层是哈希表/散列表的数据结构。
四、TreeMap集合
TreeMap集合底层是一个二叉树数据结构。
放到TreeMap集合中key部分的元素相当于放到TreeSet集合中。
TreeMap集合中存储元素的特点:与TreeSet相同,无序不可重复,元素可按照大小顺序自动排序,称为可排序集合。
public class TreeSetTest02 {
public static void main(String[] args) {
/*
1.放在集合中的元素实现java.lang.Comparable接口。
TreeSet<Vip> vips = new TreeSet<>();
*/
//2.在构造TreeSet或者TreeMap集合的时候给它传一个比较器对象。
TreeSet<Vip> vips = new TreeSet<>(new VipComparator());
}
}
//1.实现Comparable比较接口
class Vip implements Comparable<Vip>{
private String name;
private int age;
@Override
public int compareTo(Vip o) {
//比较规则:先比较年龄顺序,如果年龄相同则比较姓名顺序
//return this.age == o.getAge() ? this.name.compareTo(o.getName()) : this.age - o.getAge();
if (this.age == o.getAge()) {//如果年龄相等,就比较姓名顺序
return this.name.compareTo(o.getName());
} else {
return this.age - o.getAge();
}
}
}
//2.编写Comparator比较器
class VipComparator implements Comparator<Vip> {
@Override
public int compare(Vip o1, Vip o2) {
//先比较年龄顺序,如果年龄相同则比较姓名顺序
if (o1.getAge() == o2.getAge()) {
return o1.getName().compareTo(o2.getName());
} else {
return o1.getAge() - o2.getAge();
}
}
}
Java哈希表
哈希表(Hash table,也叫散列表):是根据关键码值(Key value)而直接进行访问的数据结构。
也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
哈希表(hash table):也称散列表,是存储群体对象的集合类结构。是根据键(Key)而直接访问在内存存储位置的数据结构。也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做散列函数,存放记录的数组称做散列表。
- 关键字(key):key可以是对象本身,也可以是对象的一部分(如某个属性),在一个哈希表里每个关键字都必须是唯一的。
- 值域(value):
- 哈希码(Hash Code):若要将对象存储到Hashtable上,就需要将关键字Key映射到一个整型数据,成为Key的哈希码。
- 项(item):Hash Table 的每一项都有两个域:关键字域(key)和值域(value:存储的对象)。key和value可以是任意Object型对象,但是不能为空。
- 装填因子(Load Factor):
哈希冲突
不同关键字通过相同哈希哈数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。
负载因子调节
负载因子 = 0.75;
为什么负载因是0.75
HashMap的扩容时取决于threshold, 而threshold取决于loadFactor, loadFactor(负载因子)HashMap的默认值是0.75(3/4), hash冲突是符合泊松分布的, 而冲突概率最小的是在7-8之间, 都小于百万分之一了; 所以HashMap.loadFactor选取只要在7-8之间的任意值即可。
哈希表和 java 类集的关系
HashMap 和 HashSet 即 java 中利用哈希表实现的 Map 和 Set
java 中使用的是哈希桶方式解决冲突的
java 会在冲突链表长度大于一定阈值后,将链表转变为搜索树(红黑树)
java 中计算哈希值实际上是调用的类的 hashCode 方法,进行 key 的相等性比较是调用 key 的 equals 方
法。所以如果要用自定义类作为 HashMap 的 key 或者 HashSet 的值,必须覆写 hashCode 和 equals 方
法,而且要做到 equals 相等的对象,hashCode 一定是一致的。