容器|集合:
如果对象的数量与生命周期都是固定的,自然我们也就不需要很复杂的数据结构。
我们可以通过创建引用来持有对象,如
Class clazz;
也可以通过数组来持有多个对象,如
Class[] clazs = new Class[10];
然而,一般情况下,我们并不知道要创建多少对象,或者以何种方式创建对象。数组显然只能创建固定长度的对象,为了使程序变得更加灵活与高效,Java类库提供了一套完整的容器类,具备完善的方法来解决上述问题。
数组: 存储多个数据
特点:
1) 定长
2) 所有数据的类型保持一致
3) 有序
容器特点: 可变长,可有存储任意类型的数据,必须是引用数据类型
只能存储字符串类型的数据
public class Demo01 {
public static void main(String[] args) {
MyContainer my=new MyContainer();
my.add("nihao");
my.add("lisi");
System.out.println(my.size);
System.out.println(my.get(0));
System.out.println(my.get(1));
my.remove(0);
System.out.println(my.size);
System.out.println(my.get(0));
}
}
//自定义的容器类
class MyContainer{
//存储数据的字符串数组
private String[] arr;
//容器中存储数据的个数
int size;
public MyContainer() {
arr=new String[0];
}
/*
* 添加 ,追加在原数据的最后
*/
public void add(String value) {
//备份原数组
String[] temp=arr;
//新数组
arr=new String[size+1];
if(size!=0){
for(int i=0;i<size;i++){
arr[i]=temp[i];
}
}
arr[size]=value; //添加的数据赋值
size++; //长度+1
}
/*
* 获取方法 根据索引获取 参数是索引
*/
public String get(int index){
if(index<0 || index>=size){
throw new ArrayIndexOutOfBoundsException("索引越界");
}
return arr[index];
}
//修改方法 修改指定索引位置的数据
//删除方法 删除指定索引位置的数据
public void remove(int index){
//备份原数组
String[] temp=arr;
//创建新数组
arr=new String[size-1];
//遍历原数组
for(int i=0;i<size;i++){
if(i<index){
arr[i]=temp[i];
}else if(i==index){
continue;
}else{
arr[i-1] = temp[i];
}
size--;
}
}
}
通过上图,可以把握两个基本主体,即Collection和Map。
- Collection是一个接口,是高度抽象出来的集合,它包含了集合的基本操作和属性。Collection包含了List和Set两大分支。
- List是一个有序的队列,每一个元素都有它的索引。第一个元素的索引值是0。List的实现类有LinkedList, ArrayList, Vector, Stack。 Set是一个不允许有重复元素的集合。
Set的实现类有HastSet和TreeSet。HashSet依赖于HashMap,它实际上是通过HashMap实现的;TreeSet依赖于TreeMap,它实际上是通过TreeMap实现的。 - Map是一个映射接口,即key-value键值对。Map中的每一个元素包含“一个key”和“key对应的value”。
- AbstractMap是个抽象类,它实现了Map接口中的大部分API。而HashMap,TreeMap,WeakHashMap都是继承于AbstractMap。
Hashtable虽然继承于Dictionary,但它实现了Map接口。 - Iterator是遍历集合的工具,即我们通常通过Iterator迭代器来遍历集合。我们说Collection依赖于Iterator,是因为Collection的实现类都要实现iterator()函数,返回一个Iterator对象。ListIterator是专门为遍历List而存在的。
- Enumeration是JDK 1.0引入的抽象类。作用和Iterator一样,也是遍历集合;但是Enumeration的功能要比Iterator少。在上面的框图中,Enumeration只能在Hashtable,
Vector, Stack中使用。 - Arrays和Collections是操作数组、集合的两个工具类。
Collection 接口
1.for each
2.迭代器
public class CollectionDemo01 {
public static void main(String[] args) {
Collection col=new ArrayList();
//添加
col.add("haha");
col.add(1);
col.add(false);
col.add(null);
//删除
col.remove("haha");
//包含
System.out.println(col.contains("haha"));
System.out.println(col.size());
//for each
for(Object o:col){
System.out.println(o);
}
//迭代器
//1)获取迭代器对象
Iterator it=col.iterator(); //it用来遍历col容器
//2)判断是否存在下一个元素boolean hasNext() 如果仍有元素可以迭代,则返回 true。
while(it.hasNext()){
//3)获取E next() 返回迭代的下一个元素。
System.out.println(it.next());
}
}
}
List 有序可重
新增:一些与索引相关的方法
遍历:
1.普通for
2.增强for
3.迭代器iterator
4.列表迭代器 listIterator
public class ListDemo01 {
public static void main(String[] args) {
//泛型可以规范容器中存放的所有数据的类型
List<Integer> list=new ArrayList<Integer>();
list.add(11);
list.add(22);
list.add(33);
list.add(44);
//void add(int index, E element)
list.add(0,2);
System.out.println(list);
//E get(int index)
System.out.println(list.get(2));
//remove(index|obj) 如果内容和索引有相同情况,以索引为主
System.out.println(list.remove(2));
System.out.println(list);
// E set(int index, E element) 用指定元素替换列表中指定位置的元素(可选操作)。
list.set(3, 333);
System.out.println(list);
//普通for
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
//for each
for(Integer i: list){
System.out.println(i);
}
//iterator
//1) 获取一个迭代器对象
for(Iterator<Integer> it=list.iterator();it.hasNext();){
System.out.println(it.next());
}
}
}
list的遍历方式:
public class ListDemo02 {
public static void main(String[] args) {
List<String> list=new ArrayList();
list.add("蜘蛛侠");
list.add("钢铁侠");
list.add("美国队长");
list.add("灭霸");
list.add("火箭浣熊");
//判断 1.contains(obj)
/*if(list.contains("灭霸")){
list.add("惊奇队长");
}*/
//遍历 for
/*for(int i=0;i<list.size();i++){
if("灭霸".equals(list.get(i))){
list.add("惊奇队长");
}
}*/
//for.each
//当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。
/*for(String str:list){
if("灭霸".equals(str)){
list.add("惊奇队长");
}
}*/
//iterator 迭代器
当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。
/*Iterator it=list.iterator();
while(it.hasNext()){
if("灭霸".equals(it.next())){
list.add("惊奇队长");
}
}*/
/*
* 列表迭代器:ListIterator<E> listIterator() 返回此列表元素的列表迭代器(按适当顺序)。
*/
ListIterator<String> ls=list.listIterator();
while(ls.hasNext()){
if("灭霸".equals(ls.next())){
ls.add("惊奇队长");
}
}
System.out.println(list);
}
}
list的实现类
-
ArrayList
底层实现: 是由可变数组实现的,通过数组的拷贝实现
优点: 查询效率高
缺点: 添加,删除效率低,需要大量的使用数组的拷贝,效率较低
使用: 大量做查询的时候使用
扩容机制: int newCapacity = oldCapacity + (oldCapacity >> 1); 新容量每次扩容原来的1.5倍,使用copyOf方法进行动态扩容存储自定义的引用数据类型的数据,使用任何比较数据是否相等的方法,需要手动重写equals方法
-
Vector 向量
区别:
1.ArrayList线程不安全的 Vector同步的线程安全的
2.ArrayList扩容原容量的1.5倍 Vector扩容原容量的2倍 -
LinkedList
底层: 使用双向链表结构实现
优点: 做增删效率高
缺点: 查询效率低
使用: 大量做增删的时候使用
新增: 新增了一些操作于链表头尾的方法public class ArrayListDemo03 { public static void main(String[] args) { ArrayList<Person> list=new ArrayList(); list.add(new Person("张三",18)); list.add(new Person("李四",19)); //默认比较对象的地址,因为多个new多个对象,重写equals方法 System.out.println(list.indexOf(new Person("张三",18))); //0 } } //Person类 public class Person { private String name; int age; public Person() { // TODO Auto-generated constructor stub } 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; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; 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; } }
简单使用单向链表实现LinkedList
public class LinkedListDemo04 {
public static void main(String[] args) {
MyLinkedList my=new MyLinkedList();
my.add("haha1");
my.add("haha2");
my.add("haha3");
my.add("haha4s");
System.out.println(my.getSize());
System.out.println(my.get(0));
System.out.println(my.get(1));
System.out.println(my.get(2));
System.out.println(my.get(3));
System.out.println(my.get(4));
}
}
//自定义MyLinkedList 类 只能存储字符串数据
class MyLinkedList{
private Node head;
private int size;
public MyLinkedList() {
// TODO Auto-generated constructor stub
}
public int getSize(){
return this.size;
}
/*
* 添加方法
*/
public void add(String value) {
//新节点
Node temp=new Node(value,null);
//把新节点挂在原链表节点的最后
//判断当前的新节点是否为链表头
if(null==head){
head=temp;
size++;
return;
}
Node node=head;
while(node.getNext()!=null){
node=node.getNext();
}
node.setNext(temp);
size++;
}
//根据索引获取
public String get(int index){
//i作为链表结构的索引 模拟
Node temp=head;
for(int i=0;i<size;i++){
if(i!=index){
temp=temp.getNext();
}else{
return temp.getData();
}
}
return null;
}
}
/*
* 节点类:
*/
class Node{
private String data; //数据
private Node next; //下一个节点的地址
public Node() {
// TODO Auto-generated constructor stub
}
public Node(String data, Node next) {
super();
this.data = data;
this.next = next;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
@Override
public String toString() {
return "Node [data=" + data + ", next=" + next + "]";
}
}
Set 无序 不可重复
新增方法: 无
遍历方式: 1)for each 2)迭代器
HashSet:*** -->HashMap维护的
底层结构:底层是由哈希表存储的 (数组+链表+红黑树组成)
优点:添加,查询,删除效率高
缺点:无序
HashSet存储自定义引用数据类型去重问题:我们认为内容相同就是一个对象
需要重写这个类型的hashcode和equals方法,如果hashcode不相同,不会比较两个对象的equals,hashcode相等才会比较equals方法进一步比价内容
public class SetDemo01 {
public static void main(String[] args) {
HashSet<String> set=new HashSet();
set.add("胡歌");
set.add("杨洋");
set.add("宋承宪"); //字符串的内容相同,hashcode值肯定相同
set.add("宋承宪");
set.add("苏志燮");
//无序: 存放的顺序与真实存储的顺序不一致,顺序一旦确定不会改变
System.out.println(set);
System.out.println(set);
System.out.println(set);
System.out.println(set);
/*String str1=new String("nihao");
String str2=new String("nihao");
System.out.println(str1.hashCode());
System.out.println(str2.hashCode());*/
}
}
TreeSet: -->–>TreeMap维护的
底层结构: 红黑树
优点:查询效率快,有序(默认升序,自定义排序方式)
缺点:但是ArrayList,HashSet快
存储自定义引用数据类型的去重+排序:存储person类型的对象数据的排序和去重问题
去重排序都是按照比较器的比较规则定义
比较器可以是内部或者外部
比较器
内部比较器|自然比较器:默认的比较方式
类实现java.lang.Comparable接口,重写compareTo(T o) 方法,在方法中定义比较规则
-
内部比较器,每次修改都要修改源码,其他类不能使用
-
外部比较器|自定义比较器
对Comparator接口的方法compare方法进行重写,定义比较规则
以外部比较规则优先,没有外部比较规则默认使用自然排序查看api,看数组的Arrays.sort方法,对对象类型进行按照身高降序排序,遍历
TreeSet|TreeMap|Arrays.sort(数组) | Collections.sort(容器,外部比较器对象)
public class Test2 {
public static void main(String[] args) {
// Comparator com=new Comparator(){
//
// @Override
// public int compare(Object o1, Object o2) { //外部比较器
// // TODO Auto-generated method stub
// return ((Person)o2).getAge() - ((Person)o1).getAge();
// }
//
// };
Set tree = new TreeSet((Object o1, Object o2)-> { //外部比较器简写
return ((Person)o2).getAge() - ((Person)o1).getAge();
});
Person p1=new Person(“zhangsan”,18);
Person p2=new Person(“lisi”,17);
Person p3=new Person(“wangwu”,19);
Person p4=new Person(“lisi”,17);
Person p5=new Person(“zhaoliu”,17);
tree.add(p1);
tree.add(p2);
tree.add(p3);
tree.add(p4);
tree.add(p5);System.out.println(tree); }
}
//自定义类
public class Person implements Comparable{ //内部比较器
//public class Person {
private String name;
int age;public Person() { // TODO Auto-generated constructor stub } 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; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } @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; 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 public int compareTo(Person o) { return this.getAge() - o.getAge(); } }
Map
每一个数据都是一个键值对形式的数据 k—v
key是唯一的,不可重复 —所有的key,set集合
value可以是任意类型的数据,value不唯一,可重复 ----所有的value,Collection集合
只按照key值计算位置|去重|排序
一个key只能对应一个value(一对多可以对应value是一个集合)
如果key相同的数据,value会覆盖
public class MapDemo01 {
public static void main(String[] args) {
Map<String,Boolean> map=new HashMap();
//存放值 put()
map.put("赵", null);
map.put("钱", false);
map.put("孙", true);
System.out.println(map.put("李", false));
System.out.println(map);
//相同key,value会覆盖
System.out.println(map.put("孙", null));
System.out.println(map);
/*
* boolean containsKey(Object key)
如果此映射包含指定键的映射关系,则返回 true。
boolean containsValue(Object value)
如果此映射将一个或多个键映射到指定值,则返回 true。
*/
System.out.println(map.containsKey("孙"));
System.out.println(map.containsValue(null));
//V get(Object key) 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
System.out.println(map.get("吴"));
//V put(K key, V value)
System.out.println(map.put("王", false));
// V remove(Object key)
System.out.println(map.remove("吴"));
}
}
Map 的遍历
1.keySet() 根据key获取value
2.values() 直接获取所有的value值
3.Set<Map.Entry<K,V>> entrySet()
public class MapDemo02 {
public static void main(String[] args) {
Map<String,Boolean> map=new HashMap();
//存放值 put()
map.put("赵", false);
map.put("钱", false);
map.put("孙", true);
map.put("李", true);
map.put("周", true);
System.out.println(map);
System.out.println("-------keySet()------");
/*Set<String> keys=map.keySet();
for(String s: keys){
System.out.println(s+"-->"+map.get(s));
}*/
System.out.println("------- values()--------");
Collection col=map.values();
/*Iterator it=col.iterator();
while(it.hasNext()){
System.out.println(it.next());
}*/
System.out.println("-----entrySet()--------");
Set<Map.Entry<String, Boolean>> set=map.entrySet();
for(Map.Entry<String, Boolean> entry : set){
System.out.println(entry.getKey()+"==>"+entry.getValue());
}
}
}
HashMap 存储自定义引用数据类型的数据
key为自定义的引用数据类型的时候,去重,需要对key数据的类型重写hashcode和equals方法
初始容量 (16) 和默认加载因子 (0.75) 每次扩容原容量的2倍
TreeMap
key为自定义的引用数据类型的时候,去重,排序,需要key数据的类型实现内部|外部比较器
HashMap存储的value值为自定义应用数据类型对象时,达到去重,如果value相同,放不进入,怎么做?
遍历value|判断value是否存在,比较如果不相同,再存放,如果相同就不放
public class HashMapDemo03 {
public static void main(String[] args) {
Person p1=new Person("zhangsan",18);
Person p2=new Person("lisi",17);
Person p3=new Person("haha",8);
Person p4=new Person("lisi",17);
TreeMap<Person,Integer> map=new TreeMap((o1,o2)->((Person)o2).getAge() -((Person)o1).getAge());
map.put(p1, 2000);
map.put(p2, 30000);
map.put(p3, 10000);
map.put(p4, 15000);
System.out.println(map);
}
}
Properties 属性列表中每个键及其对应值都是一个字符串。
经常被作为配置文件使用:
public class PropertiesDemo05{
public static void main(String[] args) throws IOException {
Properties pro=new Properties();
pro.put("小熊历险记","李四");
pro.put("小白历险记","李白");
pro.setProperty("haha", "123");
//现加载
System.out.println(pro);
pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
//读入
System.out.println(pro.getProperty("name"));
System.out.println(pro.getProperty("age"));
}
}
Hashtable 线程安全的hashmap
问如何控制HashMap的线程安全问题:
1.Hashtable
2.Collections工具类中synchronizedMap(Map<K,V> m)
3.juc包下ConcurrentHashMap --效率高
static <T extends Comparable<? super T>> void sort(List list) 根据元素的自然顺序 对指定列表按升序进行排序。
static void sort(List list, Comparator<? super T> c)
根据指定比较器产生的顺序对指定列表进行排序。
public class Demo04 {
public static void main(String[] args) {
ArrayList<Person> list=new ArrayList();
list.add(new Person("张三",18));
list.add(new Person("李四",19));
list.add(new Person("王五",17));
list.add(new Person("赵六",20));
System.out.println(list);
Collections.sort(list);
System.out.println(list);
Collections.sort(list,(o1,o2)->((Person)o2).getAge() -((Person)o1).getAge());
System.out.println(list);
//static void reverse(List<?> list) 反转指定列表中元素的顺序。
Collections.reverse(list);
System.out.println(list);
//static void shuffle(List<?> list) 使用默认随机源对指定列表进行置换。
Collections.shuffle(list);
System.out.println(list);
}
}