I have a very weird problem, And I want to do it extremely efficient way. In my app milliseconds count..
I have four ArrayLists of Strings
title desc, price, usageArray;
The first three contains data where as usageArray contains data and "NONE" at some places e.g
UsageArray
a b c NONE D NONE
etc
I want to remove the "NONE" From usageArray in such a way that e.g let the index of First NONE is 3 then the third element in title, desc and price is also remove.
How can I do this in extremely efficient way
First of all, I'll suggest you to create a class, say, Book, containing all those attributes and have a List<Book>, instead of having 4 different list for all of them.
P.S. : In general, whenever you see yourself modifying or working with multiple Lists in parallel, that is an indication that it's time to create a new class.
class Book {
String title;
String desc;
BigDecimal price;
String usage;
}
Then you have a list like this:
List<Book> books;
Now, to remove all the indices where usage is NULL, is as easy as:
ListIterator<Book> iterator = books.listIterator();
while (iterator.hasNext()) {
Book book = iterator.next();
if (book.getUsage().equals("NULL")) {
iterator.remove();
}
}
Also, wherever you are having "Null" as your string value, you should consider changing it to null.
Note: You should pay attention while removing elements from your List. You should always use an iterator while doing that.
See also:
Iterating through a Collection, avoiding ConcurrentModificationException when removing in loop
This is tested code.
public class mainclass {
public static void main(String ar[])
{
List<Integer> indexes = new ArrayList<Integer>();
List<String> title = new ArrayList<String>();
title.add("title 1");
title.add("title 2");
title.add("title 3");
List<String> desc = new ArrayList<String>();
desc.add("desc 1");
desc.add("desc 2");
desc.add("desc 3");
List<String> price = new ArrayList<String>();
price.add("price 1");
price.add("price 2");
price.add("price 3");
List<String> usageArray = new ArrayList<String>();
usageArray.add("usage 1");
usageArray.add("NONE");
usageArray.add("usage 1");
for (String string : usageArray) {
if(string.equalsIgnoreCase("NONE"))
{
indexes.add(usageArray.indexOf(string));
}
}
for (Integer index : indexes) {
price.remove(index);
desc.remove(index);
title.remove(index);
usageArray.remove(index);
}
}
}
Try this
int index = 0;
for (int i = 0; i < usageArray.size(); i++) {
if (usageArray.get(i).contains("NONE")) {
index=usageArray.indexOf("NONE");
usageArray.remove(index);
title.remove(index);
desc.remove(index);
price.remove(index);
i--;
}
}
This (as usual) depends. How big is your data set? Have you tested a basic approach and seen that it takes too much time? Simple LinkedLists for example are more efficient (compared to ArrayList) when it comes to removal. What do you want to end up with, a data structure that is fast for looking up things in what way? By index? By a key?
What do you need to be fast, the actual filtering or afterwards when you are done?
Multiple ways of doing this even in simple cases. What about merging into into one object with fields for each column:
class Data {
String title;
String desc;
String price;
String usage;
}
And then:
LinkedList<Data> allData = ...;
for (Iterator iter=allData.iterator();iter.hasNext();) {
Data data = iter.next();
if ("NONE".equals(data.usage)) { //see note about using something else here
iter.remove();
}
}
//allData is now cleaned of any entries with USAGE of NONE
Usually quicker than if using a ArrayList, certainly quicker than using multiple lists etc etc. But again depends.
Might for example want to have the usage in a separate class depending on your data modelling needs.
For further performance regardless of algorithm (which is the important part so only for fun, always measure!) consider:
Make usage an enum or int for more efficient comparison or a
String constant so don't need equals(), can instead use usage == NONE
check the Following Code,
public static void main(String ar[])
{
List<String> title = new ArrayList<String>();
title.add("title 1");
title.add("title 2");
title.add("title 3");
List<String> desc = new ArrayList<String>();
desc.add("desc 1");
desc.add("desc 2");
desc.add("desc 3");
List<String> price = new ArrayList<String>();
price.add("price 1");
price.add("price 2");
price.add("price 3");
List<String> usageArray = new ArrayList<String>();
usageArray.add("usage 1");
usageArray.add("NONE");
usageArray.add("usage 1");
int index = -1;
for (String string : usageArray) {
if(string.equalsIgnoreCase("NONE"))
{
index = usageArray.indexOf(string);
usageArray.remove(string);
price.remove(index);
desc.remove(index);
title.remove(index);
}
}
}
Related
After reading several questions and examples I came with this example which I modified a bit to make it work as expected.
Collections.sort(listToOrder, Comparator.comparing(item -> someObject.getListOfLongs().indexOf(item.getId())));
So listToOrder is a list of MyObject which has name and id, so I need to order listToOrder in the same order as listOfLongs.
With the example of code given it work as expected however if the listToOrder is different in size it fails, wondering how I could make it to work even if the sizes are different.
Edit:
I misread, the error I was getting was an IndexOutOfBoundsException which wasn't triggered by the line of code I put up there, it was because of a manual log.
List.indexOf() returns -1 if the element is not found, which means such items will be ordered first in the resulting sorted list.
Without ordering data, the only other sensible way to handle such elements is to order them last:
Collections.sort(listToOrder, Comparator.comparing(item -> someObject.getListOfLongs().contains(item.getId()) ? someObject.getListOfLongs().indexOf(item.getId()) : Integer.MAX_VALUE));
This has nothing to do with sorting, but ordering. Having the following object with full-args constructor and getters:
public static class MyObject {
private final long id;
private final String name;
}
... and the following data in a random order ...
List<Integer> ids = Arrays.asList(5,4,7,0,2,1,3,8,6);
List<MyObject> list = Arrays.asList(
new MyObject(1, "one"),
new MyObject(3, "three"),
...
new MyObject(6, "six"),
new MyObject(8, "eight")
);
The solution you are looking for is this:
List<MyObject> newList = new ArrayList<>(list);
for (int i=0; i<ids.size(); i++) {
int id = ids.get(i);
for (MyObject myObject: list) {
if (myObject.getId() == id) {
newList.set(i, myObject);
break;
}
}
}
Simply find the object with the matching ID and set it to a new list. There is no dedicated method to do that.
Sorry if the title is not clear, I'm not very good with programming jargon.
I have 2 string ArrayLists and an integer ArrayList obtained from one method which is passed to a separate method through the collection LinkedHashMap< String, List< String>>. However, when I try to set the integer ArrayList into a empty ArrayList declared in the receiving method, it shows the syntax error: "incompatible types: List< String> cannot be converted to List< Integer>".
Starter Method:
public static void main(String[] args) {
try{
LinkedHashMap lhm = new LinkedHashMap();
List<String> listEPC = new ArrayList<String>();
List<String> listTimeStamp = new ArrayList<String>();
List<Integer> listAntenna = new ArrayList<Integer>();
String tagID = "EQ5237";
String TimeStampStr = "12:23:22";
int tagAntenna = 2;
listEPC.add(tagID);
listTimeStamp.add(TimeStampStr);
listAntenna.add(tagAntenna);
lhm.put("epcs", listEPC);
lhm.put("timestamps", listTimeStamp);
lhm.put("antennas", listAntenna);
insertData insert = new insertData();
insert.insertData(lhm); //send map with values to new method
}catch(Exception e){
e.printStackTrace();
}
}
Receiving Method:
public class insertData {
public void insertData(LinkedHashMap<String, List<String>> readMap) {
List<String> listEPC = new ArrayList<String>();
List<String> listTimeStamp = new ArrayList<String>();
List<Integer> listAntenna = new ArrayList<Integer>();
String EPC = null;
String TimeStamp = null;
Integer Antenna = null;
listEPC = readMap.get("epcs");
listTimeStamp = readMap.get("timestamps");
listAntenna = readMap.get("antennas"); //error message here
for(int i=0; i<readMap.size(); i++){
EPC = listEPC.get(i);
TimeStamp = listTimeStamp.get(i);
Antenna = listAntenna.get(i);
System.out.println("Entry " + i );
System.out.println("Values: " + EPC + TimeStamp + Antenna);
}
}
}
This code works only if I change all instances of integers to strings, which is not what I would like in my actual code. Why is it so and how do I work around it?
You can't assign a List<String> to a List<Integer>. The elements are fundamentally different types.
You would need to construct a new List:
List<Integer> listOfIntegers = new ArrayList<>();
for (String entry : listOfStrings) {
listOfIntegers.add(Integer.valueOf(entry);
}
Of course, you also need to handle the possibility that elements of the list cannot be parsed as integers.
However, you are just throwing away type information by stuffing everything into a single map. It would be better to pass the three lists separately:
insertData(listEPC, listTimestamp, listAntenna);
and then you can have different list types in the method signature:
void insertData(
List<String> listEPC,
List<String> listTimestamp,
List<Integer> listAntenna) { ... }
I am going to include the proper answer at the bottom, but in regards to your question title, you'll have to change your method signature to:
LinkedHashmap<String, List<?>> readMap;
Then either cast the lists, which will cause an unsafe cast. eg.
List<String> listEPC = (List<String>)readMap.get("epcs");
Or cast the object.
List<?> listEPC = readMap.get("epcs");
Then in the loop cast.
EPC = (String)listEPC.get(i);
Note, these are not good solutions.
What you should have is one List that contains an object with all of the data's you need.
I can imagine the thought process went something along these lines, "I have these things, and they contain two strings and an integer. I will create a variable for each." Then you ask the question, "How do I create a collection of these things?"
The wrong answer to this question is, "I will make a list for each value, and match associated values by index." The correct answer is, "I will create a class to represent my data, and store that in a list." This is the basic essence of object orient programming (welcome to java).
First we design the class:
class EPCThing{
String EPC;
String timeStamp;
int Antennas;
public EPCThing(String tagId, String timeStamp, int antennas){
EPC=tagId;
this.timeStamp = timeStamp;
Antennas = antennas;
}
#Override
public String toString(){
return "Values: " + EPC + TimeStamp + Antenna
}
}
Now your program's main method will be something like.
List<EPCThing> things = new ArrayList<>();
String tagID = "EQ5237";
String TimeStampStr = "12:23:22";
int tagAntenna = 2;
EPCThing thing = new EPCThing(tagID, TimeStampStr, tagAntenna);
things.add(thing);
insertData insert = new insertData();
insert.insertData(things);
Then we can fix your insertData method
public void insertData(List<EPCThing> things) {
for(int i=0; i<things.size(); i++){
System.out.println("Entry " + i );
System.out.println("Values: " + things.get(i));
}
}
I am comparing three arrays of Strings using the two classes below. Without using any hash maps or changing the structure of my code too much (I can't change the signature of findMatchingElements()), is there a way to minimize the number of comparisons that my method makes, in order to construct the new array of shared elements?
In TestRun.java I tested my code on three arrays with 8 elements each, which resulted in 46 comparisons made. I want to achieve a lower number of comparisons. Is there a way?
I tried using the remove() method to remove a string from the collection once it was successfully compared to a matching element from the query collection. That prevented some redundant comparisons, but it did not result in a significant reduction.
import java.util.*;
public class CommonElements {
int originalCollectionCount = 0;
Object[] originalCollections;
int listCount = 1;
int matchCount;
int comparisonCount = 0;
public Comparable[] findMatchingItems(Object[] collections)
{
String[] queryArray = (String[])collections[0];
String[] secondaryArray = (String[])collections[1];
ArrayList<String> queryList = new ArrayList(Arrays.asList(queryArray));
ArrayList<String> secondaryList = new ArrayList(Arrays.asList(secondaryArray));
ArrayList<String> commonList = new ArrayList();
int i = 0;
if(listCount == 1){
originalCollectionCount = collections.length;
originalCollections = collections;
}
listCount ++;
for(String x:queryList)
{
for(String y:secondaryList)
{
comparisonCount++;
if(x.compareTo(y) == 0)
{
commonList.add(x); //add mutually shared item to commonList
secondaryList.remove(y); //remove mutually shared item from consideration
if(originalCollectionCount == listCount) //if every list has been examined
{
System.out.println(commonList.get(i));
}
i++;
break;
}
}
}
String[] commonListResult = new String[commonList.size()];
commonList.toArray(commonListResult);
if(originalCollectionCount > listCount){
findMatchingItems(new Object[] {commonListResult,originalCollections[listCount]});}
if (collections.length == 0) {
return new Comparable[0];
} else if (collections.length == 1) {
return (Comparable[]) collections[0];
}
return commonListResult;
}
public int getComparisons(){
return comparisonCount;}
}
public class TestRun {
private final static String[] COLLECTION_5_1 = {"Pittsburgh", "New York", "Chicago", "Cleveland", "Miami", "Dallas", "Atlanta", "Detroit"};
private final static String[] COLLECTION_5_2 = {"Dallas", "Atlanta", "Cleveland", "Chicago", "Washington", "Houston", "Baltimore", "Denver"};
private final static String[] COLLECTION_5_3 = {"Chicago", "Kansas City", "Cleveland", "Jacksonville", "Atlanta", "Tampa Bay", "Dallas", "Seattle"};
public static void main(String[] args) {
new TestRun();
}
public TestRun() {
CommonElements commonElements = new CommonElements();
Object[] input = new Object[3];
input[0] = COLLECTION_5_1;
input[1] = COLLECTION_5_2;
input[2] = COLLECTION_5_3;
System.out.println("Matching items:");
commonElements.findMatchingItems(input);
System.out.println(commonElements.comparisonCount + " comparisons made.");
}
}
You could run a single advanced for loop as below provide the length for both array are same if not run throug it accordingly.
for(String str:arrayStr1){
if(arrayStr2.contains(str)){
newArray.add(str);
}
}
List<String> list_5_1 = new ArrayList<>(Arrays.asList(COLLECTION_5_1));
//[Pittsburgh, New York, Chicago, Cleveland, Miami, Dallas, Atlanta, Detroit]
List<String> list_5_2 = new ArrayList<>(Arrays.asList(COLLECTION_5_2));
//[Dallas, Atlanta, Cleveland, Chicago, Washington, Houston, Baltimore, Denver]
list_5_1.retainAll(list_5_2);
//[Chicago, Cleveland, Dallas, Atlanta]
We have to pass list returned from Arrays.asList, as Arrays.asList method returns only immutable list.
am comparing three arrays of Strings using the two classes below. Without using any hash maps or changing the structure of my code too much (I can't change the signature of findMatchingElements()), is there a way to minimize the number of comparisons that my method makes, in order to construct the new array of shared elements?
Sure. Your nested loops have complexity of O(m*n). When you create a temporary HashMap, you can reduce it to O(m+n) and gain a lot for big inputs. From practical POV, somewhere around length of 10 it should get faster than your solution.
I'm giving no code as it's too straightforward.
I am fairly new to Java and I have stumbled across a problem I cannot figure out for the life of me. First let me explain what I am trying to do then I will show you the code I have so far.
I have a webservice that returns an array of arrays(which include company and lines of business strings). I wish to transform this into a string list, which I did in the first line of code below. Then I wish to Iterate through the list and every I come across a different value for company, I want to create a new ArrayList and add the associated line of business to the new list. Example output of webservice: 80,80,64,64 (this is presorted so the same companies will always be grouped together) the associated lobs would be 1,2,3,4 respectively. What I want: arraylist[0]: 1,2 arrayList[1]: 3,4
What I have so far:
List coList = Arrays.asList(coArray);
//create list of lists
List<List<String>> listOfLists = new ArrayList<List<String>>();
String cmp = "";
for (int i=0;i<coList.size();i++){//loop over coList and find diff in companies
String currentCo = ((__LOBList)coList.get(i)).getCompany();
String currentLob = ((__LOBList)coList.get(i)).getLobNum();
if(i<coArray.length-1){
String nextCo = ((__LOBList)coList.get(i+1)).getCompany();
if((currentCo.equals(nextCo))){
//do nothing companies are equal
}else{
log("NOT EQUAL"); //insert logic to create a new array??
ArrayList<String> newList = new ArrayList<String>();
// for(int j=0;j<coList.size();j++){
newList.add( ((__LOBList)coList.get(i)).getLobNum());
// }
for(int k=0; k<listOfLists.size();k++){//loop over all lists
for(int l=0;l<listOfLists.get(k).size();l++){ //get first list and loop through
}
listOfLists.add(newList);
}
}
}
}
My problem here is that it is not adding the elements to the new string array. It does correctly loop through coList and I put a log where the companies are not equal so I do know where I need to create a new arrayList but I cannot get it to work for the life of me, please help!
Yes you can do this but it's really annoying to write in Java. Note: This is a brain dead simple in a functional programming language like Clojure or Haskell. It's simply a function called group-by. In java, here's how I'd do this:
Initialize a List of Lists.
Create a last pointer that is a List. This holds the last list you've added to.
Iterate the raw data and populate into the last as long as "nothing's changed". If something has changed, create a new last.
I'll show you how:
package com.sandbox;
import java.util.ArrayList;
import java.util.List;
public class Sandbox {
public static void main(String[] args) {
ArrayList<String> rawInput = new ArrayList<String>();
rawInput.add("80");
rawInput.add("80");
rawInput.add("60");
rawInput.add("60");
new Sandbox().groupBy(rawInput);
}
public void groupBy(List<String> rawInput) {
List<List<String>> output = new ArrayList<>();
List<String> last = null;
for (String field : rawInput) {
if (last == null || !last.get(0).equals(field)) {
last = new ArrayList<String>();
last.add(field);
output.add(last);
} else {
last.add(field);
}
}
for (List<String> strings : output) {
System.out.println(strings);
}
}
}
This outputs:
[80, 80]
[60, 60]
Of course, you can do what the other guys are suggesting but this changes your data type. They're suggesting "the right tool for the job", but they're not mentioning guava's Multimap. This will make your life way easier if you decide to change your data type to a map.
Here's an example of how to use it from this article:
public class MutliMapTest {
public static void main(String... args) {
Multimap<String, String> myMultimap = ArrayListMultimap.create();
// Adding some key/value
myMultimap.put("Fruits", "Bannana");
myMultimap.put("Fruits", "Apple");
myMultimap.put("Fruits", "Pear");
myMultimap.put("Vegetables", "Carrot");
// Getting the size
int size = myMultimap.size();
System.out.println(size); // 4
// Getting values
Collection<string> fruits = myMultimap.get("Fruits");
System.out.println(fruits); // [Bannana, Apple, Pear]
Collection<string> vegetables = myMultimap.get("Vegetables");
System.out.println(vegetables); // [Carrot]
// Iterating over entire Mutlimap
for(String value : myMultimap.values()) {
System.out.println(value);
}
// Removing a single value
myMultimap.remove("Fruits","Pear");
System.out.println(myMultimap.get("Fruits")); // [Bannana, Pear]
// Remove all values for a key
myMultimap.removeAll("Fruits");
System.out.println(myMultimap.get("Fruits")); // [] (Empty Collection!)
}
}
It sounds to me like a better choice would be a Map of Lists. Let the company ID be the key in the Map and append each new item for that company ID to the List that's the value.
Use the right tool for the job. Arrays are too low level.
Create a Map<String, List<Bussiness>>
Each time you retrieve a company name, first check if the key is already in the map. If it is, retrieve the list and add the Bussiness object to it. If it is not, insert the new value when a empty List and insert the value being evaluated.
try to use foreach instead of for
just like
foreach(List firstGroup in listOfLists)
foreach(String s in firstGroup)
............
Thanks for the input everyone!
I ended up going with a list of lists:
import java.util.*;
import search.LOBList;
public class arraySearch {
/**
* #param args
*/
public static void main(String[] args) {
LOBList test = new LOBList();
test.setCompany("80");
test.setLOB("106");
LOBList test1 = new LOBList();
test1.setCompany("80");
test1.setLOB("601");
LOBList test2 = new LOBList();
test2.setCompany("80");
test2.setLOB("602");
LOBList test3 = new LOBList();
test3.setCompany("90");
test3.setLOB("102");
LOBList test4 = new LOBList();
test4.setCompany("90");
test4.setLOB("102");
LOBList test5 = new LOBList();
test5.setCompany("100");
test5.setLOB("102");
LOBList BREAK = new LOBList();
BREAK.setCompany("BREAK");
BREAK.setLOB("BREAK");
BREAK.setcompany_lob("BREAK");
// create arraylist
ArrayList<LOBList> arlst=new ArrayList<LOBList>();
// populate the list
arlst.add(0,test);
arlst.add(1,test1);
arlst.add(2,test2);
arlst.add(3,test3);
arlst.add(4,test4);
arlst.add(5,test5);
//declare variables
int idx = 0;
String nextVal = "";
//loops through list returned from service, inserts 'BREAK' between different groups of companies
for(idx=0;idx<arlst.size();idx++){
String current = arlst.get(idx).getCompany();
if(idx != arlst.size()-1){
String next = arlst.get(idx+1).getCompany();
nextVal = next;
if(!(current.equals(next))){
arlst.add(idx+1,BREAK);
idx++;
}
}
}
//add last break at end of arrayList
arlst.add(arlst.size(),BREAK);
for(int i=0;i<arlst.size();i++){
System.out.println("co:" + arlst.get(i).getCompany());
}
//master array list
ArrayList<ArrayList<LOBList>> mymasterList=new ArrayList<ArrayList<LOBList>>();
mymasterList = searchListCreateNewLists(arlst);
//print log, prints all elements in all arrays
for(int i=0;i<mymasterList.size();i++){
for(int j=0;j<mymasterList.get(i).size();j++){
System.out.println("search method: " + mymasterList.get(i).get(j).getCompany());
}
System.out.println("end of current list");
}
}
//method to loop over company array, finds break, creates new array list for each company group,
//adds this to a list of lists(masterList)
public static ArrayList<ArrayList<LOBList>> searchListCreateNewLists(ArrayList<LOBList> list){
ArrayList<ArrayList<LOBList>> masterList=new ArrayList<ArrayList<LOBList>>();
int end = 0;
int start = 0;
int index = 0;
for(int i=0;i<list.size();i++){
if(list.get(i).getCompany().equals("BREAK")){
end = i;//end is current index
masterList.add(new ArrayList<LOBList>());
for(int j = start;j<end;j++){
masterList.get(index).add(list.get(j));
}
index++;
start = i+1;
}
}
return masterList;
}
}
The output is:
search method: 80
search method: 80
search method: 80
end of current list
search method: 90
search method: 90
end of current list
search method: 100
end of current list
So all company LOBList objects with Company: 80, are grouped together in a list, as are 90 and 100.
To iterate through the list you can use
ListIterator litr = coList.listIterator();
while(litr.hasNext()){
}
I am a beginner to java, and need some help.
I am trying to convert an Abstract Data type Foo which is an associated list to an Arraylist of the strings B. How do you loop through the list and add each string to the array.
I may be over thinking it, but I am lost now.
Thanks for the help in advance.
Instantiate a new ArrayList:
List<String> myList = new ArrayList<String>();
Iterate over your data structure (with a for loop, for instance, more details on your code would help.) and for each element (yourElement):
myList.add(yourElement);
If you have an arraylist of String called 'foo', you can easily append (add) it to another ArrayList, 'list', using the following method:
ArrayList<String> list = new ArrayList<String>();
list.addAll(foo);
that way you don't even need to loop through anything.
You should be able to do something like:
ArrayList<String> list = new ArrayList<String>();
for( String s : foo )
{
list.add(s);
}
Array list can be implemented by the following code:
Arraylist<String> list = new ArrayList<String>();
list.add(value1);
list.add(value2);
list.add(value3);
list.add(value4);
Well, you have to iterate through your abstract type Foo and that depends on the methods available on that object. You don't have to loop through the ArrayList because this object grows automatically in Java. (Don't confuse it with an array in other programming languages)
Recommended reading.
Lists in the Java Tutorial
thanks for the help, I've solved my problem :) Here is the code if anyone else needs it :D
import java.util.*;
public class HelloWorld {
public static void main(String[] Args) {
Map<Integer,List<Integer>> map = new HashMap<Integer,List<Integer>>();
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(9);
list.add(11);
map.put(1,list);
int First = list.get(1);
int Second = list.get(2);
if (First < Second) {
System.out.println("One or more of your items have been restocked. The current stock is: " + First);
Random rn = new Random();
int answer = rn.nextInt(99) + 1;
System.out.println("You are buying " + answer + " New stock");
First = First + answer;
list.set(1, First);
System.out.println("There are now " + First + " in stock");
}
}
}
If you're using Java 9, there's an easy way with less number of lines without needing to initialize or add method.
List<String> list = List.of("first", "second", "third");