49

I have a List of object and the list is very big. The object is

class Sample {
    String value1;
    String value2;
    String value3;
    String value4;
    String value5;
 }

Now I have to search for a specific value of an object in the list. Say if value3=='three' I have to return those objects (My search is not always based on value3)

The list is

List<Sample> list = new ArrayList<Sample>();

What is the efficient way of doing it?

6
  • 10
    Have you tried anything yourself? Commented Oct 30, 2012 at 12:29
  • 4
    why you don't creat a List<String>? (docs.oracle.com/javase/1.4.2/docs/api/java/util/List.html) Commented Oct 30, 2012 at 12:30
  • @Tichodroma He says he has a list of those objects. Commented Oct 30, 2012 at 12:30
  • @Tichodroma I think he means a List<Sample>, which is a list Commented Oct 30, 2012 at 12:30
  • If you're looking for efficiency for a LOT of objects, I would consider a heirarchy of sorted objects, then an efficent algorithm to skip over impossibilities. Commented Oct 30, 2012 at 12:40

7 Answers 7

67

You can give a try to Apache Commons Collections.

There is a class CollectionUtils that allows you to select or filter items by custom Predicate.

Your code would be like this:

Predicate condition = new Predicate() {
   boolean evaluate(Object sample) {
        return ((Sample)sample).value3.equals("three");
   }
};
List result = CollectionUtils.select( list, condition );

Update:

In java8, using Lambdas and StreamAPI this should be:

List<Sample> result = list.stream()
     .filter(item -> item.value3.equals("three"))
     .collect(Collectors.toList());

much nicer!

Sign up to request clarification or add additional context in comments.

7 Comments

The CollectionUtils method gives me this error : java.lang.NoSuchMethodError: No interface method stream()Ljava/util/stream/Stream; in class Ljava/util/List; or its super classes (declaration of 'java.util.List' appears in /system/framework/core-libart.jar , and I can't use Lambdas as Android SDK doesn't support Java 8.
@Yankee getting the same issue. did you get it to work?
@ono I used the Java8 method, worked great. But remember you'll have to add some Java 8 specific dependencies in your Gradle file
@Yankee i have the gradle imports but im still getting it. you have these two right? compileOptions {sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8}
@ono Yes. These are in my build.gradle(Module:app) and there's classpath 'me.tatarka:gradle-retrolambda:3.4.0' in my build.gradle (project)
|
54

Using Java 8

With Java 8 you can simply convert your list to a stream allowing you to write:

import java.util.List;
import java.util.stream.Collectors;

List<Sample> list = new ArrayList<Sample>();
List<Sample> result = list.stream()
    .filter(a -> Objects.equals(a.value3, "three"))
    .collect(Collectors.toList());

Note that

  • a -> Objects.equals(a.value3, "three") is a lambda expression
  • result is a List with a Sample type
  • It's very fast, no cast at every iteration
  • If your filter logic gets heavier, you can do list.parallelStream() instead of list.stream() (read this)


Apache Commons

If you can't use Java 8, you can use Apache Commons library and write:

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;

Collection result = CollectionUtils.select(list, new Predicate() {
     public boolean evaluate(Object a) {
         return Objects.equals(((Sample) a).value3, "three");
     }
 });

// If you need the results as a typed array:
Sample[] resultTyped = (Sample[]) result.toArray(new Sample[result.size()]);

Note that:

  • There is a cast from Object to Sample at each iteration
  • If you need your results to be typed as Sample[], you need extra code (as shown in my sample)



Bonus: A nice blog article talking about how to find element in list.

3 Comments

Well a.value3.equals("three") would look more natural, but your way is also "null safe", so keep that :D. Thanks for the update, but I still wonder why no one else noticed it. Since you're more into other languages (according to your tags) I can't/won't blame you, because String comparison might work differently there. But nevermind, it is fixed now.
@Tom "three".equals(((Sample) a).value3) would also work and is "null safe".
Also add import java.util.Objects;
5

If you always search based on value3, you could store the objects in a Map:

Map<String, List<Sample>> map = new HashMap <>();

You can then populate the map with key = value3 and value = list of Sample objects with that same value3 property.

You can then query the map:

List<Sample> allSamplesWhereValue3IsDog = map.get("Dog");

Note: if no 2 Sample instances can have the same value3, you can simply use a Map<String, Sample>.

3 Comments

No.. My search is not always based on value3.. It can be based on any field in object
@Sun It is a matter of compromise between performance and memory foot print: maintaining one map per field will be faster but use more memory, looping over the list every time you need to search will be slower but use less memory.
Alternatively, if the structure of your object is flat (only String fields) you could have a look at Guava's table - I have never used it though.
3

I modifie this list and add a List to the samples try this

Pseudocode

Sample {
   List<String> values;
   List<String> getList() {
   return values}
}



for(Sample s : list) {
   if(s.getString.getList.contains("three") {
      return s;
   }
}

2 Comments

My list is very Big.. i'm looking for a efficient search. is this the only possible way?
you could organize the values as a hashmap, to avoid check every entry in Sample...
0

As your list is an ArrayList, it can be assumed that it is unsorted. Therefore, there is no way to search for your element that is faster than O(n).

If you can, you should think about changing your list into a Set (with HashSet as implementation) with a specific Comparator for your sample class.

Another possibility would be to use a HashMap. You can add your data as Sample (please start class names with an uppercase letter) and use the string you want to search for as key. Then you could simply use

Sample samp = myMap.get(myKey);

If there can be multiple samples per key, use Map<String, List<Sample>>, otherwise use Map<String, Sample>. If you use multiple keys, you will have to create multiple maps that hold the same dataset. As they all point to the same objects, space shouldn't be that much of a problem.

Comments

0

You can filter the list:

list.stream().filter(
              sample -> sample.getValue4().equals("4")
              ).forEach(System.out::println)

Comments

0

I propose for+if.

Object result; 
for (Object o: objects){ 
  if (o.value3.equals("three")){ 
    result=o; 
    break;
  }
}

no streams, no guavas, I think it's simple.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.