本文将介绍Java中的各类型容器、常用容器以及常用容器的一些基本的操作。在日后的文章中会进一步了解这些容器的内部实现、涉及容器线程安全方面的知识这里不再赘述。
一、各个类型容器之间的关系图
对这张图做一下简单的解释:
我们可以看出,在图中其实只有四种类型的容器,List,Set,Queue和Map(其实应该去掉Queue,因为Stack和Queue的功能均可以由LinkedList提供)。其中前三种容器实现了Collection接口。在这张图中,虚线加空心箭头表示实现,虚线和实心箭头表示产生。例如Map的对象,可以产生Collection类型的对象(例如keySet),每个Collection都可以产生Iterator类型对象。
二、List
实现List接口的有两个子类,ArrayList类和LinkedList类。两者的区别在于,当我们希望进行大量的随机访问的时候,我们使用ArrayList,当我们需要经常对列表从中间插入或者删除元素,那么我们使用LinkedList。可以理解为LinkedList像是一个双向链表,但是ArrayList只是数组。(其大小均可以扩展,ArrayList默认是10)
// 创建ArrayList
ArrayList<String>myList = new ArrayList<>();
//添加元素
myList.add("Irving");
myList.add("Jack");
myList.add("Kevin");
myList.add("Lily");
//排序-自定义排序Comparator和默认排序
myList.sort(new MyComparator());
Collections.sort(myList, new MyComparator());
Collections.sort(myList);
//判断是否包含某元素
System.out.println(myList.contains("Alice"));
//使用ListIterator遍历列表
ListIterator<String> iterator2 = myList.listIterator();
while(iterator2.hasNext())
{
String str = iterator2.next();
System.out.println(str);
//更改元素
if(str.equals("Lily"))
{
iterator2.set("Lucy");
}
}
//删除
myList.remove(2);
//创建LinkedList
LinkedList<String>myCollection = new LinkedList<>();
//添加元素
myCollection.add("Alice");
myCollection.add("Bob");
//在前后加入元素
myCollection.addFirst("Ada");
myCollection.addLast("Hellen");
//获取前后元素 不存在时peek返回null 其余抛出异常
System.out.println("列表第一个:" +myCollection.element()+" "+myCollection.getFirst()+" "+ myCollection.peek());
System.out.println("列表最后一个:" +myCollection.getLast()+" " +myCollection.peekLast());
LinkedList可以提供队列和栈的功能,可以从首部和尾部插入元素,同时获取首部或者尾部的元素。同时也可以使用相关的remove函数来删除元素
// 删除并返回首部元素,不存在即抛出异常
myCollection.removeFirst();
myCollection.pop();
// 删除并返回首部元素 不存在返回null
myCollection.poll();
// 同理还有删除尾部,主要的区别就是如果不存在是否抛出异常还是 返回null
由于Queue的使用和List差不多,同时Queue和Stack的功能LinkedList都可以实现,所以就不介绍Queue了。
Set包含有TreeSet和HashSet两个子类。TreeSet主要是保持元素处于排序状态,HashSet用于快速的访问,不排序元素。同时,Set不接受重复的元素。
在set中,我们经常使用的是HashSet。
public static void main(String args[]) {
HashSet<Person> mySet = new HashSet<>();
HashSet<Person> mySet2 = new HashSet<>();
Person Alice = new Person("Alice", 15);
Person Alice1 = new Person("Alice", 15);
Person Bob = new Person("Bob", 17);
Person Cindy = new Person("Cindy", 18);
Person Dave = new Person("Dave", 15);
//添加元素
mySet.add(Alice);
mySet.add(Bob);
mySet.add(Cindy);
mySet2.add(Alice1);
mySet2.add(Dave);
//删除元素
mySet.remove(Bob);
//直接增加一个集合所有的元素
mySet.addAll(mySet2);
//把集合转换成数组
Object[] parry = mySet.toArray();
for(int i = 0 ; i < parry.length;i++)
{
Person person = (Person)parry[i];
System.out.println(person.name + "->" + person.age + " ");
}
//遍历
Iterator<Person> iterator = mySet.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next().name + "->"+iterator.next().age);
}
}
四、Map
Map主要使用的是HashMap和TreeMap两种Map。其中HashMap主要是基于哈希表,TreeMap的原理则和C++中的Map一样,是基于红黑树的。
HashMap主要是用于快速访问,TreeMap保持键始终在排序的状态。我们常使用HashMap
public static void main(String args []){
// 创建HahMap
HashMap<Integer, Integer>myMap = new HashMap<>();
for(int i = 0 ; i <10;i++)
{
myMap.put(i, 1);
}
//更改 key:9 对应的value为15
myMap.replace(9,15);
//判断 key:9 对应的value是否为1,如果是1,更改为15
myMap.replace(9,1,15);
//遍历方法1:获取所有的Entry,然后遍历Key和Value,可以很方便的更改键值对
Set<Entry<Integer, Integer> > mySet = myMap.entrySet();
for(Entry<Integer, Integer> entry:mySet)
{
System.out.println(entry.getKey() +" : " + entry.getValue());
// 更改该entry的value为15
entry.setValue(15);
}
Random random = new Random();
for(int i = 10 ; i <20;i++)
{
int r = random.nextInt(10)+10;
//判断是否包含该键值
if(myMap.containsKey(r))
//使用get方法获key对应的值,同时更改。相同的key的键值对插入会覆盖
myMap.put(r, myMap.get(r)+1);
else
myMap.put(r, 1);
}
myMap.put(100, 100);
//使用Function,如果存在key:100,更改其值为103,否则更改为100
myMap.computeIfPresent(100, (k,v)-> true==false? 100:100+3);
myMap.compute(1000, (k,v)-> 1003);
//遍历方法2:首先获取所有的key的集合,然后根据key获得value,无法更改键值对
Set<Integer> kset = myMap.keySet();
Collection<Integer> myCollection = myMap.values();
for(Integer k:kset)
{
System.out.println("key:"+k + "values:" + myMap.get(k));
}
//遍历方法3:获取所有的entry的集合,然后使用iterator遍历该set集合。和1原理相同
//方便更改键值对
Iterator<Entry<Integer, Integer>> it = myMap.entrySet().iterator();
while(it.hasNext())
{
Entry<Integer, Integer> entry = it.next();
System.out.println("key: "+entry.getKey()+"values: "+entry.getValue());
}
}
五、Collections和Arrays工具类
在容器的使用时候,还有两个工具类,分别是Collections和Arrays。这两个类都只提供静态方法,而无法直接实例化。
Arrays常用方法:
String [] tmp = {"1","2","3"};
//使用asList方法,将对象数组转换为List对象
List<String>list = Arrays.asList(tmp);
//二分查找,查询目标数组中是否含有某个元素
Arrays.binarySearch(tmp, "5");
//拷贝某个数组,将其中从的0开始的length个元素作为新的数组。
//如果length大于数组长度,那么多出来的地方使用相应的数据类型默认的值填充
String tmp2[] = Arrays.copyOf(tmp, 5);
//判断两个数组是否相同
Arrays.equals(tmp, tmp2);
//使用替换值填充数组中所有元素
Arrays.fill(tmp, "替换值");
//使用归并排序把数组排成递增数组。自定义类型需要实现Comparable
Arrays.sort(tmp);
//打印数组 格式: [xxx,xxx,xxx]
System.out.println(Arrays.toString(tmp));
String [] tmp = {"1","2","3"};
String [] tmp3 = {"7","8","9"};
//使用asList方法,将对象数组转换为List对象
//这里必须先把list转为list3,否则会抛出异常UnsupportedOperationException
List<String>list = Arrays.asList(tmp);
List<String>list3 = new ArrayList<>(list);
Collections.addAll(list3, tmp3);
//二分查找
Collections.binarySearch(list, "5");
//如果直接使用List<String>list2 = new ArrayList<>();会抛出数组越界异常。
//因为此时list2的size为0,无法进行copy。可是使用下面的方法或者赋初始值的方法
List<String>list2 = new ArrayList(Arrays.asList(new Object[3]));
Collections.copy(list2, list);
//判断两个集合是否相交
Collections.disjoint(list3,list2);
//返回一个空的List,但是无法进行直接增删操作。
//同类的还有Map,Set等相关函数
List<String> list4 = Collections.emptyList();
//使用替换值填充集合中所有元素
Collections.fill(list3, "你好");
//统计集合中某对象频率
int frequency = Collections.frequency(list3, "你好");
System.out.println(frequency);
//返回集合最大/小元素。自定义元素要实现Comparable
Collections.max(list3);
Collections.min(list3);
//对集合进行反序
Collections.reverse(list2);
//对集合进行打乱
Collections.shuffle(list);
//对集合进行排序,同时也可以提供Comparator
Collections.sort(list);
Collections.sort(list, new MyComparator());
//对集合进行旋转。意思就是集合循环右移distance位数。
//[h,e,l,l,o,] 右移三个变成 [l,l,o,h,e]
Collections.rotate(list, 3);
这里解释一个地方:
使用copyof()的地方:
如果直接使用下面的代码:
List<String>list = Arrays.asList(tmp);
Collections.addAll(list, tmp3);
Collections.addAll()内部的实现还是使用list.add()逐个增加元素。Arrays.asList() 返回java.util.Arrays$ArrayList, 而不是ArrayList。
Arrays$ArrayList和ArrayList都是继承AbstractList,remove,add等method在AbstractList中是默认throw UnsupportedOperationException而且不作任何操作。
但是ArrayList 覆盖了add等方法,来对list进行操作,所以我们使用ArrayList进行add不会产生异常。
本文仅仅列举了容器一些基本的操作,Collection中还有很多其他的函数,没有列举出。在使用的时候可以查询API文档。
P.S.文章不妥之处还望指正