1.Java集合框架的概述
为什么使用集合框架?
- 集合、数组都是对多个数据进行存储操作的结构,简称Java容器。
说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt,.jpg,.avi,数据库中)。 - 数组在存储多个数据方面的特点:
①一旦初始化以后,其长度就确定了。
②数组一旦定义好,其元素的类型也就确定了。我们也就只有操作指定类型的数据了
比如:String[] arr;int[] arr1;Object[] arr2; - 数组在存储多个数据方面的缺点:
①一旦初始化以后,其长度就不可修改。
②数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。
③获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用 - 数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。
一张图了解集合框架知识体系:
具体说明集合框架:
/----Collection接口: 单列集合,用来存储一个一个的对象
/----List接口 存储有序的、可重复的数据
/----实现类:ArrayList、LinkedList、Vector
/----Set接口 存储无序的、不可重复的数据
/----实现类:HashSet、LinkedHashSet、TreeSet
/----Map接口:双列集合,用来存储一对(key-value)一对的数据
/----HashMap、LinkdedHashMap、TreeMap、Properties、HashTable
2.Collection接口方法
Collection接口中定义了很多抽象的方法,在开发中会用到。
参考API文档
注意:向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()方法.
说明:(Collection接口的某些常用方法会调用equals()判断元素是否在集合中,比如 boolean contains(Object obj)方法,默认调用的是Object类的equals()方法,比较的是两个对象是否指向同一个地址值。)
用于测试的自定义Person类:
public class Person {
private String name;
private int age;
public Person() {
}
public Person(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 boolean equals(Object o) {
System.out.println("Person类 equals()方法~~~");
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 添加
①boolean add(Object obj)
②boolean addAll(Collection coll1) - 获取有效元素的个数
int size() - 清空集合
void clear() - 是否是空集合
boolean isEmpty()
测试方法代码如下:
@Test
public void test1(){
//1. add(Object obj)向obj中添加数据
Collection coll = new ArrayList();
coll.add(789);//自动装箱
coll.add(Integer.valueOf(123));
coll.add("美美哒");
coll.add(new Person("张三",20));
System.out.println(coll);
//2. addAll(Collection coll1) 并集:将coll1集合元素全部添加到当前集合中
Collection coll1 = new ArrayList();
coll1.add(456);
coll1.add("小伙子");
coll.addAll(coll1);
System.out.println(coll);
//3. size()获得当前集合的大小
int size = coll.size();
System.out.println(size);
System.out.println("--------------");
//4. clear()清空当前集合
coll1.clear();
System.out.println(coll1);
//5. isEmpty()当前集合是否为空
boolean empty = coll1.isEmpty();
System.out.println(empty);
System.out.println("--------------");
System.out.println(coll.isEmpty());
}
- 是否包含某个元素
① boolean contains(Object obj):是通过元素的equals方法来判断是否是同一个对象
② boolean containsAll(Collection coll1): 也是调用元素的equals方法来比较的。拿两个集合的元素挨个比较。
@Test
public void test2() {
Collection coll = new ArrayList();
coll.add(new Integer(123));
coll.add(new String("美美哒"));
coll.add(new Person("张三",20));
coll.add(789);
//1. contains(Object obj) 当前集合中是否包含obj元素
//注意:contains(Object obj)参数obj调用了equals()方法 去集合中一一比较
//可以通过自定义类中的equals()方法得知
Person p = new Person("张三", 20);
boolean contains = coll.contains(p);
System.out.println(contains);
System.out.println("=================");
System.out.println(coll.contains(new String("美美哒")));
Collection coll1 = Arrays.asList(123,new Person("张三",20));
//2. containsAll(Collection coll1) 判断形参coll1中的所有元素是否都存在于当前集合中
boolean b = coll.containsAll(coll1);
System.out.println(b);
}
- 删除
① boolean remove(Object obj) 通过元素的equals方法判断是否是要删除的那个元素。只会删除找到的第一个元素
② boolean removeAll(Collection coll1) 取当前集合的差集
@Test
public void test3() {
Collection coll = new ArrayList();
coll.add(new Integer(123));
coll.add(new Person("张三",20));
coll.add(new String("美美哒"));
coll.add(789);
//3. remove(Object obj)将形参obj从当前集合中移除
//coll第一个元素和 obj元素比较
coll.remove(123);
//查看移除数据后的集合
System.out.println(coll);
System.out.println("========*******=========");
Collection coll2 = Arrays.asList(123, 789);
//4. removeAll(Collection coll1):差集:从当前集合中移除coll2中所有元素 coll2集合没有变化
//removeAll()是当前集合的元素通过调用equals()跟作差集的集合每一个元素比较
coll.removeAll(coll2);
System.out.println(coll);
}
- 取两个集合的交集
boolean retainAll(Collection coll1) 把交集的结果存在当前集合中,不影响coll1 - 集合是否相等
boolean equals(Object obj) 要想返回true,当前集合和形参集合的所有元素都相同
@Test
public void test4() {
Collection coll = new ArrayList();
coll.add(new Integer(123));
coll.add(new Person("张三",20));
coll.add(new String("美美哒"));
coll.add(789);
Collection coll1 = Arrays.asList(123, 789);
//5.retainsAll(Collection coll1):交集 获取当前集合和coll1集合的交集,结果放在当前集合中,不影响coll1集合
coll.retainAll(coll1);
System.out.println(coll);
Collection coll2 = new ArrayList();
coll2.add(new Integer(123));
coll2.add(new Person("张三2",20));
coll2.add(new String("美美哒"));
coll2.add(78910);
//6. equals(Object obj) 要想返回true,当前集合和形参集合的所有元素都相同
boolean equals = coll.equals(coll2);
System.out.println(equals);
}
- 转成对象数组
Object[] toArray() - 获取集合对象的哈希值
hashCode()
@Test
public void test5() {
Collection coll = new ArrayList();
coll.add(new Integer(123));
coll.add(new Person("张三",20));
coll.add(new String("美美哒"));
coll.add(789);
//7. hashCode值:返回当前对象哈希值
System.out.println(coll.hashCode());
//8. 集合 转 数组
Object[] array = coll.toArray();
//遍历
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
//9. 数组 转 集合 调用Arrays的静态方法asList()
List<String> list = Arrays.asList(new String[]{
"aa","bb","cc"});
System.out.println(list);
//常见问题: 如果是使用基本数据类型的数组 数组转换为集合的时候 会把数组中的元素当做一个元素
List<int[]> arr1 = Arrays.asList(new int[]{
1, 2, 3, 4});
System.out.println(arr1.size());//1
List<Integer> arr2 = Arrays.asList(new Integer[]{
1, 2, 3, 4});
System.out.println(arr2.size());//4
}
}
总结:Collection接口中交集、差集、并集方法比较重要。
对于三者的概念不清楚可以查看一个视频 概念。
3.Iterator迭代器接口
- 遍历
iterator() 返回迭代器对象,用于集合遍历
集合元素的遍历操作,使用迭代器Iterator接口。
1.内部的方法:hashNext() 和 next()
2.集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前
3.Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,那么所有实现了Colection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator 接口的对象。
public interface Collection<E> extends Iterable<E> {
Iterator<E> iterator();
}
4.Iterator 仅用于遍历集合。
测试迭代器遍历的方法
@Test
public void test() {
Collection coll = new ArrayList();
coll.add(new Integer(123));
coll.add(new Person("张三",20));
coll.add(new String("美美哒"));
coll.add(789);
//方式一:(不推荐)
Iterator iterator = coll.iterator();
for (int i = 0; i < coll.size(); i++) {
System.out.println(iterator.next());
}
System.out.println("=============");
//创建接口的实例,此时得到一个指针
//如果要遍历两次就需要创建两个接口的实例 因为遍历的第一次后指针在最下面了 创建新的实例后指针在最开始的上面
//方式二:(推荐)
Iterator iterator1 = coll.iterator();
//hasNext() 判断是否有下一个元素
while (iterator1.hasNext()) {
//next() ①指针下移 ②下移以后集合位置上的元素返回
System.out.println(iterator1.next());
}
}
3.1迭代器执行原理
3.2两种迭代器错误写法
测试代码如下:
@Test
public void test2() {
Collection coll = new ArrayList();
coll.add(new Integer(123));
coll.add(new Person("张三",20));
coll.add(new String("美美哒"));
coll.add(789);
//遍历:错误方式一
//直接迭代器对象直接调用next()方法 指针下移,当前元素不等于null,为true
//接着又调用next(),指针下移,返回当前元素,当输出完789后,又执行next()此时指针下移,没有元素会报异常java.util.NoSuchElementException
//Iterator iterator = coll.iterator();
//while (iterator.next() != null) {
// System.out.println(iterator.next());
//}
//错误方式二:
while (coll.iterator().hasNext()) {
System.out.println(coll.iterator().next());
}
}
3.3迭代器中remove方法的使用
remove方法 Iterator里面的默认remove()方法 而不是collection接口中的
public interface Iterator<E> {
/**
* @throws IllegalStateException if the {@code next} method has not
* yet been called, or the {@code remove} method has already
* been called after the last call to the {@code next}
* method
*/
default void remove() {
throw new UnsupportedOperationException("remove");
}
}
上面文档注释的意思如下:
如果还未调用next()或在上一次调用next()方法之后调用了remove方法再次调用 remove就会报IllegalStateException异常
测试代码如下:
@Test
public void test3() {
Collection coll = new ArrayList();
coll.add(new Integer(123));
coll.add(new Person("张三",20));
coll.add(new String("美美哒"));
coll.add(789);
Iterator iterator = coll.iterator();
while (iterator.hasNext()) {
//iterator.remove(); 错误写法
if ("美美哒".equals(iterator.next())){
iterator.remove();
//iterator.remove(); 错误写法
}
}
Iterator iterator1 = coll.iterator();
while (iterator1.hasNext()) {
System.out.println(iterator1.next());
}
}
3.4增强for循环遍历方式
遍历集合的底层调用Iterator完成操作。
语法:
for(集合元素的类型 局部变量 : 集合对象){
//输出局部变量
}
测试代码:
@Test
public void test() {
Collection coll = new ArrayList();
coll.add(new Integer(123));
coll.add(new Person("张三",20));
coll.add(new String("美美哒"));
coll.add(789);
//增强for循环
//for(集合元素的类型 局部变量 : 集合对象)
//内部 还是使用了迭代器
for (Object obj : coll) {
System.out.println(obj);
}
int[] arr = new int[]{
1,2,3,4,5,6};
//for(数组元素的类型 局部变量 : 数组对象)
for (int ele : arr) {
System.out.println(ele);
}
}
小测试:
@Test
public void test2() {
String[] arr = new String[]{
"MM","MM","MM"};
for (int i = 0; i < arr.length; i++) {
arr[i] = "GG";
}
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
问上面打印结果?
GG
GG
GG
如果将循环赋值改为增强for循环,结果又该如何?
for (String s : arr) {
s = "GG";
}
答曰:只是将"GG"赋值给了局部变量s,并没有改变arr数组中的值。因此结果
MM
MM
MM
4.Collection子接口一:List
List接口 存储有序的、可重复的数据
ArrayList 是List接口的主要实现类JDK1.2开始,线程不安全,效率高 底层使用 Object[] elementData存储数据。
LinkedListJDK1.2开始,对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储数据。
Vector 是一个古老的实现类,线程安全JDK1.0开始,效率低,底层使用Object[] elementData存储数据。
4.1ArrayList源码分析(JDK1.8)
1.创建ArrayList对象, Object类型的数组被初始化为默认的空数组。
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
};
transient Object[] elementData;
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
2.当添加第一个元素时,确保内部容量,此时size大小为0 ,也就是说第一次调用add()方法是,底层才创建了一个长度为10的Object[] elementDate数组。
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 这步操作除了确保内部容量,实际上还给数组初始化一个大小
elementData[size++] = e;
return true;
}
此时形参minCapacity的值为1 ,调用ensureExplicitCapacity()方法参数是方法calculateCapacity(elementData, minCapacity)的结果
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
判断Object数组默认容量是否为空 是就返回 最大值为DEFAULT_CAPACITY=10
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
判断形参minCapacity=10减去数组长度是否大于0 是就调用grow()
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
声明了一个oldCapacity变量大小是数组的长度,接着又声明了一个newCapacity变量 变量值是oldCapacity+(oldCapacity>>1)的结果,然后判断newCapacity - minCapacity 是否小于0 如果小于0的 就将形参minCapacity=10 赋值给 变量newCapacity ,最后复制elementData数组,指定新的长度,此时elementData数组对象长度指定为10。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
测试Arrays.copyOf(T[] original, int newLength)方法
Object[] ele = {
};
int count = 10;
//copyOf(T[] original, int newLength)
//复制指定的数组,指定新的长度
ele = Arrays.copyOf(ele,count);
System.out.println(ele.length);//10
结论:建议开发中使用带参的构造器ArrayList list = new ArrayList(int initialCapacity) ;
4.2Vector源码分析
protected Object[] elementData;
protected int capacityIncrement;
public synchronized int size() {
return elementCount;//记录数组长度的变量
}
1.创建Vector对象,底层通过调用了重载的构造器方法创建了长度为10的数组。
简单来说:当创建Vector对象是,底层创建了长度是10的Object[] 数组elementData。
当调用add(), 此次添加导致底层elecmentData数组容量不够,则扩容,默认情况下,扩容为原来2倍,同时将原有数组中数据复制到新的数组中。
public Vector() {
this(10);
}
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
2.添加元素的源码:
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
4.3LinkedList源码分析
LinkedList:双向链表,内部没有声明数组,而是定义了Node类型的first和last,用于记录首末元素、同时,定义内部类Node,作为LinkedList保存数据的基本结构。Node除了保存数据,还定义了两个变量
pre 变量记录前一个元素的位置
next 变量记录下一个元素的位置
源码如下:
transient Node<E> first;
transient Node<E> last;
//1.创建LinkedList对象
public LinkedList() {
}
//2.集合尾部添加元素
public boolean add(E e) {
linkLast(e);
return true;
}
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.next = next;
this.prev = prev;
}
}
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++;
}
4.4List接口常用方法
- void add(int index,Object ele):在index位置插入ele元素
- boolean addAll(int index,Collection eles):从index位置开始将eles中的所有元素添加起来
- Object get(int index):获取指定index位置的元素
- 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位置的子集合
测试方法代码如下:
@Test
public void test() {
ArrayList list = new ArrayList();
list.add(123);
list.add(456);
list.add(new Person("Tom",23));
list.add(456);
//void add(int index,Object ele):在index位置插入ele元素
list.add(1,"小伙子");
System.out.println(list.toString());
//boolean addAll(int index,Collection eles):从index位置开始将eles中的所有元素添加起来
List list1 = Arrays.asList(123, 789);
list.addAll(list1);
System.out.println(list);
System.out.println(list.size());//7
//Object get(int index):获取指定index位置的元素
Object o = list.get(3);
System.out.println(o);
//int indexOf(Object obj):返回obj在集合中首次出现的位置 如果没有找到就返回-1
System.out.println(list.indexOf(456));
//System.out.println(list.indexOf(4564));
//int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
System.out.println(list.lastIndexOf(456));
//Object remove(int index):移除指定index位置的元素,并返回此元素
Object remove = list.remove(6);
System.out.println(remove);
System.out.println(list);
//Object set(int index,Object ele):设置指定index位置的元素为ele
list.set(1,"倍棒儿");
System.out.println(list);
//List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合
List subList = list.subList(2, 6);
System.out.println(subList);
System.out.println(list);
}
4.5List接口遍历
方式一:迭代器
方式二: 增强for循环
方式三: 普通for循环
@Test
public void test2(){
ArrayList list = new ArrayList();
list.add(123);
list.add("虎牙直播");
list.add(456);
list.add("飞哥大英雄");
//遍历集合 方式一:迭代器
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
System.out.println("***********************");
//方式二:增强for循环
for (Object obj : list) {
System.out.println(obj);
}
System.out.println("***********************");
//方式三:普通for循环
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
必须掌握
ArrayList中:
增 add(Object obj)
删 remove(int index) / remove(Object obj)
改 set(int index, Object obj)
查 get(int index)
插 add(int index,Object obj)
长度 size()
遍历: 1.迭代器
2.增强for循环
3.普通for循环
5.Collection子接口二:Set
Set接口:存储无序的、不可重复的数据
HashSet:作为Set接口的主要实现类:线程不安全的:可以存储null值 底层是 数组+链表的结构。LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历。
TreeSet:可以按照添加对象的指定属性,进行排序(所以添加的元素必须是相同类的对象,否则会报异常,下面演示)
一、 Set接口:存储无序的、不可重复的数据
- 无序的:不是随机性,存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的
- 不可重复的:保证添加的元素按照equals()判断时,不能返回true,即:相同的元素只能添加一个
HashSet添加顺序解释:
二、添加元素的过程:以HashSet为例:
我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,
此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组
此位置上是否已经有元素:
如果此位置上没有其他元素,则元素a添加成功 --->情况1
如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
如果hash值不相同,则元素a添加成功。---->情况2
如果hash值相同,进而需要调用元素a所在类的equals()方法:
equals()返回true,元素a添加失败
equals()返回false,元素a添加成功。 ---->情况3
对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上数据以链表的方式存储。
jdk 7:元素a放到数组中,指向原来的元素、
jdk 8:原来的元素在数组中,指向元素a
总结:七上八下
说明:
1.Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法
2.要求:向Set中添加的数据、其所在的类一定要重写hashCode()和equals()
要求:重写的hashCode()和equals()尽可能保持一致性,相等的对象必须具有相等的散列码(哈希值)
重写hashCode()方法的基本原则:
- 在程序运行时,同一个对象多次调用hashCode()方法应该返回相同的值。
- 当两个对象的equals()方法比较返回true时,这两个对象的hashCode()方法的返回值也应该相等。
- 对象中用作equals()方法比较Field,都应该用来计算hashCode值。
User类:
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() {
System.out.println("User hashCode()....");
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
}
HashSet类测试:
@Test
@Test
public void test() {
Set set = new HashSet();
set.add(123);
set.add(456);
set.add(456);
set.add("联盟");
set.add(new User("curry",33));
set.add(new User("curry",33));
set.add("BB");
//遍历
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
测试结果:
set.add(new User(“curry”,33));
可以看到调用add()方法,会调用自定义类的hashCode()方法,当第下一次继续添加时,也会计算哈希值,此时哈希值一样,会调用equals()判断两个对象最终是否相等。
User hashCode()…
User hashCode()…
User equals()…
BB
456
123
联盟
User{name=‘curry’, age=33}
LinkedHashSet类测试:
LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据的前一个数据和后一个数据
优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet
@Test
public void test2(){
Set set = new LinkedHashSet();
set.add(123);
set.add(456);
set.add(456);
set.add("联盟");
set.add(new User("curry",33));
set.add(new User("curry",33));
set.add("BB");
//遍历
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
测试结果:
User hashCode()…
User hashCode()…
User equals()…
123
456
联盟
User{name=‘curry’, age=33}
BB
TreeSet类测试:
1.向TreeSet中添加的数据,要求是相同类的对象。
如果添加的不是相同的类的对象,会报异常 java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
测试代码:
@Test
public void test3() {
TreeSet set = new TreeSet();
set.add("Tom");
set.add(123);
set.add(456);
}
如果添加的是自定义类的对象,此类如果不实现自然排序和定制排序会报异常java.lang.ClassCastException: com.li.demo2.User cannot be cast to java.lang.Comparable
测试代码:
@Test
public void test3() {
TreeSet set = new TreeSet();
set.add(new User("Tom",23));
set.add(new User("Jack",42));
set.add(new User("Curry",12));
set.add(new User("Marry",5));
set.add(new User("Jim",38));
}
正确方式(String实现了Comparable):
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
}
测试代码:
@Test
public void test(){
Set set = new TreeSet();
//对添加的对象进行排序 从小到大顺序
set.add("sdf");
set.add("df");
set.add("zcv");
set.add("wer");
//遍历
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
自定义类的排序
2.两种排序方法:自然排序(实现Comparable接口)和定制排序(Comparator)
3.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals()
4.定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()
测试代码(自然排序):
User类的变动:
public class User implements Comparable {
//属性、无参构造、有参构造、set、get、toString方法省略
//按照姓名从小到大排列,并且年龄也从小到大排列
//按照姓名从大到小排列 return -this.name.compareTo(user.name);
@Override
public int compareTo(Object o) {
if (o instanceof User) {
User user = (User)o;
int result = this.name.compareTo(user.name);
if (result != 0) {
return result;
}else {
return Integer.compare(this.age,user.age);
}
}else {
throw new RuntimeException("输入的类型不匹配");
}
}
}
@Test
public void test2(){
TreeSet set = new TreeSet();
set.add(new User("Tom",23));
set.add(new User("Tom",10));
set.add(new User("Jack",42));
set.add(new User("Curry",12));
set.add(new User("Marry",5));
set.add(new User("Jim",38));
//遍历
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
测试结果:
User{name=‘Curry’, age=12}
User{name=‘Jack’, age=42}
User{name=‘Jim’, age=38}
User{name=‘Marry’, age=5}
User{name=‘Tom’, age=10}
User{name=‘Tom’, age=23}
测试代码(定制排序):
@Test
public void test2(){
//按照年龄从小到大排序
Comparator com = 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(com);
set.add(new User("Tom",23));
set.add(new User("Tom",10));
set.add(new User("Jack",42));
set.add(new User("Curry",12));
set.add(new User("Marry",5));
set.add(new User("Jim",38));
//遍历
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
执行结果:
User{name=‘Marry’, age=5}
User{name=‘Tom’, age=10}
User{name=‘Curry’, age=12}
User{name=‘Tom’, age=23}
User{name=‘Jim’, age=38}
User{name=‘Jack’, age=42}
6.Map接口
Map实现类的说明:
/----Map:双列数据,存储key-value对的数据
/----HashMap:作为主要实现类,线程不安全,效率高,存储null的key和value。
/----LinkedHashMap:作为HashMap的子类,保证在遍历map元素时,可以按照添加的顺序 实现遍历。
原因:在原有的HashMao底层结构基础上,添加了一对指针,指向前一个和后一个元素。
对于频繁的遍历操作,此类执行效率高于HashMap。
/----TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序,底层使用红黑树。
/----Hashtable:作为一个古老实现类,线程安全,效率低,不能存储null的key和value。
/----Properties:常用来处理配置文件。key和value都是String类型 。
HashMap的底层:数组+链表(jdk7之前)
数组+链表+红黑树(jdk8)
Map结构(key-value)的理解:
Map中的key:无序的、不可重复的,使用Set存储所有的key (key所在类要重写equals()和hashCode() 以HashMap为例)
Map中的value:无序的、可重复的,使用Collection存储所有的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添加成功。----情况1
如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:
如果key1的哈希值和已经存在的数据的哈希值都不相同,此时key1-value1添加成功。----情况2
如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2):
如果equals()返回false;此时key1-value1添加成功。----情况3
如果equals()返回true;此时value1替换value2。
说明:关于情况2和情况3,此时key1-value1和原来的数据以链表的方式存储。
在不断的添加过程中,会涉及到扩容问题,默认的扩容方式:扩容为原来容量2倍,并将原有的数据复制过来。
jdk8相较于jdk7在底层实现方面的不同:
1.new HashMap():底层没有创建一个长度为16的数组
2.jdk8底层的数组是:Node[],而非Entry[]
3.首次调用put()方法时,底层创建长度为16的数组
4.jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树
当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,此时此索引位置上的所有数据改为使用红黑树存储。
Map常用方法:
- Object(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中所有数据
测试代码:
@Test
public void test1() {
HashMap map = new HashMap();
//Object(Object key,Object value) 将指定key-value添加到(或修改)当前map对象中
map.put("tom",123);
map.put("curry",456);
map.put("jack",789);
map.put("tom",332);
System.out.println(map);
HashMap map1 = new HashMap();
map.put("tom1",456);
map.put("curry1",247);
map.put("jack1",247);
//void putAll(Map m)将m中的所有key-value对存放到当前map中
map.putAll(map1);
System.out.println(map);
//Object remove(Object key) 移除指定key的key-value对,并返回value
Object value = map.remove("tom");
System.out.println(value);
System.out.println(map);
//void clear() 情况当前map中所有数据
map.clear();
System.out.println(map.size());
System.out.println(map);
}
- Object get(Object key) 获取指定key对应的value
- boolean containsKey(Object key) 是否包含指定的key 如果不存在该key,则返回null
- boolean containsValue(Object value) 是否包含指定的value
- int size() 返回map中key-value对的个数
- boolean isEmpty() 判断当前map是否为空
- boolean equals(Object obj) 判断当前map和参数对象obj是否相等
测试代码:
@Test
public void test2() {
HashMap map = new HashMap();
map.put("tom",123);
map.put("curry",456);
map.put("jack",789);
map.put("tom",332);
map.put("mack",332);
System.out.println(map);
//Object get(Object key) 获取指定key对应的value
Object value = map.get("tom");
System.out.println(value);
//boolean containsKey(Object key) 是否包含指定的key 如果不存在该key,则返回null
boolean key = map.containsKey("curry");
System.out.println(key);
//boolean containsValue(Object value) 是否包含指定的value
boolean value1 = map.containsValue(332);
System.out.println(value1);
//int size() 返回map中key-value对的个数
System.out.println(map.size());
// map.clear();
//boolean isEmpty() 判断当前map是否为空
System.out.println(map.isEmpty());
//boolean equals(Object obj) 判断当前map和参数对象obj是否相等
//判断原理:判断map中每一个key-value对和obj的哈希值和equals是否相等
HashMap map2 = new HashMap();
map2.put("tom",123);
map2.put("curry",456);
map2.put("jack",789);
map2.put("tom",332);
map2.put("mack",332);
System.out.println(map);
System.out.println(map2);
System.out.println(map.equals(map2));
}
- Set keySet() 返回所有key构成的Set集合
- Collection Values() 返回所有value构成的Collection集合
- Set entrySet() 返回所有key-value对构成的Set集合
测试代码:
@Test
public void test4() {
HashMap map = new HashMap();
map.put("tom",123);
map.put("curry",456);
map.put("jack",789);
map.put("tom",332);
map.put("mack",332);
//Set keySet() 返回所有key构成的Set集合
Set keySet = map.keySet();
Iterator iterator = keySet.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
System.out.println();
//Collection Values() 返回所有value构成的Collection集合
Collection value = map.values();
for (Object obj : value) {
System.out.println(obj);
}
System.out.println();
//Set entrySet() 返回所有key-value对构成的Set集合
//遍历方式一:
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()) {
Object obj = iterator1.next();
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey()+ "=" + entry.getValue());
}
System.out.println();
//遍历方式二:
Set set = map.keySet();
Iterator iterator2 = set.iterator();
while (iterator2.hasNext()) {
Object key = iterator2.next();
Object val = map.get(key);
System.out.println(key + "=" +val);
}
}
TreeMap两种添加方式:
向TreeMap中添加key-value,要求key必须是相同类创建的对象
因为要按照key进行排序:自然排序、定制排序
自然排序:
@Test
public void test2(){
TreeMap map = new TreeMap();
map.put(new User("Tom",23),83);
map.put(new User("Jim",35),90);
map.put(new User("Marry",13),96);
map.put(new User("Curry",52),73);
//遍历方式一:
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()) {
Object obj = iterator1.next();
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey()+ "--->" + entry.getValue());
}
}
定制排序:
@Test
public void test(){
TreeMap map = 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());
}else {
throw new RuntimeException("输入的类型不匹配!");
}
}
});
map.put(new User("Tom",23),83);
map.put(new User("Jim",35),90);
map.put(new User("Marry",13),96);
map.put(new User("Curry",52),73);
//遍历方式一:
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()) {
Object obj = iterator1.next();
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey()+ "--->" + entry.getValue());
}
}
Properties处理属性文件:
Properties类是Hashtable的子类,该对象处理属性文件
由于属性文件里的key、value都是字符串类型,所以Properties里的key和value都是字符串类型。
存取数据时,建议使用setProperties(String key,String value)方法和getProperty(String key)方法
创建一个jdbc.properties文件存放用户名和密码
username=lisi
password=123456
public class PropertiesTest {
public static void main(String[] args) {
FileInputStream in = null;
try {
Properties pro = new Properties();
in = new FileInputStream("jdbc.properties");
pro.load(in);//读取配置文件
String username = pro.getProperty("username");
String password = pro.getProperty("password");
System.out.println("username=" + username + " password=" + password);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(null != in){
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
执行结果:
username=lisi password=123456
7.Collections工具类
Collections是一个操作Set、List和Map等集合的工具类
Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作
还提供了对集合对象设置不可变、对集合对象实现同步控制等方法。
- void reverse(List list)反转List中元素的顺序
- void shuffle(List list)对List集合元素进行随机排序
- void sort(List list)根据元素的自然排序对指定List元素按升序排序
- void sort(List list,Comparator comp)根据指定的Comparator产生的顺序对List集合进行排序
- void swap(List list,int i,int j)将指定list集合中的i处元素和j处元素进行交换
//排序操作(均为static方法)
//方法直接修改了集合
@Test
public void test1() {
List list = new ArrayList();
list.add("uiop");
list.add("qwer");
list.add("asdf");
list.add("zxcv");
System.out.println("原集合->"+list);
//void reverse(List list)反转List中元素的顺序
Collections.reverse(list);
System.out.println("反转后的集合->"+list);
//void shuffle(List list)对List集合元素进行随机排序
Collections.shuffle(list);
System.out.println("随机排序->"+list);
//void sort(List list)根据元素的自然排序对指定List元素按升序排序
Collections.sort(list);
System.out.println("自然排序(升序)->"+list);
//void sort(List list,Comparator comp)根据指定的Comparator产生的顺序对List集合进行排序
Collections.sort(list, new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof String && o2 instanceof String) {
String str = (String)o1;
String str2 = (String)o2;
return str.compareTo(str2);
}else {
throw new RuntimeException("输入类型不匹配");
}
}
});
System.out.println("定制排序->"+list);
//void swap(List list,int i,int j)将指定list集合中的i处元素和j处元素进行交换
Collections.swap(list,1,3);
System.out.println(list);
}
- Object max(Collection coll)根据元素的自然顺序,返回给定集合中的最大元素
- Object max(Collection coll,Comparator comp)根据Comparator指定的顺序,返回给定集合中的最大元素
- Object min(Collection coll)
- Object min(Collection coll,Comparator comp)
- int frequency(Collection coll,Object obj) 返回指定集合中指定元素的出现次数
- void copy(List dest,List src) 将src中的内容复制到dest中
- boolean replaceAll(List list,Object oldVal,Object newVal) 使用新值替换List对象的所有旧值
//查找、替换
@Test
public void test2() {
List list = new ArrayList();
list.add("uiop");
list.add("qwer");
list.add("qwer");
list.add("qwer");
list.add("zzzw");
list.add("asdf");
list.add("zxcv");
System.out.println(list);
//Object max(Collection coll)根据元素的自然顺序,返回给定集合中的最大元素
Comparable max = Collections.max(list);
System.out.println(max);
//Object max(Collection coll,Comparator comp)根据Comparator指定的顺序,返回给定集合中的最大元素
Object max1 = Collections.max(list, new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof String && o1 instanceof String) {
String str = (String) o1;
String str2 = (String) o2;
return str.compareTo(str2);
} else {
throw new RuntimeException("输入的类型不匹配");
}
}
});
System.out.println(max1);
//Object min(Collection coll)
//Object min(Collection coll,Comparator comp)
//int frequency(Collection coll,Object obj) 返回指定集合中指定元素的出现次数
int frequencey = Collections.frequency(list, "qwer");
System.out.println(frequencey);
List dest = Arrays.asList(new Object[list.size()]);
System.out.println(dest);
//void copy(List dest,List src) 将src中的内容复制到dest中
//错误写法:抛出异常 Source does not fit in dest
// List list1 = new ArrayList();
// Collections.copy(list1,list);
Collections.copy(dest,list);
System.out.println("list2->"+dest);
//boolean replaceAll(List list,Object oldVal,Object newVal) 使用新值替换List对象的所有旧值
boolean result = Collections.replaceAll(list, "qwer", "1111");
System.out.println(result);
System.out.println(list);
/*
Collections类中提供了多个synchronizedXxx()方法,该方法可使将指定集合包装成多线程同步的集合,
从而可以解决多线程并发访问集合时的线程安全问题
*/
List list1 = Collections.synchronizedList(list);
}
以上就是集合的所有内容,喜欢的小伙伴一键三连吧!
转载:https://blog.csdn.net/lirui1212/article/details/117338042