容器的用途
如果对象的数量与生命周期都是固定的,自然我们也就不需要很复杂的数据结构。
我们可以通过创建引用来持有对象,如
Class clazz;
也可以通过数组来持有多个对象,如
Class[] clazs = new Class[10];
然而,一般情况下,我们并不知道要创建多少对象,或者以何种方式创建对象。数组显然只能创建固定长度的对象,为了使程序变得更加灵活与高效,Java类库提供了一套完整的容器类,具备完善的方法来解决上述问题。
注意:当数组的元素的类型是类的时候,数组的每一个元素其实只是对象的管理者而不是对象本身。因此,仅仅创建数组并没有创建其中的每一个对象!
容器的分类
观察上图,我们可以得出容器主要分为两种类型,两个接口Collection与Map定义了两类不同的对象存储方式。
Collection用以保存单一的元素,Map保存关联键值对。通过泛型来指定容器存放的数据类型。 Iterator 设计的目的是在未知容器具体的类型的情况下,用来遍历容器元素。剩下的容器类型都是继承了这两个接口。
下面介绍几个适合初学者的简单容器
1.ArrayList
类型:范型容器
ArrayList<String> notes=new ArrayList<String>(); 容器类有两个类型:容器的类型、元素的类型
ArrayList有顺序的,下标索引从0开始。
它的优点在于随机访问元素快,但是在中间插入和移除比较慢
那么现在我们就一起来看看为什么ArrayList随机访问快,而插入移除比较慢。先说关于ArrayList的初始化。
ArrayList有三种方式进行初始化如下:
private transient Object[] elementData;
public ArrayList() {
this(10);
}
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
我们可以看出ArrayList其实就是采用的是数组(默认是长度为10的数组)。所有ArrayList在读取的时候是具有和数组一样的效率,它的时间复杂度为1。
插入尾部就是elementData[size++] = e;当然中间会进行扩容。现在主要说插入中间为什么相对来说比较慢源码如下:
public void add(int index, E element) {
rangeCheckForAdd(index);//验证(可以不考虑)
ensureCapacityInternal(size + 1); // Increments modCount!!(超过当前数组长度进行扩容)
System.arraycopy(elementData, index, elementData, index + 1,
size - index);(核心代码)
elementData[index] = element;
size++;
}
下面以一个简单的记事本程序来展示ArrayList的操作:
public class NoteBook {
private ArrayList<String> notes=new ArrayList<String>();
public void add(String s) {
//ArrayList的add方法
notes.add(s);
}
public void add(String s,int location) {
notes.add(location, s);
}
public int getSize() {
return notes.size();
}
public String getNote(int index) {
return notes.get(index);
}
public void removeNote(int index) {
notes.remove(index);
}
public String[] list() {
String[] a=new String[notes.size()];
/*for (int i = 0; i < notes.size(); i++) {
a[i]=notes.get(i);
}*/
notes.toArray(a);
return a;
}
public static void main(String[] args) {
String[] aStrings=new String[2];
aStrings[0]="first";
aStrings[1]="second";
NoteBook noteBook=new NoteBook();
noteBook.add("first");
noteBook.add("second");
noteBook.add("third",1);
System.out.println(noteBook.getSize());
System.out.println(noteBook.getNote(0));
System.out.println(noteBook.getNote(1));
noteBook.removeNote(1);
String[] a=noteBook.list();
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
} }
}
2.Set
Set是一个集合,它的特点是不可以有重复的对象,也就是数学中集合的概念,所以Set最常用的就是测试归属性,很容易的询问出某个对象是否存在Set中。并且Set是具有和Collection完全一样的接口,没有额外的功能,只是表现的行为不同。下面介绍Set容器中的HashSet.
HashSet
HashSet的特点是查询速度比较快,但是存储的元素是随机的并没有排序。我们可以用下面这段代码测试一下Set中元素的唯一性和无序性。
public static void main(String[] args){
/**
* 没有顺序可循,这是因为hashset采用的是散列(处于速度考虑)
*/
Random random=new Random(47); //47为种子,想要获取一个范围内的随机数(例如26,随机数可能是0-25),首先需要一个种子(其实就是一个数值)。
Set<Integer> intset=new HashSet<Integer>();
for (int i=0;i<10000;i++){
intset.add(random.nextInt(30)); //随机生成一个30以内的数
}
System.out.print(intset);
}
输出结果:
从这个结果可以看到,即使我们循环了10000次产生一个随机数,intset中还是只有30个数而且是无序的。
下面是用HashSet做的一个输出硬币名称的简单程序
public class Coin {
private HashMap<Integer, String> coinnames=new HashMap<Integer,String>();
public Coin() {
// TODO Auto-generated constructor stub
coinnames.put(1, "penny");
coinnames.put(10, "dime");
coinnames.put(25, "quarter");
coinnames.put(50, "half-dolar");
coinnames.put(50,"五毛");
System.out.println(coinnames.keySet().size());
System.out.println(coinnames);
}
public String getName(int amount) {
if (coinnames.containsKey(amount)) {
return coinnames.get(amount);
}else {
return "NOT FOUND";
}
}
public static void main(String[] args) {
java.util.Scanner in=new java.util.Scanner(System.in);
int amount=in.nextInt();
Coin coin=new Coin();
String name =coin.getName(amount);
System.out.println(name);
}
}
运行看看结果
从这个结果我们可以知道, hash表中的每一个key值只能对应一个结果,如果对一个key值多次赋值,这个key只会对应最后一次的结果。同时hash不能放入foreach循环中,遍历没有特别简单的方法,则需遍历key的集合。
for (Integer k : coinnames.keySet()) {
String string=coinnames.get(k);
System.out.println(string);
}
参考资料:
https://blog.csdn.net/weixin_41228894/article/details/80388418
https://www.cnblogs.com/ACFLOOD/p/5555555.html
https://www.cnblogs.com/LipeiNet/p/5888513.html