Collections:操作Collection、Map的工具类
面试题:Collection 和 Collections 的区别?
public class CollectionsTest {
//发现一个不知对错的规律:如果方法是void没有返回值,那么这个方法的举措是在原数组上执行的,而不是新建一个数组执行,因而改变原数组
/*
reverse(List):反转List中元素的顺序
shuffle(List):对List集合元素进行随机排序
sort(List):根据元素的自然排序对指定List集合元素按升序排序
sort(List,Comparator):根据指定的Comparator产生的顺序对List集合元素进行排序
swap(List, int ,int):将指定List集合中的i处元素和j处元素进行交换
Object max(Collection):根据元素的自然排序,返回给定集合中的最大元素
Object max(Collection,Comparator):根据Comparator指定的顺序,返回给定集合中的最大元素
Object min(Collection)
Object min(Collection,Comparator)
int frequency(Collection,Object):返回指定集合中指定元素的出现次数
void copy(List dest,List src):将src中的内容复制到dest中
boolean replaceAll(List list,Object oldAll,Object newVal):使用新值替换List对象的所有旧值
*/
@Test
public void test2(){
List list = new ArrayList();
list.add(123);
list.add(43);
list.add(765);
list.add(-97);
list.add(0);
//错误写法报异常:IndexOutOfBoundsException: Source does not fit in dest
// List dest = new ArrayList();先不初始化目的集合的容量,会报下标越界的错误
// Collections.copy(dest,list);
List dest = Arrays.asList(new Object[list.size()]);
System.out.println(dest.size());list.size();
Collections.copy(dest,list);
System.out.println(dest);
/*
Collections 类中提供了多个 synchronizedXxx()方法,
该方法可使将指定集合包装成线程同步的集合,从而解决多线程并发访问集合时的线程安全问题
*/
//返回的list1即为线程安全的List
List list1 = Collections.synchronizedList(list);
}
@Test
public void test1(){
List list = new ArrayList();
list.add(123);
list.add(43);
list.add(765);
list.add(765);
list.add(-97);
list.add(0);
System.out.println(list);
Collections.reverse(list);
System.out.println(list);
Collections.shuffle(list);
System.out.println(list);
Collections.sort(list);
System.out.println(list);
Collections.swap(list,1,2);
System.out.println(list);
int frequency = Collections.frequency(list,765);
System.out.println(frequency);//2
}
}
一、集合框架的概述
* 1.集合、数组都是对多个数据进行存储操作的结构,简称Java容器
* 说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt .jpg .avi 数据库中)
*
*2.1 数组在存储多个数据方面的特点:
* >一旦初始化以后,其长度就确定了
* >数组一旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据了
* 比如:String[] arr; int[] arr1; Object[] arr2;
*2.2 数组在存储多个数据方面的缺点:
* >一旦初始化以后,其长度就不可修改
* >数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高
* >获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用
* >数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足
*
* 二、集合框架
* |----Collection接口:单列集合,用来存储一个一个的对象
* |----List接口:存储有序的、可重复的数据。 --->“动态”数组
* |----ArrayList、LinkedList、Vector
*
* |----Set接口:存储无序的、不可重复的数据 --->高中讲的"集合"
* |----HashSet、LinkedHashSet、TreeSet
*
* |----Map接口:双列集合,用来存储一对(key - value)一对的数据 --->高中函数:y=f(x)
* |----HashMap、LinkedHashMap、TreeMap、Hashtable、Properties
*
* 三、Collection接口中的方法的使用
public class CollectionTest {
@Test
public void test1(){
Collection coll = new ArrayList();
//add(Object e):将元素e添加到集合coll中
coll.add("AA");
coll.add("BB");
coll.add(123);//自动装箱
coll.add(new Date());
//size():获取添加的元素的个数
System.out.println(coll.size());//4
//addAll(Collection coll1):将coll1集合中的元素添加到当前集合中
Collection coll1 = new ArrayList();
coll1.add(456);
coll1.add("cc");
coll.addAll(coll1);
System.out.println(coll.size());//6
System.out.println(coll);//相当于coll.toString()
//isEmpty():判断当前集合是否为空
System.out.println(coll.isEmpty());//false
//clear():清空集合元素
coll.clear();
//此操作完成后,isEmpty()为true
}
}
Collection接口中声明的方法的测试
* 结论:
* 向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()
public class CollectionTest1 {
@Test
public void test1() {
Collection coll = new ArrayList();//以ArrayList作为collcetion接口的实现类
coll.add(123);
coll.add(456);
coll.add(new String("Tom"));
coll.add(false);
coll.add(new Person("Jerry", 20));
//contains(Object obj):判断当前集合是否包含obj
//我们在判断时会调用obj对象所在类的equals()
boolean contains = coll.contains(123);
System.out.println(contains);//true
System.out.println(coll.contains(new String("Tom")));//true
System.out.println(coll.contains(new Person("Jerry", 20)));//false---->true(重写equals)
//这里new Person是自定义类调用equals(),如果不重写则返回false
//containsAll(Collection coll1):判断形参coll1中的所有元素是否都存在于当前集合中
Collection coll1 = Arrays.asList(123, 456);//也可以用这种方法
System.out.println(coll.containsAll(coll1));//true
}
@Test
public void test2() {
Collection coll = new ArrayList();//以ArrayList作为collcetion接口的实现类
coll.add(123);
coll.add(456);
coll.add(new String("Tom"));
coll.add(new Person("Jerry", 20));
coll.add(false);
//remove(Object obj):从当前集合中移除obj元素
coll.remove(123);//remove()方法是boolean类型返回值,删除成功则返回true
//remove(123),首先要判断coll里面有没有123(涉及contains),所以要调用equals(),又是比较内容是否相等
//则要重写equals(),但这里的123是Interage包装类,包装类本身是自己重写equals()的,因此不用手动重写
System.out.println(coll);//true
coll.remove(new Person("Jerry", 20));
System.out.println(coll);
//removeAll(Collection coll1):差集: 从当前集合中移除coll1中所有元素
Collection coll1 = Arrays.asList(123, 456);
coll.removeAll(coll1);//直接在coll上做出改动
System.out.println(coll1);
}
@Test
public void test3() {
Collection coll = new ArrayList();//以ArrayList作为collcetion接口的实现类
coll.add(123);
coll.add(456);
coll.add(new String("Tom"));
coll.add(new Person("Jerry", 20));
coll.add(false);
// //retainAll(Collection coll1):交集:获取当前集合和coll1集合的交集,并返回给当前集合
// Collection coll1 = Arrays.asList(123, 456, 789);
// coll.retainAll(coll1);
// System.out.println(coll);
//equals(Object obj):要想返回true,需要当前集合和形参集合的元素都相同
Collection coll2 = new ArrayList();//以ArrayList作为collcetion接口的实现类
coll2.add(123);
coll2.add(456);
// coll2.add(456);
// coll2.add(123);
//如果添加的元素一样,顺序不一样,equals会false,因为跟ArrayList的有序性有关
coll2.add(new String("Tom"));
coll2.add(new Person("Jerry", 20));
coll2.add(false);
System.out.println(coll.equals(coll2));//如果上边的retainAll执行了,coll里的元素就不一样了,所以才要注释掉
}
@Test
public void test4(){
Collection coll = new ArrayList();//以ArrayList作为collcetion接口的实现类
coll.add(123);
coll.add(456);
coll.add(new String("Tom"));
coll.add(new Person("Jerry", 20));
coll.add(false);
//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 list = Arrays.asList(new String[]{"AA", "BB","CC"});
// List list1 = Arrays.asList("AA", "BB","CC");这样写就可以了
System.out.println(list);
List arr1 = Arrays.asList(new int[]{123,456});
System.out.println(arr1.size());//1 这样写会识别为只有一个元素
List arr2 = Arrays.asList(new Integer[]{123,456});
System.out.println(arr2.size());//2
//iterator():返回Iterator接口的实例,用于遍历集合元素。放在IteratorTest.java中测试
}
}
集合元素的遍历,使用迭代器Iterator接口
* 1.内部的方法:hasNext()和 next() * 2.集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前 * 3.内部定义了一个remove(),可以在遍历的时候,删除集合中的元素。此方法不同于集合直接调用remove()
public class IteratorTest {
@Test
public void test1(){
Collection coll = new ArrayList();//以ArrayList作为collcetion接口的实现类
coll.add(123);
coll.add(456);
coll.add(new String("Tom"));
coll.add(new Person("Jerry", 20));
coll.add(false);
Iterator iterator = coll.iterator();
// //方式一
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
//方式二 不推荐
// for (int i = 0; i <coll.size() ; i++) {
// System.out.println(iterator.next());
// }
//方式三 推荐
while(iterator.hasNext()){ //hasNext()判断是否还有下一个元素
//next():①指针下移②将下移以后集合位置上的元素返回
System.out.println(iterator.next());
}
// //错误方式一:
// Iterator iterator1 = coll.iterator();
// while ((iterator1.next())!=null){
// System.out.println(iterator.next());
//
// }
// //错误方式二
// while (coll.iterator().hasNext()){
// System.out.println(coll.iterator().next());
// }
}
//测试Iterator中的remove()
//如果还未调用next()或在上一次调用next方法之后已经调用了remove方法,再调用remove会报IllegalStateException
@Test
public void test3(){
Collection coll = new ArrayList();//以ArrayList作为collcetion接口的实现类
coll.add(123);
coll.add(456);
coll.add(new String("Tom"));
coll.add(new Person("Jerry", 20));
coll.add(false);
//删除集合中“Tom”
Iterator iterator = coll.iterator();
while (iterator.hasNext()){
Object obj = iterator.next();
if("Tom".equals(obj)){
iterator.remove();
}
}
//重新遍历集合
iterator=coll.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
* |----Collection接口:单列集合,用来存储一个一个的对象
* |----List接口:存储有序的、可重复的数据。 --->“动态”数组(不用定义数组大小),替换原有数组
* |----ArrayList:作为List接口的主要实现类;线程不安全,效率高;底层使用Object[]存储
* |----LinkedList:对于频繁的插入、删除操作,使用此类操作效率比ArrayList高;底层使用双向链表存储
* |----Vector:作为List接口的古老实现类;线程安全,效率低;底层使用Object[]存储
*
*面试题:ArrayList、LinkedList、Vector三者的异同?
* 同:三个类都实现了List接口,存储数据的特点相同:存储有序的、可重复的数据
* 不同:见上
* List接口中的常用方法
public class ListTest {
/*
void add(int index, Object ele):在indx位置插入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位置的左闭右开子集合
总结:常用方法
增 add(Object obj)
删 remove(int index) / remove(Object obj)
改 set(int index, Object ele)
查 get(int index)
插 add(int index, Object ele)
长度 size()
遍历 ①Iterator迭代器方式
②增强for循环
③普通循环
*/
@Test
public void test3(){
ArrayList list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
//方式一:Iterator迭代器
Iterator list1 = list.iterator();
while (list1.hasNext()){
System.out.println(list1.next());
}
//方式二:
for(Object obj : list){//为什么是Object类型?因为ArrayList的底层就是Object[] elementData
System.out.println(obj);
}
//方式三
for (int i = 0; i <list.size() ; i++) {
System.out.println(list.get(i));
}
}
@Test
public void test2(){
ArrayList list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
list.add(new Person("Tom",12));
list.add(456);
//int indexOf(Object obj):返回obj在集合中首次出现的位置
int index = list.indexOf(456);
System.out.println(index);//1 不存在则返回-1
// int lastIndexOf(Object obj):返回obj在集合中末次出现的位置
int index1 = list.lastIndexOf(456);
System.out.println(index1);//4
// Object remove(int index):移除指定index位置的元素,并返回此元素
Object obj = list.remove(1);
System.out.println(obj);//456
System.out.println(list);//[123, AA, Person{name='Tom', age=12}, 456]
// Object set(int index, Object ele):设置指定index位置的元素为ele
System.out.println(list);//[123, AA, Person{name='Tom', age=12}, 456]
list.set(1,"CC");
System.out.println(list);//[123, CC, Person{name='Tom', age=12}, 456]
// List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
List subList = list.subList(2, 4);
System.out.println(subList);//[Person{name='Tom', age=12}, 456]
System.out.println(list);
}
@Test
public void test1(){
ArrayList list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
list.add(new Person("Tom",12));
list.add(456);
System.out.println(list);//[123, 456, AA, Person{name='Tom', age=12}, 456]
//void add(int index, Object ele):在indx位置插入ele元素
list.add(1,"BB");
System.out.println(list);//[123, BB, 456, AA, Person{name='Tom', age=12}, 456]
//boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
List list1 = Arrays.asList(1, 2, 3);//Collection eles
list.addAll(2,list1);//也可以不写index,则默认将list1加到最后面
System.out.println(list);//[123, BB, 1, 2, 3, 456, AA, Person{name='Tom', age=12}, 456]
System.out.println(list.size());//6+3
// list.add(list1);//把list1里面的元素识别成1个了
// System.out.println(list.size());//6+1
// Object get(int index):获取指定index位置的元素
System.out.println(list.get(1));
}
}
一、Map的实现类的结构
* |---Map:双列数据,用于存储key-value对的数据 ---类似于高中的函数:y=f(x)
* |---HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value
* (HashMap的子类)|---LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历
* 原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素
* 对于频繁的遍历操作,此类执行效率高于HashMap
* |---TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序
* 底层使用红黑树
* |---Hashtable:作为古老的实现类;线程安全,效率低;不能存储null的key和value
* |---Properties:常用来处理配置文件。key和value都是String类型
*
*
*
* HashMap的底层:数组+链表(jdk7及之前)
* 数组+链表+红黑树(jdk8)
*
* 面试题:
* 1.HashMap的底层实现原理?
* 2.HashMap和Hashtable的异同?
* 3。CurrentHashMap和Hashtable的异同?
*
* 二、Map结构的理解
* 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.jdk底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树
* 当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,
* 此时此索引位置上的所有数据改为使用红黑树存储
*
*
* DEFAULT_INITIAL_CAPACITY:HashMap的默认容量,16
* DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75
* threshold:扩容的临界值=容量*填充因子=16*0.75=>12
* TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8
* MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64
*
* 四、LinkHashMap的底层实现原理(理解)
*
* 五、Map中定义的方法:
* 添加、删除、修改操作:
* Object put(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中的所有数据
* 元素查询的操作:
* Object get(Object key):获取指定key对应的value
* boolean containsKey(Object key):是否包含指定的key
* boolean containsValue(Object value):是否包含指定的value
* int size():返回map中key-value对的个数
* boolean isEmpty():判断当前map是否为空
* boolean equals(Object obj):判断当前map和参数对象obj是否相等
* 元视图操作方法:
* Set keySet():返回所有key构成的Set集合
* Collection values():返回所有value构成的Collection集合
* Set entrySet():返回所有key-value对构成的Set集合
*
*
* 总结:常用方法:
* 添加:put(Object key,Object value)
* 删除:remove(Object key)
* 修改:put(Object key,Object value)
* 查询:get(Object key)
* 长度:size()
* 遍历:keySet()、values()、entrySet()
public class MapTest {
/*
元视图操作方法:
* Set keySet():返回所有key构成的Set集合
* Collection values():返回所有value构成的Collection集合
* Set entrySet():返回所有key-value对构成的Set集合
*/
@Test
public void test4(){
HashMap map = new HashMap();
map.put("AA",123);
map.put(45,1234);
map.put("BB",56);
//遍历所有的key集:keySet()
Set set = map.keySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
//遍历所有的value集:values()
Collection values = map.values();
Iterator iterator1 = values.iterator();
//也可以用增强for
while (iterator1.hasNext()){
System.out.println(iterator1.next());
}
遍历所有的key-value
//方式一:entrySet()
Set entrySet = map.entrySet();
Iterator iterator2 = entrySet.iterator();
while (iterator2.hasNext()){
Object obj = iterator2.next();
//entrySet集合中的元素都是entry
Map.Entry entry = (Map.Entry)obj;
System.out.println(entry.getKey() + "--->" + entry.getValue());
}
System.out.println();
//方式二:
Set keySet = map.keySet();
Iterator iterator3 = set.iterator();
while (iterator3.hasNext()){
Object key = iterator3.next();
Object value = map.get(key);
System.out.println(key + "--->" + value);
}
}
/*
元素查询的操作:
* Object get(Object key):获取指定key对应的value
* boolean containsKey(Object key):是否包含指定的key
* boolean containsValue(Object value):是否包含指定的value
* int size():返回map中key-value对的个数
* boolean isEmpty():判断当前map是否为空
* boolean equals(Object obj):判断当前map和参数对象obj是否相等
*/
@Test
public void test3() {
HashMap map = new HashMap();
map.put("AA",123);
map.put(45,123);
map.put("BB",56);
// Object get(Object key):获取指定key对应的value
System.out.println(map.get("AA"));//123
// boolean containsKey(Object key):是否包含指定的key
System.out.println(map.containsKey(45));//true
// boolean containsValue(Object value):是否包含指定的value
System.out.println(map.containsValue(123));//true
// int size():返回map中key-value对的个数
int size = map.size();
System.out.println(size);//3
// boolean isEmpty():判断当前map是否为空
System.out.println(map.isEmpty());//false
// boolean equals(Object obj):判断当前map和参数对象obj是否相等
HashMap map1 = new HashMap();
map1.put("AA",123);
map1.put(45,123);
map1.put("BB",56);
System.out.println(map.equals(map1));//true
}
/*
添加、删除、修改操作:
* Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
* void putAll(Map m):将m中的所有key-value对存放到当前map中
* Object remove(Object key):移除指定key的key-value对,并返回value
*/
@Test
public void test2(){
HashMap map = new HashMap();
//添加
map.put("AA",123);
map.put(45,123);
map.put("BB",56);
//修改
map.put("AA",87);
System.out.println(map);//{AA=87, BB=56, 45=123}
HashMap map1 = new HashMap();
map1.put("CC",123);
map1.put("DD",123);
map.putAll(map1);
System.out.println(map);//{AA=87, BB=56, CC=123, DD=123, 45=123}
//remove(Object key)
Object value = map.remove("CC");
//如果remove的key不存在,返回的value是null
System.out.println(value);//123
System.out.println(map);//{AA=87, BB=56, DD=123, 45=123}
//clear()
map.clear();//与map=null;操作不同
System.out.println(map.size());//0
System.out.println(map);//{}
}
@Test
public void test1(){
HashMap map = new HashMap();
map.put(null,123);
//Hashtable中不能存储null
}
}
* 1.Set接口的框架
* |----Collection接口:单列集合,用来存储一个一个的对象
* * |----Set接口:存储无序的、不可重复的数据 --->高中讲的"集合"
* * |----HashSet:作为Set接口的主要实现类;线程不安全;可以存储null值
* |----LinkedHashSet:HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历
* 对于频繁的遍历操作:LinkedHashSet效率高于HashSet
* |----TreeSet:可以按照添加的对象的指定属性,进行排序。
*
*1.Set接口中没有额外定义的新方法,使用的都是Collection中声明过的方法
*
*2.要求:向Set中添加的数据,其所在类一定要重写hashCode()和equals()
* 要求:重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码
* 重写两个方法的小技巧:对象中用作equals()方法比较的Field,都应该用来计算hasCode值
一、Set:存储无序的、不可重复的数据
以HashSet为例说明: 1.无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加。而是根据数据的哈希值决定的 2.不可重复性:保证添加的元素按照equals()判断时,不能返回true。即:相同的元素只能添加一个 二、添加元素的过程:以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 总结:七上八下 HashSet底层:数组+链表的结构(前提 jdk7)
public class SetTest {
@Test
public void test1() {
Set set = new HashSet();
set.add(465);
set.add(123);
set.add("AA");
set.add("CC");
set.add(new User("Tom", 12));
set.add(129);
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
// AA
// CC
// 465
// 129
// 123
// Person{name='Tom', age=12}
}
}
//LinkedHashSet的使用
//LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据
//优点:对于频繁的遍历操作:LinkedHashSet效率高于HashSet
@Test
public void test2() {
Set set = new LinkedHashSet();
set.add(465);
set.add(123);
set.add("AA");
set.add("CC");
set.add(new User("Tom", 12));
set.add(129);
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}