I have two classes, Image and Channel.Image has an imageId and Channel has a channelId which uniquely identify an Image and Channel object.Some other attributes are also present.
Image class also has a channelId, using which I determine to which channel the image has been assigned to.
I have a two ArrayLists of Image and Channel respectively.
List<Image> imageList = getItemist("image");
List<Image> channelList = getItemList("channel");
Now, I would like to remove all those image objects from the image list which contain channelId which are present in the channel objects of the channelList.
As of now, I am iterating the two lists and then comparing the channelId, putting the Image objects in a TreeSet and finally returning a list.Can you please help me with a solution that is simpler or more efficient ?
This sounds like a good use-case for a ListIterator:
ListIterator iter = imageList.listIterator();
Image curr = null;
while (iter.hasNext){
curr = iter.next();
for (Image img : chanelList){
if (img.chanelId == curr.chanelId){ //assuming chanelId is a primitive
iter.remove(curr); //remove curr
break; //break from the for loop to go on to the next image in imageList
}
//implicit: else continue; (i.e. go on to check the next image in chanelList)
}
}
Note that this is an O(n^2) algorithm that won't scale well for large list sizes. There are ways to optimize it further (see #dasblinkenlight's comment, for one), but for purposes of conceptual clarity, I'll limit the scope of this answer to this.
Inserting n elements to a TreeSet requires O(n*log(n)) time. However you don't need the Set to be ordered. A HashSet should be faster in the average case (of course you can still be unlucky with the hash codes).
You can then modify the list based on the set:
HashSet<Integer> channelIds = new HashSet<>();
for (Image channel : channelList) {
channelIds.add(channel.channelId);
}
// following removal code is specialized for lists that
// allow random access, like ArrayList
final int size = imageList.size();
int j = 0;
for (int i = 0; i < size; i++) {
Image image = imageList.get(i);
if (!channelIds.contains(image.channelId)) {
imageList.set(j++, image);
}
}
if (j < size) {
imageList.subList(j, size).clear();
}
Related
Right now I have an array of "Dragon"s. Each item has two values. An ID and a Count. So my array would look something like this:
Dragon[] dragons = { new Dragon(2, 4),
new Dragon(83, 199),
new Dragon(492, 239),
new Dragon(2, 93),
new Dragon(24, 5)
};
As you can see, I have two Dragons with the ID of 2 in the array. What I would like to accomplish is, when a duplicate is found, just add the count of the duplicate to the count of the first one, and then remove the duplicate Dragon.
I've done this sort of successfully, but I would end up with a null in the middle of the array, and I don't know how to remove the null and then shuffle them.
This is what I have so far but it really doesn't work properly:
public static void dupeCheck(Dragon[] dragons) {
int end = dragons.length;
for (int i = 0; i < end; i++) {
for (int j = i + 1; j < end; j++) {
if (dragons[i] != null && dragons[j] != null) {
if (dragons[i].getId() == dragons[j].getId()) {
dragons[i] = new Item(dragons[i].getId(), dragons[i].getCount() + dragons[j].getCount());
dragons[j] = null;
end--;
j--;
}
}
}
}
}
You should most probably not maintain the dragon count for each dragon in the dragon class itself.
That aside, even if you are forced to use an array, you should create an intermeditate map to store your dragons.
Map<Integer, Dragon> idToDragon = new HashMap<>();
for (Dragon d : yourArray) {
// fetch existing dragon with that id or create one if none present
Dragon t = idToDragon.computeIfAbsent(d.getId(), i -> new Dragon(i, 0));
// add counts
t.setCount(t.getCount() + d.getCount());
// store in map
idToDragon.put(d.getId(), t);
}
Now the map contains a mapping between the dragons' ids and the dragons, with the correct counts.
To create an array out of this map, you can just
Dragon[] newArray = idToDragon.values().toArray(new Dragon[idToDragon.size()]);
You may be force to store the result in an array but that doesn't mean that you're force to always use an array
One solution could be using the Stream API, group the items adding the count and save the result into an array again. You can get an example of how to use the Stream API to sum values here. Converting a List<T> into a T[] is quite straightforward but anyways, you have an example here
The size of an array cannot be changed after it's created.
So you need to return either a new array or list containing the merged dragons.
public static Dragon[] merge(Dragon[] dragonArr) {
return Arrays.stream(dragonArr)
// 1. obtain a map of dragon IDs and their combined counts
.collect(groupingBy(Dragon::getId, summingInt(Dragon::getCount)))
// 2. transform the map entries to dragons
.entrySet().stream().map(entry -> new Dragon(entry.getKey(), entry.getValue()))
// 3. collect the result as an array
.toArray(Dragon[]::new);
}
So I'm trying to go through an arraylist of objects that all have a certain strength value and depending on their strength value, they go into the bigger 2d array based on that. So if their strength value is 0 then they go in the 0th array of the bigger one and this is what my code looks like so far
private ArrayList<Battleable> arr;
public BattleDeck() {
arr = new ArrayList<Battleable>();
for (Battleable creature: arr){
arr.add(creature);
}
}
public Battleable[][] export2Darray() {
//returns a two-dimensional ragged array where each row
// contains a deep copy of all of the Battleable objects
// in the BattleStack with the corresponding Level value
Battleable[][] retVal = new Battleable[10][];
int k = 0;
for (int i = 0; i<arr.size(); i++){
int levelOfObj = arr.get(i).getLevel();
if(levelOfObj == k) {
//insert it into retVal[0][0]
}
}
}
return retVal;
}
and I was wondering how I would do that? How do i syntax-tically say "get the obj that has strength 0 and put it in position 0 0 of my 2d array
A solution using Java 8 streams:
// group Battleables ArrayList by strength
Map<Integer, List<Battleable>> map =
arr.stream().collect(Collectors.groupingBy(Battleable::getStrength));
The result is a Map containing the Battleables as Lists with their strength as their key.
If you need the result as a jagged 2D array, sort the entries like this:
final Battleable[][] arrays = new Battleable[10][];
map.entrySet().forEach(entry -> {
arrays[entry.getKey()] = entry.getValue().toArray(new Battleable[entry.getValue().size()]);
});
Since arrays are of fixed size in Java, there is no clean way to add items to an array. You can resize the array each time by creating a new array each time, one larger than the last, and copying the data from the old array to the new array, but that would be messy and you would be reinventing a wheel called ArrayList. Modus Tollens has a good answer, but it uses some slightly advanced Java 8 concepts. Here's one way to write it without them:
public Battleable[][] export2Darray() {
Battleable[][] retVal = new Battleable[10][];
// create a map that will hold the items, arranged by level
Map<Integer, List<Battleable>> byLevel = new HashMap<>();
for (int i = 0; i < 10; i++) {
// initialize all levels with empty lists
byLevel.put(i, new ArrayList<>());
}
for (Battleable battleable : arr) {
int level = battleable.getLevel();
// get the list for this level and add to it
byLevel.get(level).add(battleable);
}
// Now we have a map from levels to lists of battleables;
// we need to turn each list into an array in our retVal
for (int level = 0; level < 10; level++) {
// get each list, convert it toArray and assign to slot in retVal
retVal[level] = byLevel.get(level).toArray(new Battleable[0]);
}
return retVal;
}
Here's a solution using ArrayLists, I am creating an ArrayList which will be referenced by strength, then inside of this I have another ArrayListwhich will have all of the Battleable objects of that strength level.
public ArrayList<ArrayList<Battleable>> exportBattleable() {
ArrayList<ArrayList<Battleable>> retVal = new ArrayList<ArrayList<Battleable>>();
for (int i = 0; i < arr.size(); i++){
retVal.get(arr.getLevel()).add(arr.get(i));
}
return retVal;
}
Now if you want to print all Battleable objects of strength = 3, you would do:
ArrayList<Battleable> strength3 = retVal.get(3);
for(Battleable battleable : strength3) {
System.out.println(battleable.toString());
}
This way you don't have to worry about re-sizing your arrays depending on how many Battleable objects you are adding in, same with strength levels, if you decide that instead of using strength levels from 0-9 that you wanted to use 0-20 you already have the ability to scale up or down.
int index = hash(value, size);
if (array.get(index) == null) {
array.add(index, new LinkedList<String>());
}
I have an array of LinkedLists: Arraylist<.LinkedList<.String>>, and I want to add a LinkedList in the array index given if one hasn't been initialized in it yet. Problem is, the index hashes to some number, but the index it goes to doesn't exist, since no LinkedList has been initialized in that index yet, but that's why i am doing if(array.get(index) == null) add a LinkedList, but apparently that's an incorrect comparison to do.
I tried .isEmpty(), null, and .equals(null), they don't work.
The ArrayList doesn't auto-grow, you have to grow it yourself:
int index = hash(value, size);
// grow
if (array.size() <= index) {
array.ensureCapacity(index + 1);
while (array.size() <= index)
array.add(null);
}
if (array.get(index) == null) {
array.set(index, new LinkedList<String>());
}
Note my change of array.add(index, ...) to array.set(index, ...) (docs): add will insert (and shift the rest of the array) while set will overwrite the element at the given index.
As long as you control hash you know the upper limits of its return value (let's call this limit) and you should only need to initialise your array once:
// initialise
ArrayList<LinkedList<String>> array = new ArrayList<>();
array.ensureCapacity(limit);
for (int i = 0; i < limit; ++i)
array.add(null);
// ...
// use
int index = hash(value, size); // hash(...) returns values in 0..limit-1
if (array.get(index) == null) {
array.set(index, new LinkedList<String>());
}
As Tamas Rev stated: you don't have to reinvent the wheel, just use a HashMap. As an additional bonus, HashMap will most likely be faster and generally be more memory efficient (the above scheme allocates an array of limit elements while HashMap can deal with any key type of any size).
I have my method which sorts all the movies in the array (dArray) of class dvd. now i need to sort available movies (setAvailable == true) ONLY. the array is dynamically filled in by customers entering movies from the menu. my code currently searches through the array and sorts all movies from A to B but now i want it to search and sort only the movies whereby d.setAvailable(true)... Thanks for your help. much appreciated
here is the sorted list of all the movies in the array:
if(e.getSource() == sortMovBtn)
{
if(dArray[0]==null)
{
JOptionPane.showMessageDialog(null,"No movies, please enter\na movie from the main menu","Error",JOptionPane.ERROR_MESSAGE);
}
else
{
BtnPanel.setVisible(false);
imgPnl.setVisible(false);
btnBackDvd.setVisible(true);
txtAreaSortDvd.setVisible(true);
sortDvdPnl.setVisible(true);
Dvd tmp;
for (int i = 0; i < manyDvd; i++)
{
for (int j = 0; j < (manyDvd - 1 - i); j++)
{
if (dArray[j].getTitle().compareTo(dArray[j+1].getTitle()) > 0)
{
tmp = dArray[j];
dArray[j] = dArray[j+1];
dArray[j+1] = tmp;
}
}
}
a = "";
for (int k = 0; k <manyDvd /*dArray.length*/; k++)
a += (dArray[k]);
txtAreaSortDvd.setText(a);
txtAreaSortDvd.setVisible(true);
txtAreaSortDvd.setEditable(false);
//Set font of text area
txtAreaSortDvd.setFont(new Font("Arial", Font.BOLD, 12));
//Initialize JScrollPane
JScrollPane pane1 = new JScrollPane(txtAreaSortDvd);
//Enable user to use wheel on mouse to scroll
pane1.setWheelScrollingEnabled(true);
//Set the scrollbar to always show
pane1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
//Add ScrollPane to Panel
sortDvdPnl.add(pane1);
//Add panel to frame
add(sortDvdPnl);
}
}
First, you shouldn't have code to work with your model (data) mixed in with UI code.
Second, you should not be sorting the data on the UI thread. Do the work on a background worker thread.
Third, you should not be coding up the sort yourself, especially not like that. Your sort is O(n^2). There are good options for sorting in java.util.Arrays for arrays or java.util.Collections for the collection classes.
There are two basic ways to go about sorting just the items that meet a certain criterion. You could sort the entire data set with a custom Comparator, matching first by the criterion and second by the rest of your sort criteria. Then your results would be at the start of the data, and you would only show the results until the first item that does not meet the criterion.
This has the advantage that it uses no extra space beyond what is already required to store your data.
A cleaner way would be to make a copy of just that portion of the data that meets the criterion, and then sort that. It would also be much easier to work with if the data was in a List instead of an array, since you would not need make two passes over the data: one to find out how many items match the criterion, and one to filter out the matching items. (In between, you would allocate an array to hold them.)
Suppose your data was in a List. You could get the available movies like this:
public List<Movie> getAvailable(List<Movie> movies) {
List<Movie> avail = new ArrayList<Movie>();
for (Movie movie : movies) {
if (movie.isAvailable()) {
avail.add(movie);
}
}
return avail;
}
You could then sort these movies (in their natural sort order) like this:
List<Movie> availOnly = getAvailable(movies);
Collections.sort(availOnly);
P.S. In Java 8 you will be able to do something like:
Iterable<Movie> avail = movies.filter(Movie::isAvailable).sorted();
use comparator http://docs.oracle.com/javase/6/docs/api/java/util/Comparator.html
Vector<String> totalProducts = Products.getProductNames();
Vector<String> selectedProducts = Products.getSelectedProductNames();
The selectedProducts vector is a subvector of totalProducts (meaning that selectedProducts contains one, more or all of the elements from totalProducts). What I want is to combine these two vectors and make a single JList, which contains all the elements from totalProducts, and with the elements of selectedProducts already selected.
What I tried:
Vector<Integer> indices = new Vector<Integer>();
JList prdList = new JList(totalProducts);
for(int i = 0; i < totalProducts.size(); i++)
{
for(String name : selectedProducts)
{
if(totalProducts.contains(name)) indices.add(i);
}
}
Object [] objIndices = indices.toArray();
//... Cast from Object [] to int [] ....
prdList.setSelectedIndices(intIndices);
...but this selects all the elements in the final JList.
Previously I tried:
JList prdList = new JList(totalProducts);
for(String tName : totalProducts)
{
for(String sName : selectedProducts)
{
if(totalProducts.contains(sName)) prdList.setSelectedValue(sName, false);
}
}
...but this one selected only the last element from the selectedProducts.
Can you please help me to do it right?
Your attempt that selects all items does so because you're iterating over each item, and if any item from the selectedProducts list is in the total list, adds the iteration item's index to the final selection list. Try changing your loop to something like this:
for(int i = 0; i < totalProducts.size(); i++)
{
String name = totalProducts.get(i);
if(selectedProducts.contains(name)) indices.add(i);
}
in debugging your first attempt (which looks like it should work, what was the contents of your intIndices array? because that looks like it should work, presuming your array conversion works.
however, since selectedproducts is guaranteed to be less items than total, you might want to iterate over that instead?
List<Integer> indices = new ArrayList<Integer>(selectedProducts.size());
for(String name : selectedProducts)
{
int index = totalProducts.indexOf(name);
if (index != -1)
indices.add(index);
}
although, since indexOf is a linear search through a list, it probably doesn't make much of a difference either way.
as for your second attempt, the ListSelectionModel has methods for adding a selected index (addSelectionInterval(int index0, int index1))
, you're using the one that sets (overwrites) the selection.
see http://download.oracle.com/javase/6/docs/api/javax/swing/ListSelectionModel.html
aside: you might want to use List<> instead of Vector<>, as vector has a lot of unecessary synchronization overhead. Unless you need the synchronization....
edit fixed copy+paste of add(i) with add(index)