集合体系概述Java的集合框架是由很多接口、抽象类、具体类组成的
泛型:基本类型不能作为类型参数
参数化类型,也就是说所操作数据类型指定为一个参数
Collection 接口-定义了存取一组对象的方法,其子接口Set和List分别定义了存储方式。
List:链表中元素可以重复出现
List继承了Collection接口,有三个实现的类分别为:
**1
ArraysList
- 数组列表,数据采用数组方式存储,底部由数组实现,查询快,中间增删慢,可以通过get(index)直接获取当前位置的值;add扩容 元素移位; remove可以移除元素.
ArrayList arrlist = new ArrayList();
arrlist.add("2223");
collection.addAll(list,"a","b","c");
collection.sort(list);
ArraysList动态数组当底层满了之后将进行自动扩容,扩容为之前的1.5倍
ArrayList实现了长度可变的数组,在内存中分配连续的空间。遍历元素和随机访问元素的效率比较高
**2.
LinkedList
😗* 底部为双向链表,从头到尾进行查询,速度较慢; 增删快只需要改变指针域,元素在内存中不发生改变
LinkedList采用链表存储方式。插入、删除元素时效率比较高
LinkedList<String> list = new LinkedList<>();
list.add("a");
List接口的循环迭代
**
Vector
: 底部也为数组, 查询快,中间增删慢, 线程安全**
vector也继承了Collection接口
Vector<String> v = new Vector<>();
v.add("a");
System.out.println(v);
Set接口
*继承了Collection接口。 Set中所存储的元素是不重复的,但是是无序的
不可以重复,值没有索引
HashSet:无序 底层使用哈希表+链表+红黑树
TreeSet:有序(按照值(编码)的顺序排序) 底层是红黑树
public static void main(String[] args) {
HashSet<Integer> set=new HashSet<>();
set.add(2);
set.add(1);
set.add(4);
set.add(11);
set.add(23);
set.add(23);
set.add(null);
System.out.println(set);
/*
HashSet添加时如何判断值是否重复
添加时会调用hashCode(),equals()
添加时要比较内容是否相等,既要保证效率,又要保证安全
先调用hashCode()计算出一个哈希值,比较哈希值非常快,但是不安全
当哈希值相同时,再调用equals()方法比较
*/
int h = "通话".hashCode();
int h1 = "重地".hashCode();
System.out.println(h+"::::"+h1);
System.out.println(h==h1);
HashSet<String> set1 = new HashSet<>();
set1.add("a");
set1.add("通话");
set1.add("重地");
set1.add("a");
set1.add("c");
set1.add("b");
System.out.println(set1);
}
}
public static void main(String[] args) {
HashSet<Student> set = new HashSet<>();
/*
我们的Student类中没有重写hashCode()和equals(),会调用Object中的方法
*/
Student s1 = new Student("jim1", 21);
Student s2 = new Student("jim2", 22);
Student s3 = new Student("jim3", 23);
Student s4 = new Student("jim4", 24);
Student s5 = new Student("jim2", 22);
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s4);
set.add(s5);
System.out.println(set.size());
//"s".hashCode();//字符串.Integer....这些类重写了hashCode()都是根据对象中包含的内容来计算哈希值
}
}
public static void main(String[] args) {
HashSet<Integer> set=new HashSet<>();
set.add(2);
set.add(1);
set.add(4);
set.add(11);
set.add(23);
set.add(23);
/*
增强for
*/
/*for(Integer it : set){
System.out.println(it);
}*/
/*
listIterator()和iterator()区别
listIterator()只能遍历实现了List接口的类
iterator() 遍历list,set
*/
Iterator<Integer> it = set.iterator();
while(it.hasNext()){
Integer n = it.next();
System.out.println(n);
// it.remove();
}
/*
Stream
*/
Stream<Integer> stream = set.stream();
stream.forEach(new Consumer<Integer>(){
@Override
public void accept(Integer t) {
System.out.println(t);
}
});
}
}
Map集合 双列存储Key键不能重复,每个键只能映射一个值
Map<String, String> map = new HashMap<>();
map.put("a","a");
System.out.println(map.isEmpty());//判断map是否为空
System.out.println(map.containsKey("a"));//输出主键为a的map数组的值
System.out.println(map.containsValue("c"));//输出值为c的主键
System.out.println(map.size());//输出map数组的长度
Map集合遍历
方式1:根据键找值
•获取所有键的集合
•遍历键的集合,获取到每一个键
•根据键找值
方式2:根据键值对对象找键和值
•获取所有键值对对象的集合
•遍历键值对对象的集合,获取到每一个键值对对象
•根据键值对对象找键和值
HashMap
HashMap中元素的key值不能重复, 排列顺序是不固定的,可以存储一个为null的键
TreeMap
TreeMap中所有的元素都保持着某种固定的顺序,如果需要得到一个有序的Map就应该使用TreeMap,key值所在类必须实现Comparable接口, 重写compareTo方法。TreeMap根据compareTo的逻辑,对 key进行排序。键是红黑树结构,可以保证键的排序和唯一性
public class TreeMapDemo {
public static void main(String[] args) {
TreeMap<String, String> tmap = new TreeMap<>();
tmap.put("b", "b");
tmap.put("a", "a");
tmap.put("d", "d");
tmap.put("c", "c");
tmap.put("c", "c");
System.out.println(tmap);
}
}
HashTable
实现了同步。但其中 不能存储为null的键
public class HashtableDemo {
public static void main(String[] args) {
Hashtable<String, String> htable = new Hashtable<>();
htable.put("a", "a");
htable.put("b", "a");
htable.put("c", "a");
System.out.println(htable);
}
}
哈希表+链表+红黑树哈希表默认长度16负载因子为0.75 扩容时为原来的2倍链表长度为8时转化为红黑树
练习习题
public class demo3 {
/* 有2个数组,第一个数组内容为:[黑龙江省,浙江省,江西省,广东省,福建省],第二个数组为:[哈尔滨,杭州,南昌,广州,福州],
将第一个数组元素作为key,第二个数组元素作为value存储到Map集合中。如{黑龙江省=哈尔滨, 浙江省=杭州, …}。*/
public static void main(String[] args) {
String []src1={"黑龙江省","浙江省","江西省","广东省","福建省"};
String []src2={"哈尔滨","杭州","南昌","广州","福州"};
HashMap<String,String>map=new HashMap<>();
for (int i=0;i<=4;i++){
map.put(src1[i],src2[i] );
}
System.out.println(map);
}
}
//通过输入年份判断那一年中是否有比赛,有的话输出国家名称
//通过输入国家名称,输出该国家在哪几年有过冠军
public class demo1 {
public static void main(String[] args) {
HashMap<Integer, String> map = new HashMap<>();
map.put(1930, "乌拉圭");
map.put(1934, "意大利");
map.put(1938, "意大利");
map.put(1950, "乌拉圭");
map.put(1954, "西德");
map.put(1958, "巴西");
map.put(1962, "巴西");
map.put(1966, "英格兰");
map.put(1970, "巴西");
map.put(1974, "西德");
map.put(1978, "阿根廷");
map.put(1982, "意大利");
map.put(1986, "阿根廷");
map.put(1990, "西德");
map.put(1994, "巴西");
map.put(1998, "西德");
map.put(2002, "巴西");
map.put(2006, "意大利");
map.put(2010, "西班牙");
map.put(2014, "德国");
map.put(2018, "法国");
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个年份");
int a = sc.nextInt();
if (map.get(a) == null) {
System.out.println("这年没有世界杯");
}
{
System.out.println("获得冠军的队伍为" + map.get(a));
}
Scanner sc1 = new Scanner(System.in);
System.out.println("请输入一个国家查看大力神杯的年份:");
String b=sc1.next();
System.out.println("年份是");
for(Integer key: map.keySet()){
if(b.equals(map.get(key))){
System.out.print(key+",");
}
}
}
}
巩固拓展练习
判读正误 :
1. ConcurrentHashMap使用synchronized关键字保证线程安全
错误,JDK1.8 的 ConcurrentHashMap 采用CAS+Synchronized保证线程安全。 JDK1.7及以前采用segment的分段锁机制实现线程安全,其中segment继承自ReentrantLock,因此采用Lock锁来保证线程安全。
:
2.HashMap实现了Collction接口
*错误,**由代码可知
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
下面程序运行完之后,t2与t3的关系为()
Object obj = new Object();
List aList = new ArrayList();
List bList = new LinkedList();
long t1 = System.currentTimeMillis();
for(int i = 0;i<50000;i++)
{
aList.add(0, obj);
}
long t2 = System.currentTimeMillis() - t1;
t1=System.currentTimeMillis(); for(
int i = 0;
i<50000;i++)
{
bList.add(0, obj);
}
long t3 = System.currentTimeMillis() - t1;
答案: t2>t3
ArrayList内部是动态数组实现,在增加空间时会复制全部数据到新的容量大一些的数组中。而LinkedList内部为双向链表,可以按需分配空间,扩展容量简单,因此LinkedList用时少。