In this tutorial, we'll look at how to use Java's
Collection Framework to organize and work with data. Different types of
collection interfaces and the classes that implement them are available in the
Collection Framework. These interfaces can be separated into: the
Collection interface and the Map interface. We can build an
ordered or index-based collection object using the
Collection interface. On the other hand, we can make key-value-paired
collection objects using the Map interfaces.
Collection(I)
The Collection interface is referred to as the Collection Framework's
root interface. This interface contains a few general-purpose abstract
methods, and its child classes or interfaces automatically have access to
these methods. Important methods are as follows:
boolean add(Object object)
boolean remove(Object object)
boolean retainAll(Collection collection) : all the objects will be removed except those present in collection
boolean contains(Object object)
int size()
Object[] toArray()
Iterator iterator();
The Collection interface has no implementation class. The
List and Set interfaces extend the Collection interface.
List(I)
The Java Collection Framework's List interface stores an ordered
collection of elements. This interface contains methods for adding, removing,
and accessing collection elements. Among the most essential methods in the
List interface are:
// Adds an element at the specified index
void add(int index, Object object)
// Inserts the provied collection object at the specified index
boolean addAll(int index, Collection collection)
// Removes an element from the specified index
Object remove(int index)
// Retrieves an element from the specified index
Object get(int index)
// this will replace the element present at specified index with the object provided and returns the old object
Object set(int index, Object object)
// returns the index of the object provided
int indexOf(object object)
// returns the index of the object provided
int lastIndexOf(object object)
Insertion order is preserved in the List. This means that the elements
will be kept in the order in which they were inserted. We can keep duplicate
elements in it.
Because List is an abstract interface, you cannot directly
create an instance. You must instead make an instance of a class that
implements the List interface. ArrayList, LinkedList, and
Vector are some classes that implement the List interface.
ArrayList
The ArrayList is a class in java.util package that implements
the List interface. It offers dynamic arrays that can expand as needed.
ArrayLists can be created with a fixed capacity and will automatically
resize when that capacity is reached. We can insert
heterogeneous objects into it. Inserting NULL
values is also possible.
👉 Here are some ArrayList constructors:
// We can use this constructor to create an empty ArrayList with the default capacity (10).
// When the ArrayList is full, a new ArrayList with a new capacity is created.
ArrayList()
// We can use this constructor to create an ArrayList with the specified capacity.
ArrayList(int capacity)
// We can use this constructor to create an ArrayList with the specified Collection object.
// Using this constructor, we can convert any Collection object to ArrayList.
ArrayList(Collection collection)
👉 Here's some code that shows how to use List and ArrayList:
import java.util.*;
public class ArrayListDemo {
public static void main(String[] args) {
System.out.println("ArrayList using Homogeneous elements : ");
List nameList = new ArrayList();
nameList.add("Sally Becton");
nameList.add("Larry Bowen");
nameList.add("Elizabeth A. Fabian");
nameList.add("Maria Juarez");
Object[] nameArray = nameList.toArray();
for (int i=0; i<nameArray.length; i++) {
System.out.println(i + " : " + nameArray[i]);
}
}
}
We created an ArrayList object and assigned it to a List in the
preceding code snippet. We can do this because ArrayList is the
List interface's implementation class. Then we populate the ArrayList
with some homogeneous elements (String type). In line 13, we use the
toArray() method to convert the ArrayList into an array
object. This toArray() method was inherited by
ArrayList from the Collection interface. The array values
are then printed using the index.
👉 Consider the following code snippet:
import java.util.*;
public class ArrayListHeteroDemo {
public static void main(String[] args) {
System.out.println("ArrayList using Heretogeneous elements : ");
ArrayList differentList = new ArrayList();
differentList.add(4362);
differentList.add("Aberdeen");
differentList.add(true);
differentList.add(13.97);
differentList.add("wsalmon@example.com");
for (int i=0; i<differentList.size(); i++) {
System.out.println(i + " : " + differentList.get(i));
}
System.out.println("\nIndex of element \'13.97\' is : " + differentList.indexOf(13.97));
}
}
In this case, we've added some heterogeneous objects to the
ArrayList. The ArrayList elements are then printed using the
get(int index) method and retrieve the index of an existing element
using the indexOf(Object obj)
method.
👉 Here's an example of the generic type ArrayList:
import java.util.*;
public class ArrayListGenericDemo {
public static void main(String[] args) {
List<String> genericList = new ArrayList<>();
try {
genericList.add("Sally Becton");
genericList.add("Larry Bowen");
genericList.add("Elizabeth A. Fabian");
//genericList.add(100.77);
genericList.add(String.valueOf(123.77));
//System.out.println(genericList);
System.out.println("ArrayList elements : ");
for(String str : genericList) {
System.out.println(str);
}
//double _value = genericList.get(3);
System.out.println("\nDouble value : " + Double.parseDouble(genericList.get(3)));
} catch(Exception e) {
e.printStackTrace();
} finally {
//System.out.println(genericList);
}
}
}
We create a String-type generic ArrayList and populate it with String
elements. In line 12, we insert a double element; in line 13, we
insert another double element, but this time we convert it to a String. If we
run this code, we will get a compile-time error that says "incompatible types: double cannot be converted to String" for line 12. There will be no complaints about line
13. As a result, when we make an ArrayList generic, we
can't store other primitive or custom data types in it. We must use the
generic type to store other primitive data types.
ArrayList also supports the RandomAccess interface. This is why
we can quickly retrieve values from the large ArrayList from any
position.
However, inserting or deleting any element from a large ArrayList will take
time. Because inserting an element in the middle of a large ArrayList
necessitates shifting the other elements to insert the element at the
specified index. As a result, this will also take time.
Synchronized ArrayList
ArrayList objects are non-synchronized by default. However,
using the Collections class's synchronizedList() method, we can
create a synchronized
version of the ArrayList object, meaning multiple threads can access the list
concurrently without causing any inconsistencies or errors.
👉 Here's an example:
import java.util.*;
public class ArrayListSynchronizedDemo {
public static void main(String[] args) {
String _status = "AVAILABLE";
List<String> seatlList = new ArrayList<>();
seatlList.add("Seat no 1 is " + _status);
seatlList.add("Seat no 2 is " + _status);
seatlList.add("Seat no 3 is " + _status);
seatlList.add("Seat no 4 is " + _status);
seatlList.add("Seat no 5 is " + _status);
seatlList.add("Seat no 6 is " + _status);
seatlList.add("Seat no 7 is " + _status);
seatlList.add("Seat no 8 is " + _status);
seatlList.add("Seat no 9 is " + _status);
seatlList.add("Seat no 10 is " + _status);
System.out.println("Available seats : " + seatlList + "\n");
// Create a synchronized ArrayList from existing list
List<String> synchronizedList = Collections.synchronizedList(seatlList);
// Creating multiple threads to change seat status
Thread t2_booked = new Thread(new SeatStatusModifier(synchronizedList, 2, "BOOKED"));
t2_booked.setName("t2_booked");
Thread t5_booked = new Thread(new SeatStatusModifier(synchronizedList, 5, "BOOKED"));
t5_booked.setName("t5_booked");
Thread t3_booked = new Thread(new SeatStatusModifier(synchronizedList, 3, "BOOKED"));
t3_booked.setName("t3_booked");
Thread t6_unavailable = new Thread(new SeatStatusModifier(synchronizedList, 6, "UN-AVAILABLE"));
t6_unavailable.setName("t6_unavailable");
Thread t8_booked = new Thread(new SeatStatusModifier(synchronizedList, 8, "BOOKED"));
t8_booked.setName("t8_booked");
Thread t9_booked = new Thread(new SeatStatusModifier(synchronizedList, 9, "BOOKED"));
t9_booked.setName("t9_booked");
Thread t2_canceled = new Thread(new SeatStatusModifier(synchronizedList, 2, "CANCELED"));
t2_canceled.setName("t2_canceled");
// starts all the threads
t2_booked.start();
t5_booked.start();
t3_booked.start();
t8_booked.start();
t9_booked.start();
t2_canceled.start();
t6_unavailable.start();
}
}
// A runnable class to modify the Seat Status
class SeatStatusModifier implements Runnable {
private List<String> list;
int seatNo;
String status;
public SeatStatusModifier(List<String> list, int seatNo, String status) {
this.list = list;
this.seatNo = seatNo;
this.status = status;
}
@Override
public void run() {
synchronized (list) {
System.out.println("\n" + java.time.LocalDateTime.now() + " : Make seat no "+ (this.seatNo) + " : " + this.status);
list.set(this.seatNo-1, "Seat no " + (this.seatNo) + " is : " + this.status);
System.out.println(Thread.currentThread().getName() + ": " + list);
}
}
}
We created a non-synchronized ArrayList with ten elements in the
preceding code. Each element represents a seat with a corresponding status.
Then we use the Collections.synchronizedList() method to create a
synchronized ArrayList, which takes an ArrayList as a parameter and
returns a synchronized version of the list.
We've made a couple of Threads, each with its own
SeatStatusModifier class that implements the Runnable interface
and overrides the run() method. We have a synchronized block in
the run() method. The ArrayList object, mainly the seat status, is
modified within a synchronized block to ensure thread-safe access, and the
modified list elements are printed. Because the list is synchronized, multiple
threads can safely modify it without causing inconsistencies or errors.
It should be noted that the order of the modified elements in the list may
vary depending on how the threads access the list.
👉 It is important to note that using a synchronized ArrayList can add
some performance overhead, so it should only be used when necessary, such as
when multiple threads need to access the same list concurrently.
Happy coding!!! 😊
No comments:
Post a Comment