数组与集合都是容器,那其中又有着哪些点点滴滴的故事呢?
一、数组
1、特点
(1)一旦创建,长度很难改变
(2)内部元素全为指定的同一种类型
2、数组的初始化内存分配
数组是引用类型,存放在堆中,数组中的元素,无论是引用型变量,还是基本数据类型,均是保存在堆内存中。数组的初始化分为声明与创建过程,声明只是在栈内存中声明变量,而创建则实在堆空间开辟空间,内存分配图如下:
3、多维数组
在了解多维数组之前,先看一看下面一段代码的内存分配的图片
多维数组的意思不过是数组中的元素仍是数组,以前学数组的时候int[][] ar = new int[3][],让我很不解,数组的初始化不是一点要先指定长度吗?其实这只不过是一个障眼法,我们将其与int[] arr比对,其实int[][] arr = new int[3][]只是一个数组声明的过程而已。
二、集合框架
1、特点:
(1)长度可变,不固定
(2)其中的元素全是对象
2、常用集合框架体系图
3、具体剖析
(1)Map(键值对)
数据不只是单个出现的,许多总牵连着关系,例如,看电影买票,座位和你总存在着对应的关系,因此就有了Map的出现。
Map的特点
Map是以键值对形式存在的,其中的键是关键,所以需要保证它的唯一性,由键能索引到值,因此可以把value看错key的附属物,故可将Map看做为一个关联数组。
常用的Map实现类有HashMap,Hashtable,LinkedHashMap,Properties,TreeMap
1)HashMap与Hashtable
HashMap与Hashtable底层均是哈希表数据结构,用作键的对象必须实现hashCode和equals方法来保证键的唯一性,但其中却有着细微的区别
HashMap Hashtable
不可以使用null键和null值 允许使用null键和null值
线程同步,效率低 线程不同步,效率高
hashCode()的作用
当数据存入以hash表结构的集合中时,加入的元素就会根据其hashCode()方法算出hash值,根据hash值就找到了其对应的存储区域,然后再用equals方法对其区域内元素依次进行。
2)LinkedHashMap以链表为结构,适合于增删改,对于查找的效率相对比HashMap低
3)Properties特点为键值都是String类型,经常与流相结合应用,用于读取配置文件
4)TreeMap以红黑树为结构,添加的key需实现Comprable,加入的数据会根据复写的compareTo方法进行自然排序,同时也可以根据指定条件排序,排序条件需实现Comparator接口,实现compare方法。
Map的遍历
第一种:将Map中的key转化为Set集合,然后通过迭代Set取得key,再用key取Value
第二种:将Map转化成Set集合,可获取key以及获取value
第三种:通过将Map转化为Set集合后,通过迭代实现几个的遍历
第四种:获取所有的value,然后对value进行遍历,但是无法遍历key
TreeMap,Properties的使用与遍历示例
package cn.itheima.blog3;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;
class PersonSortByName implements Comparator{
@Override
public int compare(Object o1, Object o2) {
if(o1.getClass() != Person.class || o2.getClass() != Person.class)
throw new RuntimeException("类型不是Person,不比!!!");
Person p1 = (Person)o1;
Person p2 = (Person)o2;
int temp = p1.getName().compareTo(p2.getName());
return temp == 0 ? p1.getAge() - p2.getAge() : temp;
}
}
class Person implements Comparable{
private String name;
private int age;
public Person(String name, int age) {
super();
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;
}
//重写hashCode与equals,保证键的唯一性。
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
//重写compareTo方法,按照年龄从大到小排序,若相当则按照姓名排序
public int compareTo(Object o) {
Person p = (Person)o;
int temp = this.age-p.age;
return temp==0?this.name.compareTo(p.name):temp;
}
@Override
//方便观看打印结果
public String toString(){
return this.name + this.age + "岁";
}
}
public class TreeMapDemo {
/**
* 演示TreeMap的应用,以及Map的遍历
* TreeMap中排序的方式有两种,
* 1、自然排序,添加的元素需实现Comparable接口
* 2、自定义排序规则,实现Comparatoe接口
*
*/
public static void main(String[] args) {
//先自然排序
System.out.println("自然排序,keySet遍历---------------------------");
TreeMap<Person, Integer> map1 = new TreeMap<Person, Integer>();
map1.put(new Person("xiaowang", 19), 1);
map1.put(new Person("xiaobai", 22), 4);
map1.put(new Person("xiaoli", 21), 3);
//将key转换成Set集合
for(Person p : map1.keySet()){
System.out.println("key=" + p +"---->" +"value=" +map1.get(p));
}
System.out.println();
System.out.println("加入重复key后,Map2Set后使用foreach语句遍历------------------");
//加入key重复的值,对以前进行覆盖
map1.put(new Person("xiaobai",22), 5);
//将Map转化为Set集合
for(Map.Entry<Person, Integer> entry : map1.entrySet()){
System.out.println(entry.getKey() + "---->" + entry.getValue());
}
System.out.println();
//用自定义的排序条件进行排序
System.out.println("采用自定义条件排序,Map2Set后使用Iterator迭代遍历--------------");
TreeMap<Person, Integer> map2 = new TreeMap<Person, Integer>(new PersonSortByName());
map2.put(new Person("A", 19), 2);
map2.put(new Person("B", 18), 3);
map2.put(new Person("C", 20), 4);
Set<Map.Entry<Person, Integer>> set = map2.entrySet();
Iterator it = set.iterator();
while(it.hasNext()){
Map.Entry<Person, Integer> en = (Entry<Person, Integer>) it.next();
System.out.println(en.getKey() + "---->" + en.getValue());
}
}
}
package cn.itheima.blog3;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
public class PropertiesDemo {
/**
* Properties应用的演示
* 需求:将("黑马程序员", "就是牛")存入property.properties文件中
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//新建Properties集合
Properties prop = new Properties();
//将所需配置信息存入Properties集合中
prop.setProperty("黑马程序员", "就是牛");
//新建文件输出流对象
FileOutputStream fos = new FileOutputStream("property.properties");
//将prop存储到流中
prop.store(fos, "");
if(fos != null)
fos.close();
}
}
(2)Set集合
Set的底层是依靠Map来实现的,Set中的元素是不可重复的,如同Map中的key一般
常用实现子类HashSet,TreeSet,LinkedHashSet,其中的用法与Map相似,HashSet与TreeSet中添加的元素不按照正常插入顺序排列,而LinkedHashSet是按照正常插入顺序排列的。Set的实现子类与Map的子类用法相似,在此不赘述。
(3)List集合
List几个可以装重复的元素,而且数据的顺序按照存放顺序排列
常用实现类有ArrayList, LinkedList, Vector
1)ArrayList底层以数组维护,可以实现随机访问查询,但是在进行元素增删改方面,效率低下,因为需要移动大量的元素
2)LinkedList底层以双端队列链表为结构,既具有先进先出的队列性质,也有后进先出的栈的性质,其非常适合对匀速进行增删改操作
3)Vector是出现较早的一个集合,他与ArrayList的差别并不大,只是Vector是线程安全的,而ArrayList非线程安全
(4)Map,Set,List之间的联系
Map中分为key与value,我们将value集合起来,从某种意义上说,他就像一个List集合,List相当于所有的key都是Integer的Map;Set依靠Map实现,Map与Set可以相互转换。
(5)Iterator接口:实现该接口并复写iterator后,可使用迭代器,List与Set均实现该接口,所以可以通过迭代器进行遍历,还有一种便是通过for循环
三、数组与集合的总结
当计算机中的数据一多,就需要存储,要存储就需要容器,容器有很多,如对象,数据,集合等,面对不同的数据就需要不同的容器。
数组与集合的区别:
1、数组为定长,而集合为不定长
2、数据可以存储基本数据类型与引用数据类型数据,而集合只能存储对象
3、数组声明时需指定数据类型,而集合不需要