目录
-
1. 容器(container)
-
容器
-
概念
之前我们学习过数组,数组是相同数据类型的有序集合,可以在其中放置对象或基本类型数据,说白了就是容器。数组是一种简单的线性序列,可以快速的访问数组元素,效率高。如果从效率和类型检查的角度讲,数组是最好的。
但是从数组长度一旦确定,不可改变,因此,不灵活:容量需要事先定义好,不能随着需求的变化而扩容。
因此,数组远远不能满足我们的需求。我们需要一种灵活的,容量可以随时扩充的容器来装载我们的对象。这就是我们今天要学习的容器类,或者叫集合框架。
-
使用数组手写简单容器
package day13.collection;
/*
* 使用数组手写简单容器
*
*/
public class MyContainerDemo2 {
public static void main(String[] args) {
MyCon my = new MyCon();
my.add("98k");
my.add("AWM");
my.add("M24");
System.out.println("容器长度: " + my.size);
System.out.println("-------------------");
my.remove(0);
my.revamp(2, "M416");
for (String string : my.arr) {
System.out.println(string);
}
}
}
/*
* 自定义容器
*/
class MyCon {
// 定义容器索引
String[] arr;
// 容器长度
int size;
// 构造器
public MyCon() {
// 创建对象时就应存在一个数组
arr = new String[0];
}
// 增 --add方法
public void add(String ele) {
// 存储原数组
String[] temp = arr;
arr = new String[size + 1];
for (int i = 0; i < temp.length; i++) {
arr[i] = temp[i];
}
arr[size] = ele;
size++;
}
// 删
public void remove(int index) {
if (index < 0 || index >= size) {
System.out.println("Error");
return;
}
String[] temp = arr;
arr = new String[size - 1];
for (int i = 0; i < size; i++) {
if (i >= index) {
if (i == index) {
continue;
}
arr[i - 1] = temp[i];
} else {
arr[i] = temp[i];
}
}
// 删除
size--;
}
// 获取
public String get(int index) {
if (index < 0 || index >= size) {
return "你越界了朋友";
}
return arr[index];
}
// 改
public void revamp(int index, String str) {
for (int i = 0; i < arr.length; i++) {
if (index < 0 || index > size - 1) {
System.out.println("没有此数据");
return;
}
arr[index] = str;
}
}
}
-
泛型<>
-
为什么需要泛型
JDK1.4 以前类型不明确:
装入集合的类型都被当作Object 对待,从而失去自己的实际类型。
从集合中取出时往往需要转型,效率低,容易产生错误。
-
泛型的好处
增强程序的可读性和稳定性。
使用泛型,保留了容器中元素的类型,安全省心的使用容器。
注意:没有必要引入泛型的复杂性。
-
2. Collection接口
* Collection 接口 规定了容器类的一些使用规则,可以存储无序不可重复的数据
* 容器中只能放引用数据类型,基本数据类型会自动装箱
*
* 1.特性 无序不可重复 接口
* 2.方法
* 3.遍历
package day13.collection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class CollectionDemo3 {
public static void main(String[] args) {
//1.创建容器
Collection c=new ArrayList();
/*
* 2.添加元素
* boolean add(E e)
确保此 collection 包含指定的元素(可选操作)。
boolean addAll(Collection<? extends E> c)
将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。
*/
c.add("清华大学");
c.add("北京大学");
Collection c2=new ArrayList();
c2.add("哈工大");
c2.add("佳木斯大学");
c2.add(1); //Integer 自动装箱
System.out.println(c.size());
c.addAll(c2);
/*
* 3.获取
* int size()
返回此 collection 中的元素数。
boolean isEmpty()
如果此 collection 不包含元素,则返回 true。
*/
System.out.println(c.size());
System.out.println("判断是否为空:"+c.isEmpty());
System.out.println(c);
/*
* 4.删除
* void clear()
移除此 collection 中的所有元素(可选操作)。
boolean remove(Object o)
从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。
boolean removeAll(Collection<?> c)
移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)。
boolean retainAll(Collection<?> c)
仅保留此 collection 中那些也包含在指定 collection 的元素(可选操作)。
*/
System.out.println(c.size());
System.out.println("remove:"+c.remove("清华大学"));
System.out.println(c.size());
//System.out.println("removeAll:"+c.removeAll(c2));
System.out.println("removeAll:"+c.retainAll(c2));
System.out.println(c.size());
// c.clear();
System.out.println(c.size());
System.out.println("判断是否为空:"+c.isEmpty());
/*
* Object[] toArray()
返回包含此 collection 中所有元素的数组。
*/
Object[] obj=c.toArray();
System.out.println(Arrays.toString(obj));
/*
* 5.是否包含
* boolean contains(Object o)
如果此 collection 包含指定的元素,则返回 true。
*/
System.out.println(c.contains("佳木斯大学"));
/*
* 6.遍历
* foreach
* 迭代器
*/
}
}
-
collection遍历
* Collection 遍历
* 两种:1)增强for
* 2)迭代器
package day13.collection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
/*
* Collection 遍历
* 两种:1)增强for
* 2)迭代器
*/
public class CollectionDemo4 {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("拇指");
c.add("食指");
c.add("中指");
c.add("无名指");
c.add("小手指");
//foreach
for (Object obj : c) {
System.out.println(obj);
}
/*迭代器
* 1.获取一个迭代器对象,凡是实现了Collection接口的;类都有iterator()
* 2.判断是否有下一个元素
* 3.如果有用next()方法获取
*/
Iterator it =c.iterator();
while (it.hasNext()) {
Object o = it.next();
System.out.println(o);
}
}
}
-
List接口
* List
* 特性: 有序 可重复
* 方法:
* 遍历:
package day13.test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/*
* 遍历
* 如果容器中存在火,那就添加一个元素灭火器
*
* ConcurrentModificationException-----foreach&迭代器(底层是foreach)
* 方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。
*/
public class ListTest1 {
public static void main(String[] args) {
List<String> ls = new ArrayList();
// 增加
ls.add("金");
ls.add("木");
ls.add("水");
ls.add("火");
ls.add("土");
// 1.----------for
for (int i = 0; i < ls.size(); i++) {
if (ls.get(i).equals("火")) {
ls.add("灭火器1");
}
// System.out.println(ls.get(i));
}
System.out.println(ls);
System.out.println("↓--↓--↓---↓---↓---↓---↓--↓--↓");
// ----------foreach
for (String str : ls) {
System.out.println(str);
}
System.out.println("↓--↓--↓---↓---↓---↓---↓--↓--↓");
/*-------------迭代器
* 解决并发增加问题:使用 ListIterator
*/
ListIterator it = ls.listIterator();
while (it.hasNext()) {
if (it.next().equals("火")) {
it.add("灭火器2");
}
}
System.out.println(ls);
}
}
-
ArrayList
* ArrayList : 有序的 可重复的 线程不安全
* 底层由可变数组结构实现
* 优点:查询,以及随机获取效率高
* 缺点:增加,删除效率低
* 动态扩容:使用copyof进行扩容,新数组的大小是原数组的1.5倍
* Vector:线程安全的,效率低 ,每次扩容原来的2倍
package day13.list;
import java.util.ArrayList;
import java.util.ListIterator;
public class ArrayListDemo08 {
public static void main(String[] args) {
ArrayList<String> ls=new ArrayList(); //构造一个初始容量为 10 的空列表。
System.out.println(ls);
ls.add("钢铁侠");
ls.add("雷神");
ls.add("灭霸");
ls.add("美国队长");
System.out.println(ls.indexOf("雷神"));
//1.获取迭代器
ListIterator li=ls.listIterator();
//判断
while(li.hasNext()){
System.out.println(li.next());
}
System.out.println("------------------");
while(li.hasPrevious()){
System.out.println(li.previous());
}
}
}
-
ArrayList存储对象
package day13.list;
import java.util.ArrayList;
import java.util.List;
/*
* ArrayList存储对象
*/
public class ArrayListTest09 {
public static void main(String[] args) {
//创建容器
List<Person> ls=new ArrayList();
ls.add(new Person("张三",18));
ls.add(new Person("李四",19));
System.out.println(ls.indexOf(new Person("李四",19))); //-1 因为Person中没有重写equals方法
List<String> ls2=new ArrayList();
ls2.add(new String("哈哈"));
System.out.println(ls2.indexOf(new String("哈哈"))); // 0 因为String类型中重写了squals方法
}
}
class Person{
private String name;
private int age;
public Person() {
// TODO Auto-generated constructor stub
}
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;
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
-
LinkedList
* LinkedList 有序的 可重复的 线程安全
* 实现结构:是由链表结构实现的
* 优点:插入,删除效率高
* 缺点:查询,随机获取效率低
* 新增了一些操作类表头和类表尾的方法
package day13.test;
import java.util.LinkedList;
public class LinkedListTest {
public static void main(String[] args) {
LinkedList<String> ls = new LinkedList();
ls.add("哈哈");
ls.add("呵呵");
ls.add("嘿嘿");
ls.addFirst("嚯嚯");
System.out.println(ls);
System.out.println(ls.clone());
System.out.println(ls.contains("哈哈"));//true
System.out.println(ls.descendingIterator().next());//嘿嘿
}
}
-
Vector与Stack
-
三者比较
LinkedList:底层用双向链表实现的List。特点:查询效率低,增删效率高,线程不安全。
ArrayList:底层用数组实现的List。特点:查询效率高,增删效率低,线程不安全。
Vector:底层用数组实现的List,特点:线程安全。
如何选用?
线程安全用Vector。
线程不安全,查找较多用ArrayList。增加或删除元素较多用LinkedList
-
Set接口
package day13.set;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/*
* Set 接口 无序的 不可重复的
*/
public class SetDemo11 {
public static void main(String[] args) {
Set<Character> set=new HashSet();
set.add('a');
set.add('b');
set.add('c');
set.add('c'); //不能重复
set.add('d'); //不能重复
set.add('e'); //不能重复
System.out.println(set.size());
set.remove('c');
System.out.println(set); //[d, e, b, a] 不会按照添加的顺序存储,但是一旦储存顺序就确定了
//遍历
//----foreach
for(char c:set){
System.out.println(c);
}
//----迭代器
Iterator it=set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
-
HashSet
* HashSet
* 无序的 不可重复的
* 由哈希表的数据结构存储的 哈希表=数组+链表
* 优点:查询 增加 删除 效率高
* 缺点:无序的
*
* 去重:从写hashcode 与 equals ---- alt+shif+s ->h 按业务更改
* 先调用hashcode,如果计算出来的位置不相同,默认就是两个对象
* 如果hashcode值相同,再去调用equals,去比较内容
package day13.set;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
public class HashSetDemo12 {
public static void main(String[] args) {
HashSet<User> set=new HashSet<>();
set.add(new User("李健",40));
set.add(new User("谢霆锋",41));
set.add(new User("李健",40));
System.out.println(set.size());
for(User u:set){
System.out.println(u.hashCode());
}
TreeSet tr=new TreeSet();
tr.add("c");
tr.add("b");
tr.add("哈哈");
tr.add("a");
tr.add("呵呵");
tr.add("e");
System.out.println(tr);
System.out.println(tr.ceiling("b")); //c
System.out.println(tr.floor("b")); //a
System.out.println(tr.higher("b")); //c
System.out.println(tr.pollFirst()); //c
}
}
class User{
private String name;
private int 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;
}
public User(String name, int age) {
super();
this.name = name;
this.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;
User other = (User) 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;
}
}
-
TreeSet
* TreeSet
* 有序的(排序--升序) 不可重复的
* 使用二叉树结构存储
-
equal和hashcode的关系和原理
1. Hashcode并不是内存地址,是内存地址转换而来的。系统通过它也可以确定内存地址。
2. hashcode方法主要用在集合框架中,目的是为了快速比较两个对象是否相等,因为集合框架中的对象很多,每个都使用equals比较效率很差。
每个对象都有一个hashcode,规定:
1、内容相同的对象hashcode肯定相等
2、内容不相同的对象hashcode可能相等也可能不相等
所以如果两个对象的hashcode不相等则两个对象的内容肯定不相等,这样就不必一个一个去比较属性的值了,从而提高对象比较的速度。
-
3. Iterator接口
为了遍历容器方便,所有实现了Collection接口的容器类都有一个iterator方法用以返回一个实现了Iterator接口的对象。Iterator对象称作迭代器,用以方便的实现对容器内元素的遍历操作。
使用迭代器时,分三步走策略,
第一步:获取对象
第二步:判断是否存在下一个
第三步:获取元素
见List接口的使用迭代器遍历部分
-
ListIterator接口
* ConcurrentModificationException
* 方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。
解决并发增加问题:使用 ListIterator
//1.获取迭代器
ListIterator li=ls.listIterator();
//判断
while(li.hasNext()){
if(li.next().equals("灭霸")){
li.add("惊奇队长");
}
}
System.out.println(ls);