集合
集合
集合框架的概述
- 集合、数组都是对多个数据进行存储(内存层面的存储)操作的结构,简称java容器。
- 数组在存储多个数据方面的特点:
- 一旦初始化以后,其长度就确认了。
- 数组一旦定义好,其元素的类型也就确定了,我们也就只能操作指定类型的数据了。
- 数组在存储多个数据方面的缺点:
- 一旦初始化以后,其长度就不可修改了。
- 数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。
- 获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用。
- 数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。
集合框架
Collection接口:单列集合,用来存储一个一个的对象
- List接口:存储有序的、可重复的数据。---->”动态“数组
- ArrayList:作为List接口的主要实现类;线程不安全,效率高;底层使用Object[] elementData存储
- LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
- Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储
- Set接口:存储无序的、不可重复的数据 ----->数学意义上的集合。
- HashSet:作为Set接口的主要实现类;线程不安全,可以存储null值
- LinkedHashSet:作为HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历,对于频繁的遍历操作,LinkedHashSet效率高于HashSet
- TreeSet:可以按照添加对象的指定属性进行排序。
- HashSet:作为Set接口的主要实现类;线程不安全,可以存储null值
Map接口:双列集合,用来存储一对(key - value)一对的数据
-
HashMap:作为Map的主要实现类:线程不安全,效率高;可以存储null的key和value(底层时数组+链表+红黑树)
-
LinkedHashMap:保证遍历map元素时,可以按照添加的顺序实现遍历。
原因:在原有的HashMap底层结构的基础上,添加了一对指针,指向前一个和后一个元素,对于频繁的遍历操作,此类执行效率高于HashMap。
-
-
TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序。底层使用红黑树
-
Hashtable:作为古老实现类:线程安全,效率低
- Properties:常用来处理配置文件。key和value都是String问题。
Collection接口中的方法
向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals().
package collectionTest;
import org.junit.jupiter.api.Test;
import java.util.*;
public class CollectionTest {
@Test
public void test(){
Collection col1 = new ArrayList();
//add(object e):将元素e添加到集合col1中
col1.add("AA");
col1.add("BB");
col1.add(123);
col1.add(new Date());
//size():获取添加的元素的个数
System.out.println(col1.size());//4
//addAll():
Collection col2 = new ArrayList();
col2.add(456);
col2.add("CC");
col1.addAll(col2);
System.out.println(col1.size());//6
System.out.println(col1);
//clear():清空元素
col1.clear();
//isEmpty():
System.out.println(col1.isEmpty());//true
}
@Test
public void test1(){
Collection col1 = new ArrayList();
col1.add(123);
col1.add(456);
col1.add(new String("Tom"));
col1.add(false);
col1.add(new Person("Jerry",20));
//contains(Object obj):判断当前集合是否包含obj,在判断时会调用所在类的equals()。
boolean contains = col1.contains(123);
System.out.println(contains);//true
System.out.println(col1.contains(new String("Tom")));//true
System.out.println(col1.contains(new Person("Jerry",20)));//true
//containsAll(Collection col2):判断形参col1中的所有元素是否都存在与当前集合中
Collection col2 = Arrays.asList(123,456);
System.out.println(col1.containsAll(col2));//true
}
@Test
public void test2(){
Collection col1 = new ArrayList();
col1.add(123);
col1.add(456);
col1.add(new Person("Jerry",20));
col1.add(new String("Tom"));
col1.add(false);
//remove(Object obj):从当前集合中移除obj元素
col1.remove(123);
System.out.println(col1);
col1.remove(new Person("Jerry",20));
System.out.println(col1);
//removeAll(Collection col2):从当前集合中移除col2中所有的元素
Collection col2 = Arrays.asList(123,456);
col1.removeAll(col2);
System.out.println(col1);
}
@Test
public void test3(){
Collection col1 = new ArrayList();
col1.add(123);
col1.add(456);
col1.add(new Person("Jerry",20));
col1.add(new String("Tom"));
col1.add(false);
Collection col2 = Arrays.asList(123,456,false,3333);
//retainAll(Collection col2):交集:获取当前集合和col2的交集并返回给col1
col1.retainAll(col2);
System.out.println(col1);
System.out.println("=================");
//equals(Collection col3):如果是true,则当前集合和形参集合元素都相同
Collection col3 = new ArrayList();
col3.add(123);
col3.add(456);
col3.add(new Person("Jerry",20));
col3.add(new String("Tom"));
col3.add(false);
Collection col4 = new ArrayList();
col4.add(456);
col4.add(123);
col4.add(new Person("Jerry",20));
col4.add(new String("Tom"));
col4.add(false);
System.out.println(col4.equals(col3));//false
}
@Test
public void test4(){
Collection col1 = new ArrayList();
col1.add(123);
col1.add(456);
col1.add(new Person("Jerry",20));
col1.add(new String("Tom"));
col1.add(false);
//hashCode():返回当前对象的hash值
System.out.println(col1.hashCode());//-1200490100
//toArray():集合--->数组
Object[] arr = col1.toArray();
for (Object i:arr) {
System.out.println(i);
}
//调用Arrays类的静态方法asList();数组--->集合
List<Object> list = Arrays.asList(arr);
System.out.println(list);
//注意
List<int[]> ints = Arrays.asList(new int[]{123, 456});
System.out.println(ints.size());//1
List<Integer> integers = Arrays.asList(new Integer[]{123, 4565, 234});
System.out.println(integers);
}
}
迭代器Iterator的使用
主要用来遍历Collection,不遍历Map。
- 集合元素的遍历操作,使用迭代器Iterator接口
- 内部的方法:hasNext()、next()
package collectionTest;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class IteratorTest {
@Test
public void test(){
Collection col1 = new ArrayList();
col1.add(123);
col1.add(456);
col1.add(new Person("Jerry",20));
col1.add(new String("Tom"));
col1.add(false);
Iterator iterator = col1.iterator();//创建一个迭代器对象
while (iterator.hasNext()){
System.out.println(iterator.next());
}
//错误示范:每执行一次col1.iterator()就创建一个新的迭代器对象
while (col1.iterator().hasNext()){
System.out.println(col1.iterator().next());//死循环输出123
}
}
}
@Test
public void test2(){
Collection col1 = new ArrayList();
col1.add(123);
col1.add(456);
col1.add(new Person("Jerry",20));
col1.add(new String("Tom"));
col1.add(false);
Iterator iterator = col1.iterator();
while (iterator.hasNext()){
Object obj = iterator.next();
if ("Tom".equals(obj)){
iterator.remove();
}
}
Iterator iterator1 = col1.iterator();
while (iterator1.hasNext()){
System.out.println(iterator1.next());
}
}
ArrayList源码分析
ArrayList arrayList = new ArrayList();//底层创建了一个elementData[] {}
arrayList.add(123);//将element[]长度扩容到10
...
arrayList.add(11);//如果此次的添加导致底层elementData数组容量不够,则扩容
/*
默认情况下,扩容为原来容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
结论:建议开发中使用带参的构造器:ArrayList arrayList = new ArrayList(int capacity)
*/
小结:
jdk7中的ArrayList的对象的创建类似于单例模式的饿汉式,而jdk8中的ArrayList的对象的创建类似于单例模式的懒汉式,延迟了数组的创建,节省内存。
LinkedList源码分析
实例化时:
LinkedList linkedList = new LinkedList();
声明Node类型的两个属性默认值为null:
transient Node<E> first;
transient Node<E> last;
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
add操作:
linkedList.add(123);
public boolean add(E e) {
linkLast(e);
return true;
}
当链表里没有元素时,newNode就是last也是first
当有一个元素时,newNode是last,first.next是newNode
当有多个元素时,先把last赋值给之前的last,然后new一个新Node,然后把新Node也就是newNode赋给last。
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
由于是链表所以不用扩容
Vector源码分析
Vector vector = new Vector();//底层创建一个elementData[]大小为10
扩容是扩容到原来的两倍
List接口方法
@Test
public void test(){
ArrayList arrayList = new ArrayList();
arrayList.add(123);
arrayList.add(456);
arrayList.add(new Person("Jerry",20));
arrayList.add(new String("Tom"));
arrayList.add(false);
arrayList.add(3,"ele");
ArrayList arrayList1 = new ArrayList();
arrayList1.add(123);
arrayList1.add(13);
arrayList1.add(3);
arrayList.addAll(arrayList1);
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("==================");
System.out.println(arrayList.get(3));
System.out.println(arrayList.indexOf("ele"));
Object index_3 = arrayList.remove(3);
System.out.println(index_3);
System.out.println("==================");
Iterator iterator1 = arrayList.iterator();
while (iterator1.hasNext()){
System.out.println(iterator1.next());
}
arrayList.set(3,"ele");
System.out.println(arrayList);
System.out.println(arrayList.subList(0, 2));
}
Set接口
HashSet
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
一、
- 无序性:不等同于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定位置。
- 不可重复性:保证添加的元素按照equals()判断时,不能返回true,即相同元素只有一个。
二、添加元素的过程:
向HashSet中添加元素a,首先调用元素a所在类的hashcode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素:
- 如果没有,则添加成功。
- 如果有其他元素或以链表形式存在的多个元素,则比较元素a与其他元素的哈希值:
- 如果哈希值不相同,则元素a添加成功。
- 如果哈希值相同,进而需要调用元素a所在类的equals()方法:
- equals()返回true,则添加失败。
- equals()返回false,则添加成功。
向Set中添加的数据,其所在类一定要重写hashCode()和equals()
- 重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相同的哈希值。
- 重写两个方法的技巧:对象中用作equals()方法比较的值,都应该用来计算哈希值。
@Test
public void test(){
Set set = new HashSet();
set.add(456);
set.add(123);
set.add("AA");
set.add("CC");
set.add(new User("Tom",12));
set.add(new User("Tom",12));
set.add(129);
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
package list;
public class User {
private String name;
private int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
System.out.println("User equals");
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if (age != user.age) return false;
return name != null ? name.equals(user.name) : user.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
}
LinkedHashSet
在添加数据时,每个数据还维护了两个引用,记录此数据的前一个数据和后一个数据。
TreeSet
@Test
public void test(){
TreeSet set = new TreeSet();
set.add(new User("Tom",12));
set.add(new User("See",22));
set.add(new User("Fsdfs",32));
set.add(new User("Jsedr",2));
set.add(new User("Tim",62));
set.add(new User("Tim",42));
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
package list;
public class User implements Comparable{
private String name;
private int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
System.out.println("User equals");
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if (age != user.age) return false;
return name != null ? name.equals(user.name) : user.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
//按照姓名从小到大排列
@Override
public int compareTo(Object o) {
if (o instanceof User) {
User user = (User) o;
//return this.name.compareTo(user.name);
int i = this.name.compareTo(user.name);
if (i!=0){
return i;
}else
return Integer.compare(this.age,user.age);
}else
throw new ClassCastException("输入的类型不匹配");
}
}
自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals().
在定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()。~
@Test
public void test1(){
Comparator comparator = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof User && o2 instanceof User){
User user1 = (User)o1;
User user2 = (User)o2;
return Integer.compare(user1.getAge(),user2.getAge());
}else {
throw new ClassCastException("数据不对");
}
}
};
TreeSet set = new TreeSet(comparator);
set.add(new User("Tom",12));
set.add(new User("See",22));
set.add(new User("Fsdfs",32));
set.add(new User("Jsedr",2));
set.add(new User("Tim",62));
set.add(new User("Tim",42));
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
package list;
public class User implements Comparable{
private String name;
private int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
System.out.println("User equals");
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if (age != user.age) return false;
return name != null ? name.equals(user.name) : user.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
//按照姓名从小到大排列
@Override
public int compareTo(Object o) {
if (o instanceof User) {
User user = (User) o;
//return this.name.compareTo(user.name);
int i = this.name.compareTo(user.name);
if (i!=0){
return i;
}else
return Integer.compare(this.age,user.age);
}else
throw new ClassCastException("输入的类型不匹配");
}
}
排序方式
自然排序:实现Comparable接口
定制排序:实现Comparator接口
Map
- HashMap的底层实现原理?
- HashMap和Hashtable的异同?
- CurrentHashMap与Hashtable的异同?
Map结构:
Map中的key:无序的、不可重复的,使用set存储所有的key。-->(以HashMap为例)key所在的类要重写equals()和)Code()
Map中的value:无序的、可重复的,使用Collention存储所有的value。-->value所在的类要重写equals()
一个键值对:key-value构成了一个Entry对象。
Map中的Entry:无序的、不可重复的,使用set存储所有的entry。
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的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)
如果equals()返回false:此时key1-value1添加成功。
如果equals()返回true:使用value替换value2。
扩容问题,当超出临界值(且要存放的位置非空)时,扩容。默认的扩容方式;扩容为原来容量的2倍,并将原有的数据复制过来。
jdk8与7的不同
-
new HashMap():底层没用创建一个长度为16的数组
-
jdk8底层的数组是:Node[],而非Entry[]
-
首次调用put()方法时,底层创建长度为16的数组
-
jdk7底层结构只用:数组+链表。jdk8中底层结构:数组+链表+红黑树。
当数组的某个索引位置上的元素以链表形式存在的数组个数>8且当前数组的的长度>64时,此时此索引位置上的所有数据改为使用红黑数存储。
LinkedHashMap
元素结构
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;//能够记录添加元素的先后顺序
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
Map接口:常用方法
TreeMap
package map;
import list.User;
import org.junit.jupiter.api.Test;
import java.util.*;
public class TreeMapTest {
/**
* 向TreeMap中添加key-value,要求key必须是由同一个类创建的对象
* 因为要按照key进行排序:自然排序、定制排序
*自然排序
*/
@Test
public void test() {
TreeMap treeMap = new TreeMap();
User user1 = new User("Tom", 24);
User user2 = new User("Jerry", 33);
User user3 = new User("Jack", 23);
User user4 = new User("Marry", 24);
User user5 = new User("Rose", 45);
treeMap.put(user1,11);
treeMap.put(user2,22);
treeMap.put(user3,33);
treeMap.put(user4,44);
treeMap.put(user5,55);
Set set = treeMap.entrySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
Object next = iterator.next();
Map.Entry entry = (Map.Entry) next;
System.out.println(entry.getKey()+"----->"+entry.getValue());
}
}
/**
* 定制排序
*/
@Test
public void test1() {
TreeMap treeMap = new TreeMap(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());
}
throw new RuntimeException("输出的类型不匹配");
}
});
User user1 = new User("Tom", 24);
User user2 = new User("Jerry", 33);
User user3 = new User("Jack", 23);
User user4 = new User("Marry", 24);
User user5 = new User("Rose", 45);
treeMap.put(user1,11);
treeMap.put(user2,22);
treeMap.put(user3,33);
treeMap.put(user4,44);
treeMap.put(user5,55);
Set set = treeMap.entrySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
Object next = iterator.next();
Map.Entry entry = (Map.Entry) next;
System.out.println(entry.getKey()+"----->"+entry.getValue());
}
}
}
package list;
public class User implements Comparable{
private String name;
private int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
System.out.println("User equals");
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if (age != user.age) return false;
return name != null ? name.equals(user.name) : user.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
//按照姓名从小到大排列
@Override
public int compareTo(Object o) {
if (o instanceof User) {
User user = (User) o;
//return this.name.compareTo(user.name);
int i = this.name.compareTo(user.name);
if (i!=0){
return i;
}else
return Integer.compare(this.age,user.age);
}else
throw new ClassCastException("输入的类型不匹配");
}
}
Properties
Properties类是Hashtable的子类,该对象用于处理属性文件
由于属性文件里的key、value都是字符串类型,所以Properties里的key和value都是字符串类型
存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法
package map;
import java.io.FileInputStream;
import java.util.Properties;
public class PropertiesTest {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
FileInputStream fileInputStream = new FileInputStream("jdbc.properties");
properties.load(fileInputStream);
String pro1 = properties.getProperty("name");
String pro2 = properties.getProperty("password");
System.out.println("name=" + pro1 + " , "+"password="+pro2 );
}
}
name=Tom
password=123