容器框架
数组
数组是相同类型数据的有序集合:
1、相同类型的若干个数据,按照一定先后次序排列组合而成。
2、 其中,每一个数据称作一个数组元素
3、每个数组元素可以通过一个下标来访问它们.
数组的特点:
1、 其长度是确定的。数组一旦被创建,它的大小就是不可以改变的。
2、 其元素必须是相同类型,不允许出现混合类型
3、 数组中的元素可以是任何数据类型,包括基本类型和引用类型。
数组的创建
int[] s = new int[10];
//创建并初始化
int[] a = {1,2,3,4,5};
二维数组
二维数组可以看成以数组为元素的数组
二维数组的创建
int[][] buf = new int[10][10];
int[][] buf = new int[10][];
int[][] buf = new int[][10];//非法
//创建并初始化
int intA[][] = {{1,2},{2,3},{3,4,5}};
数组的拷贝:
调用java.lang.System的静态方法
//可以用于数组src从第srcPos项元素开始的length个元素拷贝到目标数组从destPos项开始 的length个位置
public static void arraycopy (Object src,int srcPos,Object dest, int destPos,int length)
Arrays类
常用操作
@Test
void arrTest() {
//以字符串形式输出
int[] buf = {1,3,2,5,4};
System.out.println(Arrays.toString(buf));//[1, 3, 2, 5, 4]
//排序
//Jdk1.8中源码是数据量大采用快排,数据少采用插入排序
//sort适用于20w以下的数据
//parallelSort适用于大量数据的排序
Arrays.sort(buf);
System.out.println(Arrays.toString(buf));//[1, 2, 3, 4, 5]
int[] tmp = new int[5];
System.arraycopy(buf, 0, tmp, 0, 5);
System.out.println(Arrays.toString(tmp));//[1, 2, 3, 4, 5]
System.out.println(tmp.toString());//默认toString输出的是tmp的hash值
//二分查找
System.out.println(Arrays.binarySearch(buf, 3));//2
}
关于Arrays还有一些比较少使用的方法可以翻一下文档
JAVA中的数组跟其他语言定义概念基本一样
总结:
数组的特点:
长度固定,连续空间,存储同一种类型数据
for-each循环:简单、主要用于遍历操作
优点:按照索引查询效率高
缺点:添加删除元素效率低;按照内容查询效率低(无序)
集合
当我们需要将一些相同结构的个体整合在一起时,就可以考虑使用集合了
集合和数组相似点:
都可以存储多个对象,对外作为一个整体存在
数组的缺点:
1、长度必须在初始化时指定,且固定不变
2、数组采用连续存储空间,删除和添加效率低下
3、数组无法直接保存映射关系
4、数组缺乏封装,操作繁琐
关于集合的架构:
List
特点:有序 不唯一(可重复)
ArrayList 线性表中的顺序表 :
1、 在内存中分配连续的空间,实现了长度可变的数组
2、 优点:遍历元素和随机访问元素的效率比较高
3、 缺点:添加和删除需大量移动元素效率低,按照内容查询效率低
LinkedList 线性表中双向链表 :
1、采用双向链表存储方式。
2、 缺点:遍历和随机访问元素效率低下
3、优点:插入、删除元素效率比较高
ArrayList基本操作
@Test
void ListTest() {
List<Integer> arrlist = new ArrayList<>();
arrlist.add(1);
arrlist.add(3);
//必须是顺序插入
arrlist.add(2,5);
if(arrlist.contains(3)) {
System.out.println("Yes");
}
System.out.println(arrlist.get(1));
System.out.println(arrlist.get(2));
arrlist.remove(Integer.valueOf(5));
System.out.println(arrlist.toString());//[1, 3]
//根据索引更改
arrlist.set(1, 2);
System.out.println(arrlist.toString());//[1, 2]
//链表转换成数组
Object[] arr = arrlist.toArray();
System.out.println(arr[0]);
}
ArrayList虽然常用,但是关于ArrayList的坑还是不少的
通过Arrays的asList转换成List集合,需要注意的坑(巨坑)
@Test
void ListTest() {
String[] arr = {"hello","java"};
List<String> list = Arrays.asList(arr);
list.add("Hello world");
}
简单地add操作,报了一个奇怪的异常
就连内部方法中,方法也是返回ArrayList
实际上Arrays中返回的ArrayList跟我们用的ArrayList并不是同一个
这是Arrays下的ArrayList
实际上,我们要用到的是java.util.ArrayList
区别:
Arrays.ArrayList和java.util.ArrayList都是继承AbstractList<>,但是Arrays.ArrayList没有重写父类的方法,所以没有add等方法
坑2:
String[] arr = {"hello","java"};
List<String> list = Arrays.asList(arr);
list.set(1, "hi");
for (String string : list) {
System.out.println(string);//hello hi
}
在更改list的内部,会对原数组产生影响,主要是因为Array.asList内部还是使用了arr原始数组,所有的操作还是对arr原始数组进行操作,所以无论使更改arr还是更改Array.asList的list内部都是对原始数组操作
结局方法:
1、在外部封装一层真正的ArrList,简称套娃操作
List<String> list2 = new ArrayList<>(Arrays.asList(arr));
2、采用Google的Guava Lists包的方法
List<String> list = List.newArrayList(arrays);
如果之后马代码的时候出现UnSupportedOperationException,大概率是这个问题了
测试代码
@Test
void ListTest() {
String[] arr = {"hello","java"};
List<String> list = Arrays.asList(arr);
for (String string : list) {
System.out.println(string);
}
list.add("Hello world");
list.set(1, "hi");
for (String string : list) {
System.out.println(string);
}
List<String> list2 = new ArrayList<>(Arrays.asList(arr));
}
Vector
内部实现跟ArrayList差不多,但是Vector是线程同步的
LinkedList
采用双向链表存储方式。
基本操作跟 ArrayList 差不多
ArrayList、LinkedList、Vector对比
1、ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要讲已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。
2、Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。
3、LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。另外,他还提供了List接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。
4、vector是线程(Thread)同步(Synchronized)的,所以它也是线程安全的,而Arraylist是线程异步(ASynchronized)的,是不安全的。如果不考虑到线程的安全因素,一般用Arraylist效率比较高。
5、如果集合中的元素的数目大于目前集合数组的长度时,vector增长率为目前数组长度的100%,而arraylist增长率为目前数组长度的50%.如过在集合中使用数据量比较大的数据,用vector有一定的优势。
6、如果查找一个指定位置的数据,vector和arraylist使用的时间是相同的,都是0(1),这个时候使用vector和arraylist都可以。而如果移动一个指定位置的数据花费的时间为0(n-i)n为总长度,这个时候就应该考虑到使用Linkedlist,因为它移动一个指定位置的数据所花费的时间为0(1),而查询一个指定位置的数据时花费的时间为0(i)。
7、ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,但是插入数据要设计到数组元素移动 等内存操作,所以索引数据快插入数据慢, Vector由于使用了synchronized方法(线程安全)所以性能上比ArrayList要差,LinkedList使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项的前后项即可,所以插入数度较快!
Set
特点:无序 唯一(不重复)
HashSet
采用Hashtable哈希表存储结构
1、优点:添加速度快 查询速度快 删除速度快
2、缺点:无序
内部就是一个HashMap,到HashMap的时候再细讲
LinkedHashSet
1、 采用哈希表存储结构,同时使用链表维护次序
2、 有序(添加顺序)
也属于Hash的存储方式
TreeSet
1、 采用二叉树(红黑树)的存储结构
2、优点:有序 查询速度比List快(按照内容查询)
3、 缺点:查询速度没有HashSet快
Set无序:
1、无法使用for进行遍历(因为无序,所以没有get(i))
2、HashSet、HashMap或Hashtable中对象唯一性判断 :hashCode唯一
3、具体存储方式可以看我的博客—>数据结构与算法:哈希哈希实现
4、基本操作跟HashMap差不多
public static void main(String[] args) {
Set<String> set1 = new HashSet<>();
set1.add("aa");
set1.add("bb");
set1.add("aa");
System.out.println(set1);
set1.remove("bb");
System.out.println(set1);
Set<String> set2 = new HashSet<>();
set2.add("hello");
set2.addAll(set1);
System.out.println(set2);
}
public static void main(String[] args) {
Set<Integer> set = new TreeSet<>();
set.add(300);
set.add(200);
set.add(600);
//按照元素递增的方式排好序
for(Integer m:set){
System.out.println(m);
}
Set<Emp2> set2 = new TreeSet<>();
set2.add(new Emp2(100,"张三",3000));
set2.add(new Emp2(50,"李四",2000));
set2.add(new Emp2(150,"王五",8000));
set2.add(new Emp2(30,"赵六",20000));
for(Emp2 m:set2){
System.out.println(m);
}
}
}
class Emp2 implements Comparable<Emp2> {
int id;
String name;
double salary;
public Emp2(int id, String name, double salary) {
super();
this.id = id;
this.name = name;
this.salary = salary;
}
@Override
public String toString() {
return "id:"+id+",name:"+name+",salary:"+salary;
}
@Override
public int compareTo(Emp2 o) { //负数:小于,0:等于,正数:大于
if(this.salary>o.salary){
return 1;
}else if(this.salary<o.salary){
return -1;
}else{
if(this.id>o.id){
return 1;
}else if(this.id<o.id){
return -1;
}else{
return 0;
}
}
}
Map
Map:
key-value映射 ,以Key->value存储
HashMap
Key 无序 唯一 (Set)
Value 无序 不唯一 (Collection)
LinkedHashMap
有序的HashMap 速度快
TreeMap
有序 速度没有hash快
Set和Map有关系吗?
采用了相同的数据结构,只用于map的key存储数据,就是Set
用hashMap模拟数据库的存储
public static void main(String[] args) {
Map<String,Object> row1 = new HashMap<>();
row1.put("id", 1001);
row1.put("姓名", "张三");
row1.put("薪水", 20000);
row1.put("入职日期", "2010.5.5");
Map<String,Object> row2 = new HashMap<>();
row2.put("id", 1002);
row2.put("姓名", "李四");
row2.put("薪水", 30000);
row2.put("入职日期", "2000.4.4");
Map<String,Object> row3 = new HashMap<>();
row3.put("id", 1003);
row3.put("姓名", "王五");
row3.put("薪水", 3000);
row3.put("入职日期", "2018.5.4");
List<Map<String,Object>> table1 = new ArrayList<>();
table1.add(row1);
table1.add(row2);
table1.add(row3);
for(Map<String,Object> row:table1){
Set<String> keyset = row.keySet();
//keyset遍历
for (String key : keyset) {
System.out.print(key+":"+row.get(key)+"\t");
}
System.out.println();
}
}
用TreeMap模拟
public static void main(String[] args) {
Map<Integer,String> treemap1 = new TreeMap<>();
treemap1.put(20, "aa");
treemap1.put(3, "bb");
treemap1.put(6, "cc");
//按照key递增的方式排序
for(Integer key:treemap1.keySet()){
System.out.println(key+"---"+treemap1.get(key));
}
Map<Emp,String> treemap2 = new TreeMap<>();
treemap2.put(new Emp(100,"张三",50000), "张三业绩不错");
treemap2.put(new Emp(200,"李四",5000), "李四工作不积极");
treemap2.put(new Emp(150,"王五",6000), "王五经常迟到");
treemap2.put(new Emp(50,"赵六",6000), "赵六代码写的好");
//按照key递增的方式排序
for(Emp key:treemap2.keySet()){
System.out.println(key+"---"+treemap2.get(key));
}
}
}
class Emp implements Comparable<Emp> {
int id;
String name;
double salary;
public Emp(int id, String name, double salary) {
super();
this.id = id;
this.name = name;
this.salary = salary;
}
@Override
public String toString() {
return "id:"+id+",name:"+name+",salary:"+salary;
}
@Override
public int compareTo(Emp o) { //负数:小于,0:等于,正数:大于
if(this.salary>o.salary){
return 1;
}else if(this.salary<o.salary){
return -1;
}else{
if(this.id>o.id){
return 1;
}else if(this.id<o.id){
return -1;
}else{
return 0;
}
}
}
Map有很多方法,用到的时候看看文档,内部存储和实现可以看数据结构的课程
Iterator
Iterator:所有集合类均未提供相应的遍历方法,而是把把遍历交给迭代器完成。迭代器为集合而生,专门实现集合遍历
Iterator是迭代器设计模式的具体实现,本质是实现了Iterable接口
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
//使用iterator遍历List
for(Iterator<String> iter=list.iterator();iter.hasNext();){
String temp = iter.next();
System.out.println(temp);
}
Set<String> set = new HashSet<>();
set.add("aa");
set.add("bb");
set.add("cc");
//使用iterator遍历Set
for(Iterator<String> iter=set.iterator();iter.hasNext();){
String temp = iter.next();
System.out.println(temp);
}
Map<Integer,String> map1 = new HashMap<>();
map1.put(100, "aa");
map1.put(200, "bb");
map1.put(300, "cc");
//第一种遍历Map的方式
Set<Entry<Integer,String>> ss = map1.entrySet();
for(Iterator<Entry<Integer,String>> iter=ss.iterator();iter.hasNext();){
Entry<Integer,String> temp = iter.next();
System.out.println(temp.getKey()+"--"+temp.getValue());
}
System.out.println("++++++++++++++++++++++++");
//第二种遍历Map的方式
Set<Integer> keySet = map1.keySet();
for(Iterator<Integer> iter=keySet.iterator();iter.hasNext(); ){
Integer key = iter.next();
System.out.println(key+"----"+map1.get(key));
}
总结