there are many posts on how to display the elements of LinkedList<String>, but I can't find something that I understand for LinkedList<String[]>.
The following code shows the output:
LinkedList:[[Ljava.lang.String;#5305068a, [Ljava.lang.String;#1f32e575
whereas I'm looking for the output:
LinkedList:[[Audi SQ5,341], [LandRover Discovery,306]].
and I don't want to overwrite the toString().
Code:
LinkedList<String[]> cars2 = new LinkedList<>();
String[] split_result = new String[2];
split_result = "Audi SQ5,341".split(",");
cars2.add(split_result);
split_result = "LandRover Discovery,306".split(",");
cars2.add(split_result);
// Displaying the size of the list
System.out.println("The size of the linked list is: " + cars2.size());
// Displaying the Strings in the linkedlist ??? (not their addresses)
System.out.println("LinkedList:" + cars2);
cars2.forEach(element -> System.out.println(element));
I would use a Java Stream for that. It has the helpful joining collector:
LinkedList<String[]> cars2 = new LinkedList<>();
String output = cars2
.stream()
.map(Arrays::toString)
.collect(Collectors.joining(", ", "[", "]"));
Your problem is that while default Java collections like LinkedList implement a nice toString to print their memebers, arrays do not. So either you have to iterate over your collection and print the results yourself, as some other answers suggest, or you have to put objects into your LinkedLists that have a nice toString implementation. Those can either be collections themselves List<List<String>>, or you can define a class to contain your data.
class CarInfo {
// these should be encapsulated with getters/setters, I'm being lazy.
public String model;
public String value; // I don't know what this represents, or if it's a string or int
public void toString() { return model + ", " + value; }
}
If you use Lombok, this is very easy with the #Data annotation, which will generate toString and getters/setters for you.
#Data
class CarInfo {
String model;
String value;
}
However, it won't be exactly the same as your desired output. If you want it specifically formatted with the brackets as you described, you'd have to do your own toString anyway.
Related
I have a condition like :
public String createId(List<String> list)
{
String id="";
if(list.contains("name"))
id+="TEST VALUE NAME";
if(list.contains("age"))
id+="Test Value AGE";
.
.
. likewise many if condition
return id;
}
As per my understanding we should use StringBuilder in loop condition and String in simple concatenation. So here wanted to ask I should use String or StringBuilder? Kindly suggest
StringBuilder is the best for this scenario because it's mutable. the String is immutable so when you modify the string it creates a new object.
It seems that for the given task it would be better to get rid of the multiple duplicated if statements by defining a list of the keys to match the input list and use Stream API to generate the string id, e.g. Collectors.joining with delimiter or without the delimiter.
Assuming that there is a single rule to create a part of the id: append "Test Value " + key.toUpperCase(), the implementation may look as follows:
final List<String> keys = Arrays.asList(
"name", "age" /* and other needed keys*/
);
public String createId(List<String> list) {
return keys
.stream()
.filter(list::contains)
.map(String::toUpperCase)
.map(str -> "Test Value " + str)
.collect(Collectors.joining("_")); // or Collectors.joining()
}
System.out.println(createId(Arrays.asList("age", "name", "surname")));
// output: Test Value NAME_Test Value AGE
If custom parts should be provided for name, age, etc., a Map of matches should be prepared and used, also it may make sense to convert the input list into Set<String to facilitate look-ups:
final Map<String, String> keys = new LinkedHashMap<>(); {
// fill the map in special order
keys.put("name", "Name Part");
keys.put("age", "Test Age");
/* and other needed keys*/
}
public String createId(List<String> list) {
Set<String> words = new HashSet<>(list);
return keys.keySet()
.stream()
.filter(words::contains) // faster lookup O(1) at the cost of another collection
.map(keys::get)
.collect(Collectors.joining("_")); // or Collectors.joining()
}
System.out.println(createId(Arrays.asList("age", "surname", "name")));
// output: Name Part_Test Age
In general your understanding is correct about when to use String concatenation vs StringBuilder. The Java Language Specification says
To increase the performance of repeated string concatenation, a Java
compiler may use the StringBuffer class or a similar technique to
reduce the number of intermediate String objects that are created by
evaluation of an expression.
For the larger majority of cases you should use whichever method results in better readability and maintainability.
I am looking for a clean and simple way to iterate over two ArrayLists that are related directly in that each index of one maps to the index of another (in relationship).
Currently, I'm doing it via a simple for loop and I tried looking into lambdas and for-each loops but don't see a way to apply it to two lists at the same time that are of the same size.
firstList: ["Blue", "Red", "Green"]
secondList: ["Sky", "Roses", "Grass"]
for(int i = 0; i < firstList.size(); i++){
System.out.println(firstList.get(i) + " " + secondList.get(i));
}
Result:
Blue Sky
Red Roses
Green Grass
Is there a way to effectively iterate over both lists simultaneously using a lambda or a for-each loop to avoid using a for loop?
What you have it is already pretty efficient (assuming that you are using ArrayList), concise and readable. However, this type of scenarios:
I am looking for a clean and simple way to iterate over two ArrayLists
that are related directly in that each index of one maps to the index
of another (in relationship).
typically require a class that defines the relationship, in your case:
public class MyC {
private final String color;
private final String object;
public MyC(String color, String object) {
this.color = color;
this.object = object;
}
public String getColor(){
return color;
}
public String getObject(){
return object;
}
#Override
public String toString() {
return "MyC{" +
"color='" + color + '\'' +
", object='" + object + '\'' +
'}';
}
}
then the two lists would become one:
List<MyC> list = List.of(new MyC("Blue", "Sky"), new MyC("Red", "Roses"), new MyC("Green", "Grass") );
and then you can use:
list.forEach(System.out::println);
The Answer by dreamcrash is correct: While your looping of a pair of arrays works, you should instead take advantage of Java as a OOP language by defining your own class.
Record
Defining such a class is even simpler in Java 16 with the new records feature. Write your class as a record when it’s main purpose is communicating data, transparently and immutably.
A record is very brief by default. The compiler implicitly creates the constructor, getters, equals & hashCode, and toString. You need only declare the type and name of each member field.
record ColorNoun ( String color , String noun ) {}
Use a record like a conventional class. Instantiate with new.
ColorNoun blueSky = new ColorNoun ( "Blue" , "Sky" ) ;
Note that a record can be declared locally within a method, or declared separately like a conventional class.
You can use the "range"-statement to iterate the index with a lambda expression.
https://www.baeldung.com/java-stream-indices
Like this:
List<String> firstList = Arrays.asList("Blue","Red","Green");
List<String> secondList = Arrays.asList("Sky","Roses","Grass");
IntStream
.range(0, firstList.size())
.forEach(index ->
System.out.println(String.format("%s %s", firstList.get(index), secondList.get(index)))
);
It's basically the same approach - just with lambdas.
The only way to get rid of the index-based access is by using a different data-structure or use an alternating iteration technique as shown in the other answers.
If your intention is to just avoid using a for loop, then you could try the below:
Iterator<String> iterator = secondList.iterator();
firstList.forEach(s -> System.out.println(s + " " + iterator.next()));
Or even add to a StringBuilder and then display the result in the end.
All the other answers are correct and the preferred solution should always be the one shown by #Basil Bourque , but as others pointed out in the comments, iterating a non-array-based list using indexes is not very efficient. However, iterating using an iterator should (where each implementation can provide an efficient implementation) is.
Here is an example how you can iterate 2 lists using their iterator:
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.function.BiConsumer;
public class IterateTwoListsExample {
public static void main(String[] args) {
List<String> firstList = Arrays.asList("Blue","Red","Green");
List<String> secondList = Arrays.asList("Sky","Roses","Grass");
forEach(firstList, secondList, (first, second) -> System.out.printf("%s %s%n", first, second));
}
public static <T0, T1> void forEach(List<? extends T0> first, List<? extends T1> second, BiConsumer<? super T0, ? super T1> consumer) {
final Iterator<? extends T0> firstIt = first.iterator();
final Iterator<? extends T1> secondIt = second.iterator();
while (firstIt.hasNext() && secondIt.hasNext()) {
consumer.accept(firstIt.next(), secondIt.next());
}
}
}
I have two Lists and trying to form a String with element from each List, and in between when I do " ", the sorted order is maintained. But once I put "|" in the middle, which I would want to, the order of the elements in the Set gets switched around.
How can I add "|" and still maintain the sorted order in the Set students?
Here is the code:
Set<String> students = new HashSet<>();
Set<String> fn = new HashSet<>();
Set<String> nums = new HashSet<>();
List<String> firstNames = new ArrayList<>(fn);
Collections.sort(firstNames);
List<String> favNumbers = new ArrayList<>(nums);
Collections.sort(favNumbers);
for(int i=0; i<firstNames.size(); i++) {
students.add(firstNames.get(i) + "|" + favNumbers.get(i));
}
System.out.println(students);
With ... + " " + ..., the order is [Joshua 4, Lyon 7], but if "|" is added in place of " ", the order becomes [Lyon|7, Joshua|4] when I want and should be[Joshua|4, Lyon|7].
A HashSet does not provide any ordering guarantees about its contents, using whatever ordering the underlying HashMap generates, which is in turn based on the hashCode() of the elements.
When you change the contents of a string, you get a different hash code--simple as that. The order in a HashMap is undefined and could change if you inserted additional elements triggering a rehash.
If you want a set with a guaranteed order, you can use a SortedSet implementation (such as TreeSet), but you'd need to write a proper class and implement suitable Comparators. Alternately, you could use LinkedHashSet, which maintains elements in insertion order at the expense of additional overhead.
You should be using object oriented design, as Java is an object oriented language. Instead of trying to represent the various features of a student as independent collection, create a Student POJO which contains these features. Then, create custom comparators to sort by either name or favorite number.
public class Student {
private String firstName;
private String lastName;
private int favNumber;
// getters and setters
public static Comparator<Student> NameComparator
= new Comparator<Student>() {
public int compare(Student s1, Student s2) {
String f1 = s1.getFirstName();
String f2 = s2.getFirstName();
String l1 = s1.getLastName();
String l2 = s2.getLastName();
if (l1.equalsIgnoreCase(l2) {
return f1.toUpperCase().compareTo(f2);
}
else {
return l1.toUpperCase().compareTo(l2);
}
}
};
public static Comparator<Student> FavComparator
= new Comparator<Student>() {
public int compare(Student s1, Student s2) {
return s1.getFavNumber() < s2.getFavNumber();
}
};
}
Now if you have a list of students, List<Student> list, you can sort via:
Collections.sort(list, Student.NameComparator);
Or, to sort by favorite numbers, use:
Collections.sort(list, Student.FavComparator);
From the documentation for HashSet:
It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time.
So you cannot rely on HashSet to preserve any kind of order.
It looks to me like you just need to preserve the order of insertion, in which case you're better off not using a Set and rather a List, e.g.,
List<String> students = new ArrayList<>();
I checked many examples but i could not applied for my variables.
I have a ArratyList Of lists of Strings.
ArrayList<List<String>> bulkUploadList = new ArrayList<List<String>>();
and it's look like this:
[id, title, tags, descriptions]
[4291483113.0000000000000, Camden, camdentown;london, NoValue]
[4292220054.0000000000000, IMG_2720, NoValue, NoValue]
[4292223824.0000000000000, IMG_2917, london;camdentown, NoValue]
[4292224728.0000000000000, IMG_2945, London;CamdenTown, NoValue]
I want to remove those rows which have the same titles and the same tags.
I do not know how work with HashSet since I have a ArrayList of List of Strings.
Not best solution, but you can start with this:
ArrayList<List<String>> bulkUploadList = new ArrayList<List<String>>();
ArrayList<List<String>> result = new ArrayList<List<String>>();
HashSet<String> hashSet = new HashSet<>();
for(List<String> item : bulkUploadList) {
String title = item.get(1);
String tags = item.get(2);
String uniqueString = (title + "#" + tags).trim().toUpperCase();
if(!hashSet.contains(uniqueString)) {
result.add(item);
hashSet.add(uniqueString);
} else {
System.out.println("Filtered element " + uniqueString);
}
}
As suggested in one of the comments, you should create a class for the data, make that class implement equals(), and then use HashSet to remove dups. Like this.
class Foo {
String id;
String title;
String tags;
String description;
public boolean equals(Foo this, Foo other) {
return this.id.equals(other.id)
&& this.title.equals(other.title)
&& etc.
}
then you can remove dups with
Set<Foo> set = new LinkedHashSet<Foo>(list);
as Sets do not allow duplication, and the equals() method is used to check.
You should use a linkedHashSet here because you want to preserve the order (according to a comment you made on elsewhere).
You should also implement a hashcode() method consistent with equals().
I have an an arraylist called Semesters of Semester objects
private ArrayList<Semester> Semesters
I wrote the following code to printout a string using the objects(Semester) in the list(Semesters)
String report = year + "\n";
for (Semester s : Semesters) {
report += s.toString() + "\n";
}
Then I got a tip on netbeans where the entire above code can be written in one line like this:
String report = year + "\n";
report = Semesters.stream().map((s) -> s.toString() + "\n").reduce(report, String::concat);
I am an intermediate java student and I am trying to understand this feature which I find to be very useful but can't quite get my head around it.
Please help me understand how these two pieces of code are the same.
I haven't worked with Java lambdas, but going by the map and reduce\fold operations from the functional programming paradigm, this is how the 2 snippets are equivalent:
map basically applies the particular function to each element in the collection. In this case, that function/method is s.toString() + "\n". This is the same as iterating over the collection and applying the function to each element via a foreach loop.
reduce is a fold operation i.e. it concatenates each element returned by the map operation to the original string report.
So in your original code
String report = year + "\n";
for (Semester s : Semesters) {
report += s.toString() + "\n";
}
You basically operate on the element s, and then concatenate the result of that operation to report. Using a lambda, you first operate on the whole collection in the map phase, and then traverse the collection returned during mapping and concatenate each element to report in the reduce phase.
Here is the equivalent functionality broken down using anonymous classes:
Function<Semester, String> toStrFn = new Function<Semester, String>() {
#Override
public String apply(Semester semester) {
return semester + "\n";
}
};
BinaryOperator<String> reducer = new BinaryOperator<String>() {
#Override
public String apply(String lastResult, String currentValue) {
return lastResult.concat(currentValue);
}
};
String report = year + "\n";
Stream<Semester> semesterStream = Semesters.stream();
Stream<String> stringStream = semesterStream.map(toStrFn);
report = stringStream.reduce(report, reducer);
semesters.stream() will give a Stream<Semester> object for one time usage (stateful like iterator).
.map(...) is given a function interface: an interface with only one method with parameter type Semester.
The lambda exoression will generate an instance of a generated interface containing a method with Semester parameter and String as result type.
Hence map yields a Stream
Map will later iterate over the Semester-s, apply the method and collect the String.
Stream<String>.reduce(String startValue, stepFunction) will let the operation (iterating) start collecting a single String, initializing to startValue, on every step: `result = result.concat(return value of map method).
I hope I have got the explanation right.
System.out.println("Mapping Semester to String...");
Stream<String> sems = Semesters.stream().map((s) -> {
System.out.println("map call " + s);
return s + "\n";
});
System.out.println("Reducing String stream to String...");
String report = sems.reduce(year + "\n", String::concat);
The above shows eager/lazy evaluation. Maybe look into the Builder Pattern, for what happens under the hood. Try out parallel and filter.
String report = year + "\n"
+ semesters.stream()
.filter(...)
.sorted(...)
.map(Semester::toString)
.collect(Collectors.joining("\n"));